summaryrefslogtreecommitdiffstats
path: root/src/klamonacc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/klamonacc.cpp')
-rw-r--r--src/klamonacc.cpp387
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 <[email protected]>
+ *
+ * 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