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.
387 lines
11 KiB
387 lines
11 KiB
4 years ago
|
/*
|
||
|
* 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"
|