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"))
49 static gulong hook_id
= HOOK_NONE
;
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 name
= clamd_get_virus_name(buf
.msg
);
120 msg
= g_strconcat(_("Detected %s virus."),
123 g_warning("%s", msg
);
124 debug_print("no_recv: %d\n", prefs_common_get_prefs()->no_recv_err_panel
);
125 if (prefs_common_get_prefs()->no_recv_err_panel
) {
126 statusbar_print_all("%s", msg
);
129 alertpanel_warning("%s\n", msg
);
132 config
.alert_ack
= TRUE
;
135 debug_print("Error: %s\n", buf
.msg
);
136 if (config
.alert_ack
) {
137 alertpanel_error(_("Scanning error:\n%s"), buf
.msg
);
138 config
.alert_ack
= FALSE
;
142 debug_print("No virus detected.\n");
143 config
.alert_ack
= TRUE
;
148 msg
= g_strdup_printf(_("File: %s. Size (%d) greater than limit (%d)\n"), outfile
, (int) info
.st_size
, max
);
149 statusbar_print_all("%s", msg
);
150 debug_print("%s", msg
);
154 if (g_unlink(outfile
) < 0)
155 FILE_OP_ERROR(outfile
, "g_unlink");
158 return (result
->status
== OK
) ? FALSE
: TRUE
;
161 static gboolean
mail_filtering_hook(gpointer source
, gpointer data
)
163 MailFilteringData
*mail_filtering_data
= (MailFilteringData
*) source
;
164 MsgInfo
*msginfo
= mail_filtering_data
->msginfo
;
167 struct clamd_result result
;
169 if (!config
.clamav_enable
)
172 mimeinfo
= procmime_scan_message(msginfo
);
173 if (!mimeinfo
) return FALSE
;
175 debug_print("Scanning message %d for viruses\n", msginfo
->msgnum
);
176 if (message_callback
!= NULL
)
177 message_callback(_("ClamAV: scanning message..."));
179 g_node_traverse(mimeinfo
->node
, G_PRE_ORDER
, G_TRAVERSE_ALL
, -1, scan_func
, &result
);
180 debug_print("status: %d\n", result
.status
);
182 if (result
.status
== VIRUS
) {
183 if (config
.clamav_recv_infected
) {
184 FolderItem
*clamav_save_folder
;
186 if ((!config
.clamav_save_folder
) ||
187 (config
.clamav_save_folder
[0] == '\0') ||
188 ((clamav_save_folder
= folder_find_item_from_identifier(config
.clamav_save_folder
)) == NULL
))
189 clamav_save_folder
= folder_get_default_trash();
191 procmsg_msginfo_unset_flags(msginfo
, ~0, 0);
192 msginfo
->filter_op
= IS_MOVE
;
193 msginfo
->to_filter_folder
= clamav_save_folder
;
195 folder_item_remove_msg(msginfo
->folder
, msginfo
->msgnum
);
199 procmime_mimeinfo_free_all(&mimeinfo
);
201 return (result
.status
== OK
) ? FALSE
: TRUE
;
204 Clamd_Stat
clamd_prepare(void) {
205 debug_print("Creating socket\n");
206 if (!config
.clamd_config_type
207 || (config
.clamd_host
!= NULL
208 && *(config
.clamd_host
) != '\0'
209 && config
.clamd_port
> 0)) {
210 if (config
.clamd_host
== NULL
211 || *(config
.clamd_host
) == '\0'
212 || config
.clamd_port
== 0) {
216 /* Manual configuration has highest priority */
217 debug_print("Using user input: %s:%d\n",
218 config
.clamd_host
, config
.clamd_port
);
219 clamd_create_config_manual(config
.clamd_host
, config
.clamd_port
);
221 else if (config
.clamd_config_type
|| config
.clamd_config_folder
!= NULL
) {
222 if (config
.clamd_config_folder
== NULL
) {
226 debug_print("Using clamd.conf: %s\n", config
.clamd_config_folder
);
227 clamd_create_config_automatic(config
.clamd_config_folder
);
230 /* Fall back. Try enable anyway */
231 if (! clamd_find_socket())
235 return clamd_init(NULL
);
238 ClamAvConfig
*clamav_get_config(void)
243 void clamav_save_config(void)
248 debug_print("Saving Clamd Page\n");
250 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, COMMON_RC
, NULL
);
251 pfile
= prefs_write_open(rcpath
);
253 if (!pfile
|| (prefs_set_block_label(pfile
, "ClamAV") < 0))
256 if (prefs_write_param(param
, pfile
->fp
) < 0) {
257 g_warning("failed to write Clamd configuration to file");
258 prefs_file_close_revert(pfile
);
261 if (fprintf(pfile
->fp
, "\n") < 0) {
262 FILE_OP_ERROR(rcpath
, "fprintf");
263 prefs_file_close_revert(pfile
);
265 prefs_file_close(pfile
);
268 void clamav_set_message_callback(MessageCallback callback
)
270 message_callback
= callback
;
273 gint
plugin_init(gchar
**error
)
277 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
278 VERSION_NUMERIC
, PLUGIN_NAME
, error
))
281 hook_id
= hooks_register_hook(MAIL_FILTERING_HOOKLIST
, mail_filtering_hook
, NULL
);
282 if (hook_id
== HOOK_NONE
) {
283 *error
= g_strdup(_("Failed to register mail filtering hook"));
287 prefs_set_default(param
);
288 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, COMMON_RC
, NULL
);
289 prefs_read_config(param
, "ClamAV", rcpath
, NULL
);
294 if (config
.clamav_enable
) {
295 debug_print("Creating socket\n");
296 config
.alert_ack
= TRUE
;
297 Clamd_Stat status
= clamd_prepare();
300 g_warning("[init] no socket information");
301 alertpanel_error(_("Init\nNo socket information.\nAntivirus disabled."));
304 g_warning("[init] Clamd does not respond to ping");
305 alertpanel_warning(_("Init\nClamd does not respond to ping.\nIs clamd running?"));
312 debug_print("Clamd plugin loaded\n");
318 gboolean
plugin_done(void)
320 hooks_unregister_hook(MAIL_FILTERING_HOOKLIST
, hook_id
);
321 g_free(config
.clamav_save_folder
);
325 debug_print("Clamd plugin unloaded\n");
329 const gchar
*plugin_name(void)
334 const gchar
*plugin_desc(void)
336 return _("This plugin uses Clam AntiVirus to scan all messages that are "
337 "received from an IMAP, LOCAL or POP account.\n"
339 "When a message attachment is found to contain a virus it can be "
340 "deleted or saved in a specially designated folder.\n"
342 "Because this plugin communicates with clamd via a\n"
343 "socket then there are some minimum requirements to\n"
344 "the permissions for your home folder and the\n"
345 ".claws-mail folder provided the clamav-daemon is\n"
346 "configured to communicate via a unix socket. All\n"
347 "users at least need to be given execute permissions\n"
348 "on these folders.\n"
350 "To avoid changing permissions you could configure\n"
351 "the clamav-daemon to communicate via a TCP socket\n"
352 "and choose manual configuration for clamd.\n"
354 "Options can be found in /Configuration/Preferences/Plugins/Clam AntiVirus");
357 const gchar
*plugin_type(void)
362 const gchar
*plugin_licence(void)
367 const gchar
*plugin_version(void)
372 struct PluginFeature
*plugin_provides(void)
374 static struct PluginFeature features
[] =
375 { {PLUGIN_FILTERING
, N_("Virus detection")},
376 {PLUGIN_NOTHING
, NULL
}};