diff options
Diffstat (limited to 'src/klamonacc.cpp')
-rw-r--r-- | src/klamonacc.cpp | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/klamonacc.cpp b/src/klamonacc.cpp new file mode 100644 index 0000000..76a41d8 --- /dev/null +++ b/src/klamonacc.cpp @@ -0,0 +1,387 @@ +/* + * KlamOnAcc class -- the non-graphical class which manages clamonacc. + * + * Copyright (C) 2021 Mavridis Philippe <mavridisf@gmail.com> + * + * Portions taken from freshklam.cpp and scanviewer.cpp + */ + +/* TODO: + - Implement a separate start/stop daemon process so that we don't need to + ask for root privgileges every time we start or kill clamonacc + - processOutput: [Initializing] and [Scanner ready] notifications + */ + +#include "klamonacc.h" +#include "klamonacc_alert.h" +#include "klamav.h" +#include "klamavconfig.h" +#include "collectiondb.h" +#include "directorylist.h" + +#include <tdeglobal.h> +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdetempfile.h> +#include <kprocess.h> +#include <tdemessagebox.h> +#include <ksystemtray.h> +#include <knotifyclient.h> + +/* Required by quarantine() function */ +#include <kiconloader.h> +#include <tdeio/netaccess.h> + +KlamOnAcc::KlamOnAcc( TQWidget *parent, const char *name ) + : TQObject( parent, name ) +{ + config = TDEGlobal::config(); + config->setGroup("OnAccess"); + + // Initial state + toggle( config->readBoolEntry("EnableOnAccess", false) ); +} + +KlamOnAcc::~KlamOnAcc() +{ +} + +TQString KlamOnAcc::tdesu(TQString command, TQString caption, bool terminal) +{ + TQString sucommand; + + if(terminal) + sucommand = TQString("tdesu --caption \"%1\" --ignorebutton -t ").arg( caption ); + else + sucommand = TQString("tdesu --caption \"%1\" --ignorebutton ").arg( caption ); + + sucommand += "\"" + command + "\""; + + return sucommand; +} + +TQString KlamOnAcc::startPrepare() +{ + // Determine and write configuration + TQString daemonOpts; + TQStringList daemonConfig; + + // Create a config file based on the default one + TQFile defaultConfigFile( "/etc/clamav/clamd.conf" ); + + if( defaultConfigFile.open(IO_ReadOnly) ) + { + TQTextStream in_stream( &defaultConfigFile ); + TQString in_line; + int in_line_n = 0; + + while(! in_stream.atEnd() ) + { + daemonConfig += in_stream.readLine(); + ++in_line_n; + } + + defaultConfigFile.close(); + } + + // Set up ClamOnAcc's config + daemonConfig += "OnAccessPrevention yes"; + + config->setGroup("OnAccess"); + + if ( config->readBoolEntry("ExtraScanning", false) ) + daemonConfig += "OnAccessExtraScanning yes"; + + daemonConfig += TQString("OnAccessMaxFileSize %1M").arg( + config->readNumEntry("OnAccessMaxFile", 5) + ); + + daemonConfig += "OnAccessExcludeUname clamav"; + + // Specify directories to watch + TQStringList dirs = CollectionSetup::pruneSelectedDirs( config->readListEntry("Watchlist") ); + + if (! dirs.count() ) { + fatalError( i18n("Please select the directories you want to watch from the Options dialog.") ); + return TQString::null; + } + + for ( TQStringList::Iterator it = dirs.begin(); it != dirs.end(); it++ ) + daemonConfig += TQString("OnAccessIncludePath %1").arg(*it); + + /* BUG: DOES NOT WORK (why do they have this option then?) + "ERROR: ClamInotif: can't exclude '/home/user/.trinity'" */ + // if ( config->readBoolEntry("ExcludeConfDir", true) ) + // daemonConfig += TQString("OnAccessExcludePath %1/.trinity").arg(getenv("HOME")); + + // Write the config + KTempFile tf; + + if ( tf.status() != 0 ) { + tf.close(); + + fatalError( i18n("Could not create temporary configuration file for ClamOnAcc!") ); + return TQString::null; + } + + TQString tempFileName = tf.name(); + + TQTextStream &ts = *(tf.textStream()); + + for ( TQStringList::Iterator it = daemonConfig.begin(); it != daemonConfig.end(); it++ ) + ts << (*it) << endl; + + tf.close(); + + // Set up ClamOnAcc's command-line options + daemonOpts += " --fdpass -v --stdout --foreground"; + daemonOpts += TQString(" --config-file=%1").arg(tempFileName); + + // Make the start command + TQString command = "clamonacc"; + command += daemonOpts; + + return command; +} + +void KlamOnAcc::startProcess( TQString command ) +{ + childproc = new KShellProcess(); + *childproc << command; + childproc->start(TDEProcess::NotifyOnExit, TDEProcess::Stdout); + + connect( childproc, SIGNAL(receivedStdout(TDEProcess*, char*, int)), SLOT(processOutput(TDEProcess*, char*, int)) ); + connect( childproc, SIGNAL(processExited(TDEProcess*)), SLOT(childExited()) ); + + emit stateUpdated(); +} + +void KlamOnAcc::start() +{ + if( active || !enabled ) return; + active = true; + crashed = false; + + // Log this event + CollectionDB::instance()->insertEvent("On-Access Scanner","Starting On-Access Scanner",0); + + startProcess( tdesu(startPrepare(), i18n("Start On-Access Scanner"), true) ); +} + +TQString KlamOnAcc::stopPrepare() +{ + disconnect( childproc, 0, 0, 0 ); + + // It's like this until a proper start/stop daemon is implemented + return TQString("killall -9 clamonacc"); +} + +void KlamOnAcc::stopProcess( TQString command ) +{ + if( childproc->isRunning() ) { + TDEProcess *terminator = new KShellProcess(); + *terminator << command; + terminator->start(); + } + + emit stateUpdated(); +} + +void KlamOnAcc::stop() +{ + if( !active || !enabled ) return; + active = false; + + // Log this event + CollectionDB::instance()->insertEvent("On-Access Scanner","Stopping On-Access Scanner",0); + + stopProcess( tdesu(stopPrepare(), i18n("Stop On-Access Scanner")) ); +} + + +void KlamOnAcc::restart() +{ + kdDebug() << "restart()" << endl; + if( isActive() ) + { + active = false; + + // We combine two commands here + TQString command = stopPrepare() + TQString("; ") + startPrepare(); + kdDebug() << "restart(): " << command << endl; + startProcess( tdesu(command, i18n("Restart On-Access Scanner"), true) ); + + active = true; + } +} + +void KlamOnAcc::toggle( bool on ) { + kdDebug() << "toggle()" << endl; + + if ( !on && isEnabled() ) + disable(); + else if ( on && !isEnabled() ) + enable(); +} + +void KlamOnAcc::childExited() { + if(active) // died too early + fatalError( i18n("ClamOnAcc has died unexpectedly. If you did not kill it yourself, please check your ClamAV installation.") ); +} + +void KlamOnAcc::fatalError(TQString descr) +{ + if( crashed ) return; // do not display further errors + + active = false; + crashed = true; + + CollectionDB::instance()->insertEvent("On-Access Scanner","On-Access Scanner has died!",0); + + disable(); + + KMessageBox::sorry( + 0, + descr, + i18n("Fatal Error") + ); +} + +void KlamOnAcc::processOutput(TDEProcess*, char* buffer, int buffSize) +{ + + TQString buff( buffer ); + buff = buff.mid( 0, buff.find("\n") ).stripWhiteSpace(); + + kdDebug() << "KLAMONACC " << buff << endl; + + int pos; + + if( buff.find("Could not connect to clamd") != -1 ) + { + fatalError( i18n("The ClamAV daemon is unavailable! Please ensure that it is running and try again.") ); + return; + } + else if( buff.find("ClamInotif: watching") != -1 ) + { + // TODO: "initialization complete" notification + } + else if( (pos = buff.find("FOUND")) != -1 ) + { + fname = buff.mid( 0, buff.find(":") ); + vname = buff.mid( buff.find(":")+2, pos ); + + if( shownAlerts.find(fname) != shownAlerts.end() ) + return; // alert already shown for this file + + TQListViewItem *virusItem; + alert = new KlamOnAccAlert(); + alert->setModal(false); + alert->setActiveWindow(); + + TQListViewItem *virus = new TQListViewItem( alert->VirusList, fname, vname, i18n("Loose") ); + virus->setPixmap( 0, SmallIcon("klamav_virus") ); + + shownAlerts << fname; + alert->exec(); + + if( alert->result() == TQDialog::Accepted ) + quarantine(); + } +} + +void KlamOnAcc::enable() +{ + kdDebug() << "% ENABLE()" << endl; + + config->setGroup("OnAccess"); + config->writeEntry("EnableOnAccess", true); + config->sync(); + + enabled = true; + + if(! isActive() ) start(); + + emit stateUpdated(); +} + +void KlamOnAcc::disable() +{ + kdDebug() << "% DISABLE()" << endl; + if( isActive() ) stop(); + + config->setGroup("OnAccess"); + config->writeEntry("EnableOnAccess", false); + config->sync(); + + enabled = false; + + emit stateUpdated(); +} + +void KlamOnAcc::quarantine() +{ + TQDate today = TQDate::currentDate(); + TQTime now = TQTime::currentTime(); + TQString suffix = TQString(":%1 %2") + .arg(today.toString("ddd MMMM d yyyy")) + .arg(now.toString("hh-mm-ss-zzz ap")); + + TQStringList QuarantineList; + QuarantineList.append(fname+":"+vname+suffix); + + /* The following has been taken nearly verbatim from scanviewer.cpp */ + bool allQuarantined=TRUE; + config->setGroup("Kuarantine"); + TQStringList lastQuarLocations = config->readListEntry("KuarantineLocations"); + + tdemain->_tray->setPixmap(KSystemTray::loadIcon("klamav_quarantining")); + + TQString quarloc; + for (TQStringList::Iterator it = lastQuarLocations.begin(); it == lastQuarLocations.begin() ; it++){ + quarloc = *it; + } + TQStringList lastQuarItems = config->readListEntry(TQString("Items %1").arg(quarloc)); + + for (TQStringList::Iterator it = QuarantineList.begin(); it != QuarantineList.end(); it++ ){ + if (lastQuarItems.contains(*it) != 0) { + lastQuarItems.remove(*it); + } + TQString item2 = (*it).stripWhiteSpace(); + int fnameStartPoint = 0; + int dtStartPoint = item2.findRev(":"); + int fnameEndPoint = item2.findRev(":", (signed int)-((item2.length() - dtStartPoint)+1)); + TQString fname = item2.mid(fnameStartPoint,(fnameEndPoint - fnameStartPoint)); + TQString itemName = item2.mid((fnameEndPoint+1),((dtStartPoint+1) - (fnameEndPoint+2))); + TQString when = item2.mid((dtStartPoint+1),(item2.length() - (dtStartPoint+1))); + if (!(fname.isEmpty())){ + TQStringList tokens = TQStringList::split ( "/", fname, FALSE ); + TQString qname = tokens.last(); + qname.prepend("/"); + qname.prepend(quarloc); + qname.append(":"+when); + if (TDEIO::NetAccess::file_move(fname,qname)){ + if (lastQuarItems.contains(item2)) + lastQuarItems.remove(item2); + lastQuarItems.prepend(item2); + (alert->VirusList->findItem(fname,0))->setText(2,"Quarantined"); + (alert->VirusList->findItem(fname,0))->setPixmap( 0, SmallIcon("klamav") ); + chmod(qname.ascii(),0400); + CollectionDB::instance()->insertEvent("Quarantine",TQString("Quarantined"),fname); + + }else{ + KMessageBox::information (tdemain, i18n("<p>There was a problem quarantining <b>%1</b>. Check your diskspace, the permissions on your quarantine location and whether a file with the same name already exists in the quarantine. </p>").arg(fname)); + } + + + } + } + + emit stateUpdated(); + + config->writeEntry(TQString("Items %1").arg(quarloc), lastQuarItems); + config->sync(); + +} + +#include "klamonacc.moc"
\ No newline at end of file |