mirror of https://github.com/Cisco-Talos/clamav
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.
889 lines
24 KiB
889 lines
24 KiB
/***************************************************************
|
|
* Purpose: Code for Application Frame
|
|
*
|
|
* Copyright (C) 2010 Sourcefire, Inc.
|
|
*
|
|
* Authors: Török Edwin
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************/
|
|
|
|
#include "wx_pch.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif //__BORLANDC__
|
|
|
|
#include "../../../../libclamav/version.h"
|
|
#include "SigUIMain.h"
|
|
#include "installdb.h"
|
|
#include <wx/clipbrd.h>
|
|
#include <wx/textdlg.h>
|
|
#include <wx/uri.h>
|
|
#include <wx/socket.h>
|
|
#include <wx/tokenzr.h>
|
|
#include <wx/txtstrm.h>
|
|
#include <wx/filename.h>
|
|
#include <wx/stdpaths.h>
|
|
#include <wx/dir.h>
|
|
#include <wx/hashset.h>
|
|
|
|
#if wxUSE_DRAG_AND_DROP
|
|
class DropFiles : public wxFileDropTarget
|
|
{
|
|
public:
|
|
DropFiles(wxControlWithItems *owner)
|
|
:m_owner(owner) {}
|
|
|
|
virtual bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
|
|
const wxArrayString& filenames)
|
|
{
|
|
unsigned n = filenames.GetCount();
|
|
for (unsigned i=0;i<n;i++) {
|
|
if (!wxFile::Exists(filenames[i])) {
|
|
//TODO: show error
|
|
return false;
|
|
}
|
|
}
|
|
for (unsigned i=0;i<n;i++) {
|
|
m_owner->Append(filenames[i]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
wxControlWithItems *m_owner;
|
|
};
|
|
#endif
|
|
|
|
class HostnameValidator : public wxTextValidator
|
|
{
|
|
protected:
|
|
virtual wxString IsValid(const wxString &val) const {
|
|
if (val.length() == 1)
|
|
return "";//characters
|
|
wxURI uri;
|
|
if (!uri.Create("http://" + val))
|
|
return _("Invalid URI: %s");
|
|
if (uri.HasFragment() ||
|
|
uri.HasPath() ||
|
|
uri.HasPort() ||
|
|
uri.HasQuery() ||
|
|
!uri.HasServer() ||
|
|
uri.HasUserInfo())
|
|
return _("Invalid hostname: %s");
|
|
return "";
|
|
}
|
|
public:
|
|
HostnameValidator(wxString *valPtr=NULL)
|
|
:wxTextValidator(wxFILTER_NONE, valPtr) {}
|
|
virtual wxObject *Clone() const {
|
|
HostnameValidator *v = new HostnameValidator();
|
|
v->Copy(*this);
|
|
return v;
|
|
}
|
|
};
|
|
|
|
SigUIFrame::~SigUIFrame()
|
|
{
|
|
delete watcher;
|
|
delete icon;
|
|
delete editor;
|
|
}
|
|
|
|
void SigUIFrame::OnClose(wxCloseEvent& event)
|
|
{
|
|
if (event.CanVeto()) {
|
|
if (!Validate() || !TransferDataFromWindow()) {
|
|
wxLogWarning(_("Validation failed"));
|
|
event.Veto();
|
|
return;
|
|
}
|
|
if (!m_sig_candidates->IsEmpty()) {
|
|
int answer = wxMessageBox(_("You have added new signatures that have not been installed yet\n"
|
|
"Are you sure you want to exit?"),
|
|
_("There are new signatures"),
|
|
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION, this);
|
|
if (answer == wxNO) {
|
|
event.Veto();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (editor->Save(true)) {
|
|
int answer = wxMessageBox(_("There are unsaved changes that will be lost if you exit the application\n"
|
|
"Save changes?"),
|
|
_("There are unsaved changes"),
|
|
wxYES_NO | wxCANCEL | wxCANCEL_DEFAULT | wxICON_QUESTION, this);
|
|
switch (answer) {
|
|
case wxYES:
|
|
editor->Save();
|
|
break;
|
|
case wxNO:
|
|
// exiting and loosing changes
|
|
break;
|
|
default:
|
|
event.Veto();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// icon->RemoveIcon();
|
|
Destroy();
|
|
}
|
|
|
|
void SigUIFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void SigUIFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
}
|
|
|
|
void SigUIFrame::m_proxyOnCheckBox( wxCommandEvent& event )
|
|
{
|
|
bool enable = m_proxy->IsChecked();
|
|
m_proxy_server->Enable(enable);
|
|
m_proxy_port->Enable(enable);
|
|
m_proxyauth->Enable(enable);
|
|
m_proxyauthOnCheckBox(event);
|
|
}
|
|
|
|
void SigUIFrame::m_proxyauthOnCheckBox( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
bool enable = m_proxyauth->IsEnabled() && m_proxyauth->IsChecked();
|
|
m_proxy_user->Enable(enable);
|
|
m_proxy_password->Enable(enable);
|
|
}
|
|
|
|
class URLValidator : public wxTextValidator
|
|
{
|
|
public:
|
|
virtual wxString IsValid(const wxString &val) const {
|
|
if (val.IsEmpty())
|
|
return _("Empty URLs are not valid: %s");
|
|
if (val.length() == 1)
|
|
return "";//characters
|
|
//bb #2343
|
|
if (!*val.mb_str())
|
|
return _("URL can't contain non-ASCII characters, please URLencode it: %s");
|
|
|
|
if (val.StartsWith("\\\\")) {
|
|
if (!wxFileName::FileExists(val))
|
|
return _("UNC path doesn't exist: %s");
|
|
return "";
|
|
}
|
|
|
|
wxURI uri;
|
|
if (!uri.Create(val))
|
|
return _("Invalid URI: %s");
|
|
if (uri.HasUserInfo())
|
|
return _("User not supported in URL: %s");
|
|
if (uri.HasQuery())
|
|
return _("Query parameters not supported in URL: %s");
|
|
if (uri.HasFragment())
|
|
return _("Fragment not supported in URL: %s");
|
|
|
|
if (uri.GetScheme() == "file") {
|
|
if (!wxFileName::FileExists(uri.GetPath()))
|
|
return _("file doesn't exist: %s");
|
|
return "";
|
|
}
|
|
if (uri.GetScheme() == "http" && uri.IsReference())
|
|
return _("URL not absolute: %s");
|
|
if (uri.GetScheme() != "http" && uri.GetScheme() != "file")
|
|
return _("Only HTTP URLs accepted: %s");
|
|
if (!uri.HasServer())
|
|
return _("URL must specify a server: %s");
|
|
if (!uri.HasPath())
|
|
return _("URL must specify a path: %s");
|
|
if (!SigUICopy::validate_dbname(uri.GetPath(), false))
|
|
return _("Extension is not a valid virus signature database extension: %s");
|
|
return "";
|
|
}
|
|
public:
|
|
URLValidator(wxString *valPtr=NULL)
|
|
:wxTextValidator(wxFILTER_NONE, valPtr) {}
|
|
virtual wxObject *Clone() const {
|
|
URLValidator *v = new URLValidator();
|
|
v->Copy(*this);
|
|
return v;
|
|
}
|
|
};
|
|
|
|
class URLEntryDialog : public wxTextEntryDialog
|
|
{
|
|
public:
|
|
URLEntryDialog(wxWindow* parent, const wxString& message, const wxString& caption = "Please enter text", const wxString& defaultValue = "", long style = wxOK | wxCANCEL | wxCENTRE, const wxPoint& pos = wxDefaultPosition)
|
|
: wxTextEntryDialog(parent, message, caption, defaultValue, style, pos) {
|
|
URLValidator validator(&m_value);
|
|
SetTextValidator(validator);
|
|
}
|
|
};
|
|
|
|
#if wxUSE_DRAG_AND_DROP
|
|
class DropURLs : public wxDropTarget
|
|
{
|
|
public:
|
|
DropURLs(wxControlWithItems *owner)
|
|
: m_owner(owner)
|
|
{
|
|
SetDataObject(new wxURLDataObject);
|
|
}
|
|
|
|
virtual wxDragResult OnDragOver(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
|
|
wxDragResult WXUNUSED(def))
|
|
{
|
|
return wxDragLink;
|
|
}
|
|
|
|
virtual wxDragResult OnData(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxDragResult def)
|
|
{
|
|
if (!GetData())
|
|
return wxDragNone;
|
|
|
|
wxString url = ((wxURLDataObject*)GetDataObject())->GetURL();
|
|
url.Trim();
|
|
url.Trim(false);
|
|
|
|
wxArrayString good;
|
|
|
|
wxStringTokenizer tokenizer(url);//tokenize lines
|
|
while (tokenizer.HasMoreTokens()) {
|
|
wxString token = tokenizer.GetNextToken();
|
|
token.Trim();
|
|
|
|
URLValidator urlv;
|
|
wxString err = urlv.IsValid(token);
|
|
if (!err.IsEmpty()) {
|
|
wxString buf;
|
|
buf.Printf(err, token.c_str());
|
|
wxMessageBox(buf, _("Validation conflict"),
|
|
wxOK | wxICON_EXCLAMATION, m_owner);
|
|
return wxDragError;
|
|
}
|
|
good.Add(token);
|
|
}
|
|
for (unsigned i=0;i<good.GetCount();i++)
|
|
m_owner->Append(good[i]);
|
|
|
|
return def;
|
|
}
|
|
private:
|
|
wxControlWithItems *m_owner;
|
|
};
|
|
#endif
|
|
|
|
WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, StringSet);
|
|
|
|
static wxString GetBasename(wxString path)
|
|
{
|
|
return wxFileName(path).GetFullName().MakeLower();
|
|
}
|
|
|
|
void SigUIFrame::GetFreshclamDBnames(StringSet *set)
|
|
{
|
|
set->insert("main.cvd");
|
|
set->insert("daily.cvd");
|
|
set->insert("bytecode.cvd");
|
|
for (unsigned i=0;i<m_urls->GetCount();i++) {
|
|
set->insert( GetBasename(m_urls->GetString(i)) );
|
|
}
|
|
}
|
|
|
|
void SigUIFrame::m_custom_addOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
URLEntryDialog dlg(this, "Custom virus signatures source HTTP or file URL:", "Input http:// file:// URL or UNC path:");
|
|
if (dlg.ShowModal() == wxID_OK) {
|
|
wxString str = dlg.GetValue();
|
|
if (str.StartsWith("\\\\")) {
|
|
wxLogWarning(_("You specified an UNC path, make sure the SYSTEM account can access it!\n"
|
|
"(SYSTEM account usually can't access network shares)"));
|
|
str = "file://" + str;//freshclam remove file:// so it should be fine
|
|
}
|
|
|
|
// look for duplicate basename (since it will they will just overwrite
|
|
// eachother in the DBdir)
|
|
StringSet db_set;
|
|
GetFreshclamDBnames(&db_set);
|
|
wxString basename = GetBasename(str);
|
|
|
|
if (db_set.count(basename)) {
|
|
wxLogWarning(_("Adding this database (%s) will overwrite existing database (%s)!\n"
|
|
"You should remove one of them from the custom signatures list"),
|
|
str, basename);
|
|
}
|
|
|
|
m_urls->Append(str);
|
|
m_custom_remove->Enable();
|
|
}
|
|
}
|
|
|
|
void SigUIFrame::m_custom_removeOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
int n = m_urls->GetSelection();
|
|
if (n == wxNOT_FOUND)
|
|
return;
|
|
m_urls->Delete(n);
|
|
if (m_urls->IsEmpty())
|
|
m_custom_remove->Disable();
|
|
}
|
|
|
|
static wxString GetExecPath()
|
|
{
|
|
wxFileName exec(wxStandardPaths::Get().GetExecutablePath());
|
|
return exec.GetPathWithSep();
|
|
}
|
|
|
|
static wxFileSystemWatcher *watcher;
|
|
void SigUIApp::OnEventLoopEnter(wxEventLoopBase *WXUNUSED(loop))
|
|
{
|
|
watcher = new wxFileSystemWatcher();
|
|
watcher->SetOwner(GetTopWindow());
|
|
watcher->Add(GetExecPath(), wxFSW_EVENT_CREATE | wxFSW_EVENT_MODIFY |
|
|
wxFSW_EVENT_WARNING | wxFSW_EVENT_ERROR);
|
|
}
|
|
|
|
void SigUIFrame::m_save_settingsOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
if (!Validate() || !TransferDataFromWindow()) {
|
|
wxLogWarning(_("Validation failed"));
|
|
return;
|
|
}
|
|
editor->Save();
|
|
wxLogMessage(_("Settings saved"));
|
|
}
|
|
|
|
static wxString GetConfigFile()
|
|
{
|
|
return GetExecPath() + "freshclam.conf";
|
|
}
|
|
SigUIFrame::SigUIFrame(wxFrame *frame)
|
|
: GUIFrame(frame), val_bytecode(true), watcher(0)
|
|
{
|
|
#ifdef _WIN32
|
|
SetIcon(wxIcon(wxT("aaaa")));
|
|
#endif
|
|
|
|
icon = new wxTaskBarIcon();
|
|
icon->Connect(wxEVT_TASKBAR_BALLOON_TIMEOUT, wxTaskBarIconEventHandler(SigUIFrame::OnBalloon), NULL, this);
|
|
icon->Connect(wxEVT_TASKBAR_BALLOON_CLICK, wxTaskBarIconEventHandler(SigUIFrame::OnBalloon), NULL, this);
|
|
|
|
this->Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(SigUIFrame::OnChange));
|
|
|
|
this->Connect(wxEVT_END_PROCESS, wxProcessEventHandler(SigUIFrame::OnTerminateInstall));
|
|
|
|
this->SetStatusBar(statusBar);
|
|
statusBar->SetStatusText(REPO_VERSION, 1);
|
|
|
|
// m_sig_files->SetDropTarget(new DropFiles(m_sig_files));
|
|
// m_urls->SetDropTarget(new DropURLs(m_urls));
|
|
|
|
|
|
editor = new ConfigEditor(GetConfigFile());
|
|
editor->RegisterText("HTTPProxyServer", &val_proxy_server, m_proxy_server, "hostname or IP address");
|
|
editor->RegisterInt("HTTPProxyPort", &val_proxy_port, m_proxy_port);
|
|
editor->RegisterText("HTTPProxyUsername", &val_proxy_username, m_proxy_user);
|
|
editor->RegisterText("HTTPProxyPassword", &val_proxy_password, m_proxy_password);
|
|
editor->RegisterText("DatabaseMirror", &val_mirror, m_mirror, "db.COUNTRYCODE.clamav.net");
|
|
editor->RegisterStatic("DatabaseMirror", "database.clamav.net");
|
|
editor->RegisterBool("Bytecode", &val_bytecode, m_bytecode);
|
|
editor->RegisterList("DatabaseCustomURL", m_urls);
|
|
|
|
HostnameValidator mirrorValidator(&val_mirror);
|
|
m_mirror->SetValidator(mirrorValidator);
|
|
|
|
HostnameValidator proxyValidator(&val_proxy_server);
|
|
m_proxy_server->SetValidator(proxyValidator);
|
|
|
|
editor->Load();
|
|
if (!val_proxy_port)
|
|
val_proxy_port = 8080;//default
|
|
|
|
TransferDataToWindow();
|
|
|
|
if (!val_proxy_server.empty()) {
|
|
m_proxy->SetValue(true);
|
|
if (!val_proxy_username.empty() && !val_proxy_password.empty())
|
|
m_proxyauth->SetValue(true);
|
|
}
|
|
|
|
// update enabled status
|
|
wxCommandEvent event;
|
|
m_proxyOnCheckBox(event);
|
|
|
|
if (!m_urls->IsEmpty())
|
|
m_custom_remove->Enable();
|
|
|
|
m_proxy->SetFocus();
|
|
|
|
// keyboard shortcuts
|
|
wxAcceleratorEntry entries[1];
|
|
entries[0].Set(wxACCEL_CTRL, (int)'S', wxID_SAVE);
|
|
|
|
wxAcceleratorTable accel(sizeof(entries)/sizeof(entries[0]), entries);
|
|
this->SetAcceleratorTable(accel);
|
|
|
|
//prevent window from being resizing below minimum
|
|
this->GetSizer()->SetSizeHints(this);
|
|
show_db(true);
|
|
}
|
|
|
|
void SigUIFrame::OnBalloon(wxTaskBarIconEvent& WXUNUSED(event))
|
|
{
|
|
if (icon->IsIconInstalled())
|
|
icon->RemoveIcon();
|
|
}
|
|
|
|
void SigUIFrame::OnChange(wxFileSystemWatcherEvent &event)
|
|
{
|
|
if (event.IsError()) {
|
|
wxLogVerbose("fswatcher error: %s", event.GetErrorDescription());
|
|
return;
|
|
}
|
|
wxLogVerbose("event on %s", event.GetPath().GetFullPath());
|
|
switch (event.GetChangeType()) {
|
|
default:
|
|
break;
|
|
case wxFSW_EVENT_CREATE:
|
|
case wxFSW_EVENT_MODIFY:
|
|
wxFileName filename = event.GetPath();
|
|
if (filename.GetName() != "lastupd")
|
|
return;
|
|
show_db(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SigUIFrame::show_db(bool first)
|
|
{
|
|
wxLogNull logNo;
|
|
char msg[512];
|
|
wxFileName filename(GetExecPath() + "lastupd");
|
|
if (!filename.IsFileReadable())
|
|
return;
|
|
wxFile file(filename.GetFullPath());
|
|
if (!file.IsOpened())
|
|
return;
|
|
memset(&msg, 0, sizeof(msg));
|
|
if (file.Read(msg, sizeof(msg) - 1) <= 0)
|
|
return;
|
|
|
|
wxString line = wxString(msg).BeforeFirst('\n');
|
|
wxString text = statusBar->GetStatusText(0);
|
|
statusBar->SetStatusText(line, 0);
|
|
if (first || lastmsg == msg)
|
|
return;
|
|
lastmsg = msg;
|
|
//only show when changed, and not the first time
|
|
if (icon->IsIconInstalled())
|
|
icon->RemoveIcon();//remove old balloon
|
|
icon->SetIcon(GetIcon());
|
|
line = wxString(msg).AfterFirst('\n');
|
|
#ifdef _WIN32
|
|
icon->ShowBalloon("ClamAV database reloaded",
|
|
line, wxICON_INFORMATION);
|
|
#endif
|
|
wxFileName filename0(GetExecPath() + "forcerld");
|
|
wxLogVerbose("Reload delta: %s", filename.GetModificationTime().Subtract( filename0.GetModificationTime() ).Format());
|
|
}
|
|
|
|
void SigUIFrame::tabsOnNotebookPageChanged( wxNotebookEvent& event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
void SigUIFrame::GUIFrameOnIdle(wxIdleEvent& WXUNUSED(event))
|
|
{
|
|
wxArrayString dbfiles;
|
|
wxDir dir(GetExecPath());
|
|
if (!dir.IsOpened())
|
|
return;
|
|
wxString filename;
|
|
bool cont = dir.GetFirst(&filename);
|
|
while (cont) {
|
|
if (SigUICopy::validate_dbname(filename, true))
|
|
dbfiles.Add(filename);
|
|
cont = dir.GetNext(&filename);
|
|
}
|
|
dbfiles.Sort();
|
|
|
|
wxArrayString old_dbfiles = m_installed_sigs->GetStrings();
|
|
if (old_dbfiles != dbfiles) {
|
|
m_installed_sigs->Clear();
|
|
m_installed_sigs->Append(dbfiles);
|
|
}
|
|
}
|
|
|
|
class MyProcess : public wxProcess
|
|
{
|
|
public:
|
|
MyProcess(MyProcessOutput *parent) :
|
|
m_parent(parent) { }
|
|
virtual void OnTerminate(int pid, int status)
|
|
{
|
|
wxProcessEvent event(0,pid,status);
|
|
m_parent->OnTerminate(event);
|
|
}
|
|
private:
|
|
MyProcessOutput *m_parent;
|
|
};
|
|
|
|
void SigUIFrame::reload()
|
|
{
|
|
wxFileName filename(GetExecPath() + "forcerld");
|
|
if (!filename.FileExists()) {
|
|
wxFile file;
|
|
if (!file.Create(filename.GetFullPath(), true)) {
|
|
wxLogMessage(_("Cannot signal reload"));
|
|
return;
|
|
}
|
|
} else {
|
|
filename.Touch();
|
|
}
|
|
|
|
wxLogMessage(_("Database reload queued"));
|
|
}
|
|
|
|
void SigUIFrame::m_run_freshclamOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
MyProcessOutput *output = new MyProcessOutput(this);
|
|
wxProcess *process = new MyProcess(output);
|
|
process->Redirect();
|
|
|
|
wxString cmd;
|
|
cmd << "\"" << GetExecPath() << "freshclam.exe\" -v --config-file=\""
|
|
<< GetConfigFile() << "\" --datadir=\"" << GetExecPath() << "\"";
|
|
//wxMessageBox(cmd);
|
|
long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
|
|
if (!pid) {
|
|
wxLogError("Failed to launch freshclam");
|
|
delete process;
|
|
return;
|
|
}
|
|
process->SetPid(pid);
|
|
process->CloseOutput();
|
|
|
|
wxInputStream *in = process->GetInputStream();
|
|
if (!in)
|
|
{
|
|
wxLogError("Failed to connect to child output");
|
|
return;
|
|
}
|
|
|
|
output->SetProcess(process);
|
|
output->ShowModal();
|
|
reload();
|
|
}
|
|
|
|
MyProcessOutput::MyProcessOutput(wxWindow *parent)
|
|
: ProcessOutput(parent), m_process(0), m_wakeup(this)
|
|
{
|
|
this->Connect(wxEVT_END_PROCESS, wxProcessEventHandler(MyProcessOutput::OnTerminate));
|
|
this->Connect(wxEVT_TIMER, wxTimerEventHandler(MyProcessOutput::OnTimer),NULL, this);
|
|
this->GetSizer()->SetSizeHints(this);
|
|
this->SetDoubleBuffered(true);
|
|
}
|
|
|
|
void MyProcessOutput::SetProcess(wxProcess *process)
|
|
{
|
|
m_process = process;
|
|
}
|
|
|
|
void MyProcessOutput::ProcessOutputOnInitDialog( wxInitDialogEvent& WXUNUSED(event) )
|
|
{
|
|
m_wakeup.Start(100);
|
|
}
|
|
|
|
void MyProcessOutput::ProcessOutputOnClose(wxCloseEvent &event)
|
|
{
|
|
if (m_process) {
|
|
if (event.CanVeto()) {
|
|
event.Veto();
|
|
return;
|
|
}
|
|
m_process->Detach();
|
|
}
|
|
EndModal(wxOK);
|
|
}
|
|
|
|
void MyProcessOutput::OnTimer(wxTimerEvent& WXUNUSED(event))
|
|
{
|
|
wxWakeUpIdle();
|
|
}
|
|
|
|
bool MyProcessOutput::processInput()
|
|
{
|
|
if (!m_process)
|
|
return false;
|
|
bool hasInput = false;
|
|
m_logoutput->Freeze();
|
|
while (m_process->IsInputAvailable()) {
|
|
wxInputStream *in = m_process->GetInputStream();
|
|
wxString msg;
|
|
int c;
|
|
static bool clear = false;
|
|
|
|
do {
|
|
c = in->GetC();
|
|
if (in->Eof())
|
|
break;
|
|
if (c >= 128)
|
|
c = '?';
|
|
msg << (char)c;
|
|
} while (c != '\r' && c != '\n');
|
|
|
|
msg.Trim();
|
|
msg.Trim(false);
|
|
if (!msg.empty()) {
|
|
bool scroll = false;
|
|
if (clear && m_logoutput->GetCount() > 0)
|
|
m_logoutput->Delete(m_logoutput->GetCount()-1);
|
|
else
|
|
scroll = true;
|
|
m_logoutput->Append(msg);
|
|
m_logoutput->ScrollLines(1);
|
|
}
|
|
clear = c == '\r';
|
|
hasInput = true;
|
|
}
|
|
while (m_process->IsErrorAvailable()) {
|
|
wxTextInputStream tis(*m_process->GetErrorStream());
|
|
wxString msg;
|
|
msg << tis.ReadLine();
|
|
msg.Trim();
|
|
if (!msg.empty()) {
|
|
m_logoutput->Append(msg);
|
|
m_logoutput->ScrollLines(1);
|
|
}
|
|
hasInput = true;
|
|
}
|
|
m_logoutput->Thaw();
|
|
return hasInput;
|
|
}
|
|
|
|
void MyProcessOutput::ProcessOutputOnIdle( wxIdleEvent& event )
|
|
{
|
|
if (processInput())
|
|
event.RequestMore();
|
|
}
|
|
|
|
void MyProcessOutput::OnTerminate(wxProcessEvent &event)
|
|
{
|
|
m_wakeup.Stop();
|
|
// show all output
|
|
while (processInput()) {}
|
|
|
|
int exit = event.GetExitCode();
|
|
delete m_process;
|
|
m_process = 0;
|
|
|
|
m_cancel_process->SetLabel(_("&Close window"));
|
|
wxString msg;
|
|
msg << "Freshclam exited with code: " << exit;
|
|
m_logoutput->Append(msg);
|
|
}
|
|
|
|
void MyProcessOutput::m_cancel_processOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
if (m_process) {
|
|
long pid = m_process->GetPid();
|
|
wxLogVerbose("terminate pid %ld", pid);
|
|
if (!wxProcess::Exists(pid)) {
|
|
wxLogVerbose("process doesn't exist anymore");
|
|
return;
|
|
}
|
|
int answer = wxMessageBox(_("Are you sure you want to forcefully terminate freshclam?"),
|
|
_("Force terminate freshclam?"),
|
|
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION, this);
|
|
if (answer != wxYES)
|
|
return;
|
|
wxLogVerbose("kill pid %ld", pid);
|
|
wxKillError rc = wxProcess::Kill(pid, wxSIGKILL);
|
|
if (rc != wxKILL_OK) {
|
|
wxLogVerbose("kill pid %ld failed: %d", pid, rc);
|
|
wxLogWarning(_("Failed to terminate process"));
|
|
return;
|
|
}
|
|
wxLogVerbose("killed pid %ld", pid);
|
|
/* wxProcessEvent event(0,pid,255);
|
|
OnTerminate(event);*/
|
|
return;
|
|
}
|
|
// this is really the close button now
|
|
EndModal(wxOK);
|
|
}
|
|
|
|
void SigUIFrame::m_local_addOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
//TODO: keep in sync with DBEXT
|
|
wxString wildcard = "ClamAV database files (*.cbc, *.cdb, *.cfg, *.cld, *.cvd, *.db, *.fp, *.ftm, *.gdb, *.hdb, *.hdu, *.idb, *.ldb, *.ldu, *.mdb, *.mdu, *.ndb, *.ndu, *.pdb, *.rmd, *.sdb, *.wdb, *.zmd)|*.cbc;*.cdb;*.cfg;*.cld;*.cvd;*.db;*.fp;*.ftm;*.gdb;*.hdb;*.hdu;*.idb;*.ldb;*.ldu;*.mdb;*.mdu;*.ndb;*.ndu;*.pdb;*.rmd;*.sdb;*.wdb;*.zmd";
|
|
wxFileDialog dlg(this, _("Choose a virus signature file"),
|
|
wxEmptyString, wxEmptyString,
|
|
wildcard,
|
|
wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_CHANGE_DIR);
|
|
dlg.CentreOnParent();
|
|
if (dlg.ShowModal() == wxID_OK) {
|
|
wxArrayString paths;
|
|
dlg.GetPaths(paths);
|
|
StringSet db_set;
|
|
GetFreshclamDBnames(&db_set);
|
|
|
|
for (unsigned i=0;i<paths.GetCount();i++) {
|
|
wxString path = paths[i];
|
|
if (m_sig_candidates->FindString(path, false) != wxNOT_FOUND) {
|
|
wxLogWarning(_("File already added: %s"), path);
|
|
continue;
|
|
}
|
|
if (db_set.count(GetBasename(path))) {
|
|
wxLogWarning(_("File is managed by freshclam. On next update it will be overwritten: %s"), path);
|
|
}
|
|
|
|
m_sig_candidates->Append(path);
|
|
}
|
|
m_local_remove->Enable(!m_sig_candidates->IsEmpty());
|
|
}
|
|
}
|
|
|
|
void SigUIFrame::m_local_removeOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
wxArrayInt selections;
|
|
int n = m_sig_candidates->GetSelections(selections);
|
|
while ( n > 0 )
|
|
{
|
|
m_sig_candidates->Delete(selections[--n]);
|
|
}
|
|
m_local_remove->Enable(!m_sig_candidates->IsEmpty());
|
|
}
|
|
|
|
void SigUIFrame::OnTerminateInstall(wxProcessEvent &event)
|
|
{
|
|
wxEndBusyCursor();
|
|
wxWakeUpIdle();
|
|
if (event.GetExitCode() == 0) {
|
|
m_sig_candidates->Clear();
|
|
wxLogMessage(_("Successfully installed new virus signatures\n"));
|
|
reload();
|
|
} else {
|
|
bool had_errors = false;
|
|
wxInputStream *err = m_siginst_process->GetErrorStream();
|
|
wxTextInputStream tis(*err);
|
|
|
|
while (!err->Eof()) {
|
|
wxString line = tis.ReadLine();
|
|
line.Trim();
|
|
if (!line.IsEmpty()) {
|
|
wxLogWarning("%s", line);
|
|
had_errors = true;
|
|
}
|
|
}
|
|
if (had_errors) {
|
|
wxLogError(_("Errors encountered during virus signature install"));
|
|
}
|
|
}
|
|
delete m_siginst_process;
|
|
|
|
m_panel_sigman->Enable();
|
|
}
|
|
|
|
void SigUIFrame::m_installOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
wxWakeUpIdle();
|
|
wxBeginBusyCursor();
|
|
m_panel_sigman->Disable();
|
|
|
|
wxFileName exec(wxStandardPaths::Get().GetExecutablePath());
|
|
m_siginst_process = new wxProcess(this);
|
|
m_siginst_process->Redirect();
|
|
|
|
long pid = wxExecute("\"" + exec.GetFullPath() + "\" -i", wxEXEC_ASYNC | wxEXEC_NOHIDE, m_siginst_process);
|
|
if (!pid) {
|
|
wxLogError(_("Failed to reexecute self for installing the virus signatures!"));
|
|
return;
|
|
}
|
|
m_siginst_process->SetPid(pid);
|
|
|
|
wxOutputStream *out = m_siginst_process->GetOutputStream();
|
|
for (unsigned i=0;i<m_sig_candidates->GetCount();i++) {
|
|
wxString str = m_sig_candidates->GetString(i) + "\n";
|
|
const char *s = str.mb_str();
|
|
if (*s) {
|
|
out->Write(s, strlen(s));
|
|
} else {
|
|
// see bb #2343
|
|
wxLogError(_("Filenames with non-ASCII characters not yet supported: %s"), str);
|
|
return;
|
|
}
|
|
}
|
|
m_siginst_process->CloseOutput();
|
|
wxWakeUpIdle();
|
|
}
|
|
|
|
void SigUIFrame::m_deleteOnButtonClick( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
wxArrayInt selections;
|
|
int n = m_installed_sigs->GetSelections(selections);
|
|
while ( n > 0 )
|
|
{
|
|
wxString file = m_installed_sigs->GetString(selections[--n]);
|
|
if (file.CmpNoCase("daily.cvd") == 0 ||
|
|
file.CmpNoCase("daily.cld") == 0) {
|
|
wxLogError(_("daily.cvd and daily.cld cannot be removed!"));
|
|
continue;
|
|
}
|
|
|
|
wxString msg;
|
|
msg.Printf(_("Are you sure you want to delete %s?"), file);
|
|
int answer = wxMessageBox(msg, _("Delete virus signature database"),
|
|
wxYES_NO | wxCANCEL | wxNO_DEFAULT | wxICON_QUESTION, this);
|
|
if (answer == wxCANCEL)
|
|
break;
|
|
if (answer != wxYES)
|
|
continue;
|
|
|
|
if (file.AfterLast('.').CmpNoCase("cvd") == 0 ||
|
|
file.AfterLast('.').CmpNoCase("cld") == 0) {
|
|
msg.Printf(_("This is an important database file, managed by freshclam.\nAre you sure you want to delete %s?"),
|
|
file);
|
|
answer = wxMessageBox(msg, _("Delete important virus signature database"),
|
|
wxYES_NO | wxCANCEL | wxNO_DEFAULT | wxICON_QUESTION, this);
|
|
if (answer == wxCANCEL)
|
|
break;
|
|
if (answer != wxYES)
|
|
continue;
|
|
}
|
|
|
|
wxFileName filepath(GetExecPath(), file);
|
|
if (!wxRemoveFile(filepath.GetFullPath())) {
|
|
wxLogError(_("Can't remove file %s"), filepath.GetFullPath());
|
|
} else
|
|
reload();
|
|
}
|
|
|
|
wxWakeUpIdle();
|
|
}
|
|
|
|
void SigUIFrame::m_bytecodeOnCheckBox( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
bool enable = m_bytecode->IsChecked();
|
|
if (!enable) {
|
|
int answer = wxMessageBox(_("It is NOT recommended to disable bytecode.\n"
|
|
"Are you sure you want to disable it?"),
|
|
_("Disabling important signature database"),
|
|
wxYES_NO | wxCANCEL | wxNO_DEFAULT | wxICON_QUESTION, this);
|
|
if (answer != wxYES)
|
|
m_bytecode->SetValue(true);
|
|
}
|
|
}
|
|
|