2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2017 Michael Rasmussen and the Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 # include "claws-features.h"
26 #include <glib/gi18n.h>
30 #include "common/claws.h"
31 #include "common/version.h"
39 #include "prefs_gtk.h"
40 #include "alertpanel.h"
41 #include "prefs_common.h"
42 #include "statusbar.h"
44 #include "clamav_plugin.h"
45 #include "clamd-plugin.h"
47 #define PLUGIN_NAME (_("Clam AntiVirus"))
50 static MessageCallback message_callback;
52 static ClamAvConfig config;
54 static PrefParam param[] = {
55 {"clamav_enable", "FALSE", &config.clamav_enable, P_BOOL,
57 /* {"clamav_enable_arc", "FALSE", &config.clamav_enable_arc, P_BOOL,
59 {"clamav_max_size", "1", &config.clamav_max_size, P_USHORT,
61 {"clamav_recv_infected", "TRUE", &config.clamav_recv_infected, P_BOOL,
63 {"clamav_save_folder", NULL, &config.clamav_save_folder, P_STRING,
65 {"clamad_config_type", "TRUE", &config.clamd_config_type, P_BOOL,
67 {"clamd_config_folder", NULL, &config.clamd_config_folder, P_STRING,
69 {"clamd_host", NULL, &config.clamd_host, P_STRING,
71 {"clamd_port", "0", &config.clamd_port, P_INT,
74 {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
81 static gboolean scan_func(GNode *node, gpointer data)
83 struct clamd_result *result = (struct clamd_result *) data;
84 MimeInfo *mimeinfo = (MimeInfo *) node->data;
91 outfile = procmime_get_tmp_file_name(mimeinfo);
92 if (procmime_get_part(outfile, mimeinfo) < 0)
93 g_warning("Can't get the part of multipart message.");
95 max = config.clamav_max_size * 1048576; /* maximum file size */
96 if (g_stat(outfile, &info) == -1)
97 g_warning("Can't determine file size");
99 if (info.st_size <= max) {
100 debug_print("Scanning %s\n", outfile);
101 result->status = clamd_verify_email(outfile, &buf);
102 debug_print("status: %d\n", result->status);
103 switch (result->status) {
105 g_warning("[scanning] No socket information");
106 if (config.alert_ack) {
107 alertpanel_error(_("Scanning\nNo socket information.\nAntivirus disabled."));
108 config.alert_ack = FALSE;
112 g_warning("[scanning] Clamd does not respond to ping");
113 if (config.alert_ack) {
114 alertpanel_warning(_("Scanning\nClamd does not respond to ping.\nIs clamd running?"));
115 config.alert_ack = FALSE;
119 msg = g_strconcat(_("Detected %s virus."),
120 clamd_get_virus_name(buf.msg), NULL);
121 g_warning("%s", msg);
122 debug_print("no_recv: %d\n", prefs_common_get_prefs()->no_recv_err_panel);
123 if (prefs_common_get_prefs()->no_recv_err_panel) {
124 statusbar_print_all("%s", msg);
127 alertpanel_warning("%s\n", msg);
130 config.alert_ack = TRUE;
133 debug_print("Error: %s\n", buf.msg);
134 if (config.alert_ack) {
135 alertpanel_error(_("Scanning error:\n%s"), buf.msg);
136 config.alert_ack = FALSE;
140 debug_print("No virus detected.\n");
141 config.alert_ack = TRUE;
146 msg = g_strdup_printf(_("File: %s. Size (%d) greater than limit (%d)\n"), outfile, (int) info.st_size, max);
147 statusbar_print_all("%s", msg);
148 debug_print("%s", msg);
155 return (result->status == OK) ? FALSE : TRUE;
158 static gboolean mail_filtering_hook(gpointer source, gpointer data)
160 MailFilteringData *mail_filtering_data = (MailFilteringData *) source;
161 MsgInfo *msginfo = mail_filtering_data->msginfo;
164 struct clamd_result result;
166 if (!config.clamav_enable)
169 mimeinfo = procmime_scan_message(msginfo);
170 if (!mimeinfo) return FALSE;
172 debug_print("Scanning message %d for viruses\n", msginfo->msgnum);
173 if (message_callback != NULL)
174 message_callback(_("ClamAV: scanning message..."));
176 g_node_traverse(mimeinfo->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, scan_func, &result);
177 debug_print("status: %d\n", result.status);
179 if (result.status == VIRUS) {
180 if (config.clamav_recv_infected) {
181 FolderItem *clamav_save_folder;
183 if ((!config.clamav_save_folder) ||
184 (config.clamav_save_folder[0] == '\0') ||
185 ((clamav_save_folder = folder_find_item_from_identifier(config.clamav_save_folder)) == NULL))
186 clamav_save_folder = folder_get_default_trash();
188 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
189 msginfo->filter_op = IS_MOVE;
190 msginfo->to_filter_folder = clamav_save_folder;
192 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
196 procmime_mimeinfo_free_all(&mimeinfo);
198 return (result.status == OK) ? FALSE : TRUE;
201 Clamd_Stat clamd_prepare(void) {
202 debug_print("Creating socket\n");
203 if (!config.clamd_config_type
204 || (config.clamd_host != NULL
205 && *(config.clamd_host) != '\0'
206 && config.clamd_port > 0)) {
207 if (config.clamd_host == NULL
208 || *(config.clamd_host) == '\0'
209 || config.clamd_port == 0) {
213 /* Manual configuration has highest priority */
214 debug_print("Using user input: %s:%d\n",
215 config.clamd_host, config.clamd_port);
216 clamd_create_config_manual(config.clamd_host, config.clamd_port);
218 else if (config.clamd_config_type || config.clamd_config_folder != NULL) {
219 if (config.clamd_config_folder == NULL) {
223 debug_print("Using clamd.conf: %s\n", config.clamd_config_folder);
224 clamd_create_config_automatic(config.clamd_config_folder);
227 /* Fall back. Try enable anyway */
228 if (! clamd_find_socket())
232 return clamd_init(NULL);
235 ClamAvConfig *clamav_get_config(void)
240 void clamav_save_config(void)
245 debug_print("Saving Clamd Page\n");
247 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
248 pfile = prefs_write_open(rcpath);
250 if (!pfile || (prefs_set_block_label(pfile, "ClamAV") < 0))
253 if (prefs_write_param(param, pfile->fp) < 0) {
254 g_warning("failed to write Clamd configuration to file");
255 prefs_file_close_revert(pfile);
258 if (fprintf(pfile->fp, "\n") < 0) {
259 FILE_OP_ERROR(rcpath, "fprintf");
260 prefs_file_close_revert(pfile);
262 prefs_file_close(pfile);
265 void clamav_set_message_callback(MessageCallback callback)
267 message_callback = callback;
270 gint plugin_init(gchar **error)
274 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
275 VERSION_NUMERIC, PLUGIN_NAME, error))
278 hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST, mail_filtering_hook, NULL);
280 *error = g_strdup(_("Failed to register mail filtering hook"));
284 prefs_set_default(param);
285 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
286 prefs_read_config(param, "ClamAV", rcpath, NULL);
291 if (config.clamav_enable) {
292 debug_print("Creating socket\n");
293 config.alert_ack = TRUE;
294 Clamd_Stat status = clamd_prepare();
297 g_warning("[init] No socket information");
298 alertpanel_error(_("Init\nNo socket information.\nAntivirus disabled."));
301 g_warning("[init] Clamd does not respond to ping");
302 alertpanel_warning(_("Init\nClamd does not respond to ping.\nIs clamd running?"));
309 debug_print("Clamd plugin loaded\n");
315 gboolean plugin_done(void)
317 hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id);
318 g_free(config.clamav_save_folder);
322 debug_print("Clamd plugin unloaded\n");
326 const gchar *plugin_name(void)
331 const gchar *plugin_desc(void)
333 return _("This plugin uses Clam AntiVirus to scan all messages that are "
334 "received from an IMAP, LOCAL or POP account.\n"
336 "When a message attachment is found to contain a virus it can be "
337 "deleted or saved in a specially designated folder.\n"
339 "Because this plugin communicates with clamd via a\n"
340 "socket then there are some minimum requirements to\n"
341 "the permissions for your home folder and the\n"
342 ".claws-mail folder provided the clamav-daemon is\n"
343 "configured to communicate via a unix socket. All\n"
344 "users at least need to be given execute permissions\n"
345 "on these folders.\n"
347 "To avoid changing permissions you could configure\n"
348 "the clamav-daemon to communicate via a TCP socket\n"
349 "and choose manual configuration for clamd.\n"
351 "Options can be found in /Configuration/Preferences/Plugins/Clam AntiVirus");
354 const gchar *plugin_type(void)
359 const gchar *plugin_licence(void)
364 const gchar *plugin_version(void)
369 struct PluginFeature *plugin_provides(void)
371 static struct PluginFeature features[] =
372 { {PLUGIN_FILTERING, N_("Virus detection")},
373 {PLUGIN_NOTHING, NULL}};