You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
klamav/src/dbviewer.cpp

949 lines
24 KiB

/*
* Copyright (C) 2004 Robert Hogan <robert at roberthogan dot net>
*
* ClamAV DB code:
* Copyright (C) 2002 - 2005 Tomasz Kojm <tkojm@clamav.net>
*/
#include "dbviewer.h"
#include "klamav.h"
#include <clamav.h>
#include "pageviewer.h"
#include "tabwidget.h"
#include "freshklam.h"
#include "../config.h"
#include <tqheader.h>
#include <tqlayout.h>
#include <tqpopupmenu.h>
#include <tdetoolbarbutton.h> //ctor
#include <tdetempfile.h>
#include <ktempdir.h>
#include <tdelistviewsearchline.h>
#include <tdelistview.h>
#include <kprogress.h>
#include <dirent.h>
#include <zlib.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <kurl.h>
#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);
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<int> 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 <fieldno> from the string <line>, where
* fields are delimited by any char from <delim>, or NULL if <line>
* doesn't have <fieldno> 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 # <fieldno> */
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"