/* * Copyright (C) 2004 Robert Hogan * * ClamAV DB code: * Copyright (C) 2002 - 2005 Tomasz Kojm */ #include "dbviewer.h" #include "klamav.h" #include #include "pageviewer.h" #include "tabwidget.h" #include "freshklam.h" #include "../config.h" #include #include #include #include #include //ctor #include #include #include #include #include #include #include #include #include #include #include #define TAR_BLOCKSIZE 512 #define FILEBUFF 8192 int cli_untgz(int fd, const char *destdir); int cli_rmdirs(const char *dirname); int cli_strbcasestr(const char *haystack, const char *needle); int cli_chomp(char *string); char *cli_strtok(const char *line, int field, const char *delim); using namespace KlamAV; /* * Constructs a KlamDB as a child of 'parent', with the * name 'name' and widget flags set to 'f'. * * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ KlamDB::KlamDB( TQWidget* parent, const char* name, bool modal, WFlags fl ) : TQDialog( parent, name, modal, fl ) { if ( !name ) setName( "KlamDB" ); loadinprogress = false; TQVBoxLayout *vbox = new TQVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint(), "vbox"); TQWidget* privateLayoutWidget = new TQWidget( this, "dblayout" ); vbox->addWidget(privateLayoutWidget); dblayout = new TQGridLayout( privateLayoutWidget, 1, 1, 2, 2, "dblayout"); dblayout->setColStretch(1, 1); tabBrowser = new TabWidget(privateLayoutWidget, "KlamDB", TQStringList("Home")); dblayout->addMultiCellWidget( tabBrowser, 0, 1, 1, 1 ); TDEToolBarButton *button; TDEToolBar* searchToolBar = new TDEToolBar( privateLayoutWidget ); searchToolBar->setMovingEnabled(false); searchToolBar->setFlat(true); searchToolBar->setIconSize( 16 ); searchToolBar->setEnableContextMenu( false ); button = new TDEToolBarButton( "locationbar_erase", 0, searchToolBar ); VirusList = new TDEListView( privateLayoutWidget, "VirusList" ); VirusList->addColumn( i18n( "All Known Viruses" ),150 ); connect(VirusList, SIGNAL( doubleClicked( TQListViewItem * , const TQPoint &, int ) ), this, SLOT(slotOpenTab(TQListViewItem * , const TQPoint &, int )) ); menu = new TQPopupMenu( VirusList ); TQPixmap gicon; TQPixmap ticon; TQString iconPath = locate("cache", KMimeType::favIconForURL("http://www.google.com")+".png"); if ( iconPath.isEmpty() ) gicon = SmallIcon("edit-find"); else gicon = TQPixmap( iconPath ); iconPath = locate("cache", KMimeType::favIconForURL("http://www.trendmicro.com")+".png"); if ( iconPath.isEmpty() ) ticon = SmallIcon("edit-find"); else ticon = TQPixmap( iconPath ); menu->insertItem(ticon, i18n("Search with Trend Micro"), this,SLOT(slotTrendMicro()) ); menu->insertItem(gicon, i18n("Search with Google"), this,SLOT(slotGoogle()) ); googlePrefix = TQString::fromAscii("http://www.google.com/search?ie=ISO-8859-1&q="); tMicroPrefix = TQString::fromAscii("https://www.trendmicro.com/vinfo/us/threat-encyclopedia/search/"); connect(VirusList, SIGNAL( contextMenuRequested( TQListViewItem *, const TQPoint& , int ) ), this, SLOT( slotRMB( TQListViewItem *, const TQPoint &, int ) ) ); kLineEdit1 = new TDEListViewSearchLine( (TQWidget *)searchToolBar, VirusList,"klinedit1"); TQValueList columns; columns.append(0); kLineEdit1->setSearchColumns(columns); kLineEdit1->setMaxLength(2); connect(button, SIGNAL( clicked() ), kLineEdit1, SLOT(clear()) ); dblayout->addWidget( searchToolBar, 0, 0 ); dblayout->addWidget( VirusList, 1, 0 ); languageChange(); clearWState( WState_Polished ); slotOpenHome(); } /* * Destroys the object and frees any allocated resources */ KlamDB::~KlamDB() { // no need to delete child widgets, TQt does it all for us } /* * Sets the strings of the subwidgets using the current * language. */ void KlamDB::languageChange() { setCaption( i18n( "Form1" ) ); tabBrowser->changeTab( tab, i18n( "Info" ) ); VirusList->header()->setLabel( 0, i18n( "All Known Viruses" ) ); VirusList->clear(); /*TDEListViewItem * item =*/ new TDEListViewItem( VirusList, 0 ); //item->setText( 0, tr( "New Item" ) ); } int KlamDB::listdb(const char *filename) { FILE *fd, *tmpd; // char *buffer, *pt, *start, *dir, *tmp; char *buffer, *pt, *start; int line = 0, bytes; const char *tmpdir; if(cli_strbcasestr(filename, ".inc")) { /* incremental directory */ if(listdir(filename) == -1) { printf("!listdb: Can't list incremental directory %s\n", filename); return -1; } return 0; } if((fd = fopen(filename, "rb")) == NULL) { printf("!listdb(): Can't open file %s\n", filename); return -1; } if(!(buffer = (char *) malloc(FILEBUFF))) { printf("!listdb(): Can't allocate memory.\n"); fclose(fd); return -1; } memset(buffer, 0, FILEBUFF); /* check for CVD file */ fgets(buffer, 12, fd); rewind(fd); if(!strncmp(buffer, "ClamAV-VDB:", 11)) { fseek(fd, 512, SEEK_SET); tmpdir = getenv("TMPDIR"); if(tmpdir == NULL) #ifdef P_tmpdir tmpdir = P_tmpdir; #else tmpdir = "/tmp"; #endif KTempDir* tf = new KTempDir(); if ( tf->status() != 0 ) { delete tf; return -1; } /* FIXME: it seems there is some problem with current position indicator * after gzdopen() call in cli_untgz(). Temporarily we need this wrapper: */ /* start */ KTempFile* tn = new KTempFile(tf->name()); if((tmpd = fopen(tn->name(), "wb+")) == NULL) { printf("!listdb(): Can't create temporary file \n"); delete tf; delete tn; free(buffer); fclose(fd); return -1; } while((bytes = fread(buffer, 1, FILEBUFF, fd)) > 0) fwrite(buffer, 1, bytes, tmpd); free(buffer); fclose(fd); fflush(tmpd); fseek(tmpd, 0L, SEEK_SET); if(cli_untgz(fileno(tmpd), tf->name())) { printf("!listdb(): Can't unpack CVD file.\n"); cli_rmdirs(tf->name()); delete tf; delete tn; fclose(tmpd); free(buffer); return -1; } fclose(tmpd); delete tn; /* wrapper end */ /* list extracted directory */ listdir(tf->name()); cli_rmdirs(tf->name()); delete tf; return 0; } if(cli_strbcasestr(filename, ".db") || cli_strbcasestr(filename, ".db2")) { /* old style database */ while(fgets(buffer, FILEBUFF, fd)) { line++; pt = strchr(buffer, '='); if(!pt) { printf("!listdb(): Malformed pattern line %d (file %s).\n", line, filename); fclose(fd); free(buffer); return -1; } start = buffer; *pt = 0; if((pt = strstr(start, " (Clam)"))) *pt = 0; //printf("%s\n", start); addVirusName(start); } } else if(cli_strbcasestr(filename, ".hdb")) { while(fgets(buffer, FILEBUFF, fd)) { line++; cli_chomp(buffer); start = cli_strtok(buffer, 2, ":"); if(!start) { printf("!listdb(): Malformed pattern line %d (file %s).\n", line, filename); fclose(fd); free(buffer); return -1; } if((pt = strstr(start, " (Clam)"))) *pt = 0; //printf("%s\n", start); addVirusName(start); free(start); } } else if(cli_strbcasestr(filename, ".ndb")) { while(fgets(buffer, FILEBUFF, fd)) { line++; cli_chomp(buffer); start = cli_strtok(buffer, 0, ":"); if(!start) { printf("!listdb(): Malformed pattern line %d (file %s).\n", line, filename); fclose(fd); free(buffer); return -1; } if((pt = strstr(start, " (Clam)"))) *pt = 0; //printf("%s\n", start); addVirusName(start); free(start); } } fclose(fd); free(buffer); return 0; } int KlamDB::listdir(const char *dirname) { DIR *dd; struct dirent *dent; char *dbfile; if((dd = opendir(dirname)) == NULL) { printf("!Can't open directory %s\n", dirname); return -1; } while((dent = readdir(dd))) { #ifndef C_INTERIX if(dent->d_ino) #endif { if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && (cli_strbcasestr(dent->d_name, ".db") || cli_strbcasestr(dent->d_name, ".hdb") || cli_strbcasestr(dent->d_name, ".mdb") || cli_strbcasestr(dent->d_name, ".ndb") || cli_strbcasestr(dent->d_name, ".sdb") || cli_strbcasestr(dent->d_name, ".zmd") || cli_strbcasestr(dent->d_name, ".rmd") || cli_strbcasestr(dent->d_name, ".inc") || cli_strbcasestr(dent->d_name, ".cvd"))) { dbfile = (char *) calloc(strlen(dent->d_name) + strlen(dirname) + 2, sizeof(char)); if(!dbfile) { printf("!listdir(): Can't allocate memory.\n"); closedir(dd); return -1; } sprintf(dbfile, "%s/%s", dirname, dent->d_name); if(listdb(dbfile)) { printf("!listdb(): error listing database %s\n", dbfile); free(dbfile); closedir(dd); return -1; } free(dbfile); } } } closedir(dd); return 0; } int KlamDB::checkdir(const char *dirname) { DIR *dd; struct dirent *dent; if((dd = opendir(dirname)) == NULL) { printf("!Can't open directory %s\n", dirname); return -1; } int found = 0; while((dent = readdir(dd))) { #ifndef C_INTERIX if(dent->d_ino) #endif { if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && (cli_strbcasestr(dent->d_name, ".db") || cli_strbcasestr(dent->d_name, ".hdb") || cli_strbcasestr(dent->d_name, ".mdb") || cli_strbcasestr(dent->d_name, ".ndb") || cli_strbcasestr(dent->d_name, ".sdb") || cli_strbcasestr(dent->d_name, ".zmd") || cli_strbcasestr(dent->d_name, ".rmd") || cli_strbcasestr(dent->d_name, ".inc") || cli_strbcasestr(dent->d_name, ".cvd"))) { found = 1; } } } closedir(dd); if (!(found)) return -1; return 0; } int cli_untgz(int fd, const char *destdir) { char *fullname, osize[13], name[101], type; char block[TAR_BLOCKSIZE]; int nbytes, nread, nwritten, in_block = 0; unsigned int size; FILE *outfile = NULL; gzFile infile; printf("in cli_untgz()\n"); if((infile = gzdopen(fd, "rb")) == NULL) { printf("Can't gzdopen() descriptor %d\n", fd); return -1; } fullname = (char *) calloc(sizeof(char), strlen(destdir) + 100 + 5); while(1) { nread = gzread(infile, block, TAR_BLOCKSIZE); if(!in_block && nread == 0) break; if(nread != TAR_BLOCKSIZE) { printf("Incomplete block read.\n"); free(fullname); gzclose(infile); return -1; } if(!in_block) { if (block[0] == '\0') /* We're done */ break; strncpy(name, block, 100); name[100] = '\0'; strcpy(fullname, destdir); strcat(fullname, "/"); strcat(fullname, name); printf("Unpacking %s\n",fullname); type = block[156]; switch(type) { case '0': case '\0': break; case '5': printf("Directories in CVD are not supported.\n"); free(fullname); gzclose(infile); return -1; default: printf("Unknown type flag %c.\n",type); free(fullname); gzclose(infile); return -1; } in_block = 1; if(outfile) { if(fclose(outfile)) { printf("Cannot close file %s.\n", fullname); free(fullname); gzclose(infile); return -1; } outfile = NULL; } if(!(outfile = fopen(fullname, "wb"))) { printf("Cannot create file %s.\n", fullname); free(fullname); gzclose(infile); return -1; } strncpy(osize, block + 124, 12); osize[12] = '\0'; if((sscanf(osize, "%o", &size)) == 0) { printf("Invalid size in header.\n"); free(fullname); gzclose(infile); fclose(outfile); return -1; } } else { /* write or continue writing file contents */ nbytes = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size; nwritten = fwrite(block, 1, nbytes, outfile); if(nwritten != nbytes) { printf("Wrote %d instead of %d (%s).\n", nwritten, nbytes, fullname); free(fullname); gzclose(infile); return -1; } size -= nbytes; if(size == 0) in_block = 0; } } if(outfile) fclose(outfile); gzclose(infile); free(fullname); return 0; } int cli_rmdirs(const char *dirname) { DIR *dd; struct dirent *dent; #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) union { struct dirent d; char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; } result; #endif struct stat maind, statbuf; char *fname; chmod(dirname, 0700); if((dd = opendir(dirname)) != NULL) { while(stat(dirname, &maind) != -1) { if(!rmdir(dirname)) break; #ifdef HAVE_READDIR_R_3 while(!readdir_r(dd, &result.d, &dent) && dent) { #elif defined(HAVE_READDIR_R_2) while((dent = (struct dirent *) readdir_r(dd, &result.d))) { #else while((dent = readdir(dd))) { #endif #ifndef C_INTERIX if(dent->d_ino) #endif { if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { fname = (char *)calloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char)); sprintf(fname, "%s/%s", dirname, dent->d_name); /* stat the file */ if(lstat(fname, &statbuf) != -1) { if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) { if(rmdir(fname) == -1) { /* can't be deleted */ if(errno == EACCES) { printf("Can't remove some temporary directories due to access problem.\n"); closedir(dd); free(fname); return 0; } cli_rmdirs(fname); } } else unlink(fname); } free(fname); } } } rewinddir(dd); } } else { return 53; } closedir(dd); return 0; } int cli_strbcasestr(const char *haystack, const char *needle) { char *pt = (char *) haystack; int i, j; i = strlen(haystack); j = strlen(needle); if(i < j) return 0; pt += i - j; return !strcasecmp(pt, needle); } /* * Remove trailing NL and CR characters from the end of the given string. * Return the new length of the string (ala strlen) */ int cli_chomp(char *string) { int l; if(string == NULL) return -1; l = strlen(string); if(l == 0) return 0; --l; while((l >= 0) && ((string[l] == '\n') || (string[l] == '\r'))) string[l--] = '\0'; return l + 1; } /* * char *cli_strok(const char *line, int fieldno, char *delim) * Return a copy of field from the string , where * fields are delimited by any char from , or NULL if * doesn't have fields or not enough memory is available. * The caller has to free() the result afterwards. */ char *cli_strtok(const char *line, int fieldno, const char *delim) { int counter = 0, i, j; char *buffer = NULL; /* step to arg # */ for (i=0; line[i] && counter != fieldno; i++) { if (strchr(delim, line[i])) { counter++; while(line[i+1] && strchr(delim, line[i+1])) { i++; } } } if (!line[i]) { /* end of buffer before field reached */ return NULL; } for (j=i; line[j]; j++) { if (strchr(delim, line[j])) { break; } } if (i == j) { return NULL; } buffer = (char *)malloc(j-i+1); if(!buffer) return NULL; strncpy(buffer, line+i, j-i); buffer[j-i] = '\0'; return buffer; } void KlamDB::shouldIShow(TQWidget * current) { static struct cl_stat *dbstat=NULL; TQString location; if ((current == this) && (!(loadinprogress))){ TQString db = tdemain->freshklam->getCurrentDBDir(); if (checkdir((const char *)db) == -1){ kdDebug() << "returned -1" << endl; location = locate("data", "klamav/about/nodb.html"); homepage->openURL(location); return; } if ( ((cl_statchkdir(dbstat) == 1) || (dbstat == NULL))) { location = locate("data", "klamav/about/wait.html"); homepage->openURL(location); loadinprogress = true; sigs = ( int )getSigNos(); progress = new KProgressDialog (this, "progress", i18n( "Loading .." ), i18n( "Loading..." ), true); progress->setAllowCancel(false); prog = progress->progressBar(); progress->setLabel(i18n( "Loading lots and lots and lots of virus information" )); prog->setTotalSteps(sigs); //kdDebug() << prog->totalSteps() << endl; VirusList->hide(); if (VirusList->childCount() > 0) VirusList->clear(); count = 0; prog->setProgress (count); kapp->processEvents(); // TQString db = tdemain->freshklam->getCurrentDBDir(); listdir((const char *)db); prog->setProgress (sigs); VirusList->show(); if(dbstat == NULL) { dbstat = (struct cl_stat *) malloc(sizeof(struct cl_stat)); } else { cl_statfree(dbstat); } memset(dbstat, 0, sizeof(struct cl_stat)); cl_statinidir((const char *)db, dbstat); loadinprogress = false; // Default english TQString location = locate( "data", "klamav/about/main-" + TDEGlobal::locale()->language() + ".html" ); if( location != TQString::null ) homepage->openURL( location ); else homepage->openURL( locate("data", "klamav/about/main.html") ); } } } void KlamDB::addVirusName(char *start) { count++; kapp->processEvents(); if ((count % 500) == 0){ //progress->setLabel("Loading " + TQString(start)); progress->setLabel( i18n( "Loading ClamAV's Database of Virus Signatures") ); prog->setProgress (count); kapp->processEvents(); } new TDEListViewItem( VirusList, TQString(start)); kapp->processEvents(); } unsigned int KlamDB::getSigNos() { unsigned int ret= 0; unsigned int no = 0; struct cl_engine *engine = NULL; TQStringList lastDownloadPaths; TQString dbdir; TQString db; TDEConfig* config = TDEGlobal::config(); config->setGroup("Freshklam"); lastDownloadPaths = config->readListEntry("lastDownloadPaths"); dbdir = lastDownloadPaths.first(); #ifdef SUPPORT_CLAMAV_V095 if((engine = cl_engine_new()) == NULL) { printf("Database initialization error: %s\n", cl_strerror(ret));; cl_engine_free(engine); return 0; } #endif #ifdef SUPPORT_CLAMAV_V095 ret = cl_load((const char *)dbdir, engine, &no, CL_DB_STDOPT); cl_engine_free(engine); #else ret = cl_load((const char *)dbdir, &engine, &no, CL_DB_STDOPT); cl_free(engine); #endif kdDebug() << "no of sigs" << no << endl; return no; } void KlamDB::slotOpenTab(TQListViewItem * item, const TQPoint& , int ) { PageViewer* page = new PageViewer(this, "page"); //connect( m_part, SIGNAL(signalSettingsChanged()), page, SLOT(slotPaletteOrFontChanged())); //connect( page, SIGNAL(setTabIcon(const TQPixmap&)), // this, SLOT(setTabIcon(const TQPixmap&))); //connect( page, SIGNAL(setWindowCaption (const TQString &)), // this, SLOT(slotTabCaption (const TQString &)) ); connect( page, SIGNAL(urlClicked(const KURL &,bool)), this, SLOT(slotOpenTabPlain(const KURL &,bool)) ); TQString url = item->text(0); Frame *frame=new Frame(this, page, page->widget(), "VirusList : "+item->text(0)); //connectFrame(frame); tabBrowser->addFrame(frame); // if(!background) tabBrowser->showPage(page->widget()); // else setFocus(); //if (m_tabs->count() > 1 && m_tabs->currentPageIndex() != 0) // m_tabsClose->setEnabled(true); page->openURL("http://www.viruslist.com/en/find?search_mode=virus&words="+url); } void KlamDB::slotOpenTabPlain(const KURL& url, bool background) { PageViewer* page = new PageViewer(this, "page"); //connect( m_part, SIGNAL(signalSettingsChanged()), page, SLOT(slotPaletteOrFontChanged())); /* connect( page, SIGNAL(setTabIcon(const TQPixmap&)), this, SLOT(setTabIcon(const TQPixmap&)));*/ connect( page, SIGNAL(setWindowCaption (const TQString &)), this, SLOT(slotTabCaption (const TQString &)) ); connect( page, SIGNAL(urlClicked(const KURL &,bool)), this, SLOT(slotOpenTabPlain(const KURL &,bool)) ); Frame *frame=new Frame(this, page, page->widget(), i18n("Untitled")); //connectFrame(frame); tabBrowser->addFrame(frame); if(!background) tabBrowser->showPage(page->widget()); else setFocus(); //if (m_tabs->count() > 1 && m_tabs->currentPageIndex() != 0) // m_tabsClose->setEnabled(true); //kdDebug() << url << endl; page->openURL(url); } void KlamDB::slotTabCaption(const TQString &caption) { if (!caption.isEmpty()) { PageViewer *pv=(PageViewer *)sender(); tabBrowser->setTitle(caption, pv->widget()); pv->slotSetCaption(caption); } } void KlamDB::slotOpenHome() { homepage = new PageViewer(this, "page"); Frame *frame=new Frame(this, homepage, homepage->widget(), "Home"); //connectFrame(frame); tabBrowser->addFrame(frame); // if(!background) tabBrowser->showPage(homepage->widget()); // else setFocus(); TQString location = locate( "data", "klamav/about/wait-" + TDEGlobal::locale()->language() + ".html" ); if( location != TQString::null ) homepage->openURL( location ); else homepage->openURL( locate("data", "klamav/about/wait.html") ); } void KlamDB::slotRMB( TQListViewItem* Item, const TQPoint & point, int ) { if( Item ) menu->popup( point ); } void KlamDB::slotOpenPrefix(TQString prefix, TQString title,TQString url) { PageViewer* page = new PageViewer(this, "page"); Frame *frame=new Frame(this, page, page->widget(), title+" : "+url); tabBrowser->addFrame(frame); tabBrowser->showPage(page->widget()); setFocus(); page->openURL(prefix+url); } void KlamDB::slotGoogle() { TQString url = KURL::encode_string( VirusList->selectedItem()->text(0) ); slotOpenPrefix(googlePrefix,"Google",url); } void KlamDB::slotTrendMicro() { TQString url = KURL::encode_string( VirusList->selectedItem()->text(0) ); slotOpenPrefix(tMicroPrefix,"TrendMicro",url); } void KlamDB::slotExternal(TQString name,TQString service) { tdemain->showVirusBrowser(); shouldIShow(this); TQString prefix; if (service == "Google") prefix = googlePrefix; else prefix = tMicroPrefix; slotOpenPrefix(prefix,service,name); } #include "dbviewer.moc"