2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2010 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", NULL, &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 (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\n", msg);
122 debug_print("no_recv: %d\n", prefs_common.no_recv_err_panel);
123 if (prefs_common.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 || (config.clamd_host != NULL && config.clamd_port > 0)) {
204 if (config.clamd_host == NULL || config.clamd_port < 1) {
208 /* Manual configuration has highest priority */
209 debug_print("Using user input: %s:%d\n",
210 config.clamd_host, config.clamd_port);
211 clamd_create_config_manual(config.clamd_host, config.clamd_port);
213 else if (config.clamd_config_type || config.clamd_config_folder != NULL) {
214 if (config.clamd_config_folder == NULL) {
218 debug_print("Using clamd.conf: %s\n", config.clamd_config_folder);
219 clamd_create_config_automatic(config.clamd_config_folder);
222 /* Fall back. Try enable anyway */
223 if (! clamd_find_socket())
227 return clamd_init(NULL);
230 ClamAvConfig *clamav_get_config(void)
235 void clamav_save_config(void)
240 debug_print("Saving Clamd Page\n");
242 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
243 pfile = prefs_write_open(rcpath);
245 if (!pfile || (prefs_set_block_label(pfile, "ClamAV") < 0))
248 if (prefs_write_param(param, pfile->fp) < 0) {
249 g_warning("failed to write Clamd configuration to file\n");
250 prefs_file_close_revert(pfile);
253 if (fprintf(pfile->fp, "\n") < 0) {
254 FILE_OP_ERROR(rcpath, "fprintf");
255 prefs_file_close_revert(pfile);
257 prefs_file_close(pfile);
260 void clamav_set_message_callback(MessageCallback callback)
262 message_callback = callback;
265 gint plugin_init(gchar **error)
269 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
270 VERSION_NUMERIC, PLUGIN_NAME, error))
273 hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST, mail_filtering_hook, NULL);
275 *error = g_strdup(_("Failed to register mail filtering hook"));
279 prefs_set_default(param);
280 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
281 prefs_read_config(param, "ClamAV", rcpath, NULL);
286 if (config.clamav_enable) {
287 debug_print("Creating socket\n");
288 config.alert_ack = TRUE;
289 Clamd_Stat status = clamd_prepare();
292 g_warning("[init] No socket information");
293 alertpanel_error(_("Init\nNo socket information.\nAntivirus disabled."));
296 g_warning("[init] Clamd does not respond to ping");
297 alertpanel_warning(_("Init\nClamd does not respond to ping.\nIs clamd running?"));
304 debug_print("Clamd plugin loaded\n");
310 gboolean plugin_done(void)
312 hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id);
313 g_free(config.clamav_save_folder);
317 debug_print("Clamd plugin unloaded\n");
321 const gchar *plugin_name(void)
326 const gchar *plugin_desc(void)
328 return _("This plugin uses Clam AntiVirus to scan all messages that are "
329 "received from an IMAP, LOCAL or POP account.\n"
331 "When a message attachment is found to contain a virus it can be "
332 "deleted or saved in a specially designated folder.\n"
334 "Because this plugin communicates with clamd via a\n"
335 "socket then there are some minimum requirements to\n"
336 "the permissions for your home folder and the\n"
337 ".claws-mail folder provided the clamav-daemon is\n"
338 "configured to communicate via a unix socket. All\n"
339 "users at least need to be given execute permissions\n"
340 "on these folders.\n"
342 "To avoid changing permissions you could configure\n"
343 "the clamav-daemon to communicate via a TCP socket\n"
344 "and choose manual configuration for clamd.\n"
346 "Options can be found in /Configuration/Preferences/Plugins/Clam AntiVirus");
349 const gchar *plugin_type(void)
354 const gchar *plugin_licence(void)
359 const gchar *plugin_version(void)
364 struct PluginFeature *plugin_provides(void)
366 static struct PluginFeature features[] =
367 { {PLUGIN_FILTERING, N_("Virus detection")},
368 {PLUGIN_NOTHING, NULL}};