1 /* vim: set textwidth=80 tabstop=4: */
4 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
5 * Copyright (C) 1999-2008 Michael Rasmussen and the Claws Mail Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include <glib/gi18n.h>
31 #include <gtk/gtkutils.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
41 #include <netinet/in.h>
43 #include <arpa/inet.h>
46 #include "common/claws.h"
47 #include "common/version.h"
52 #include "prefs_gtk.h"
53 #include "foldersel.h"
54 #include "statusbar.h"
55 #include "alertpanel.h"
56 #include "clamd-plugin.h"
58 /* needs to be generic */
59 static const gchar* config_dirs[] = {
63 "/usr/local/etc/clamav",
66 static const gchar* clamd_tokens[] = {
72 static Clamd_Socket* Socket = NULL;
74 static Config* config = NULL;
78 * prefixing with either z or n is recommended
79 * z <=> null terminated command
80 * n <=> newline terminated command
82 static const gchar ping[] = "nPING\n";
83 static const gchar version[] = "nVERSION\n";
84 static const gchar scan[] = "nSCAN";
85 static const gchar contscan[] = "nCONTSCAN";
86 static const gchar instream[10] = "zINSTREAM\0";
88 void clamd_create_config_automatic(const gchar* path) {
94 /*debug_set_mode(TRUE);*/
95 /*debug_print("%s : %s\n", folder, path);*/
97 g_warning("Missing path");
100 if (config && config->ConfigType == AUTOMATIC &&
101 config->automatic.folder &&
102 strcmp(config->automatic.folder, path) == 0) {
103 debug_print("%s : %s - Identical. No need to read again\n",
104 config->automatic.folder, path);
108 clamd_config_free(config);
109 config = clamd_config_new();
111 config->ConfigType = AUTOMATIC;
112 config->automatic.folder = g_strdup(path);
113 debug_print("Opening %s to parse config file\n", path);
114 conf = fopen(path, "r");
116 /*g_error("%s: Unable to open", path);*/
117 alertpanel_error(_("%s: Unable to open\nclamd will be disabled"), path);
120 while (fgets(buf, sizeof(buf), conf)) {
124 const gchar** tokens = clamd_tokens;
126 const gchar* token = *tokens++;
127 if ((key = g_strstr_len(buf, strlen(buf), token)) != NULL) {
128 gchar* tmp = &(*(key + strlen(token)));
129 tmp = g_strchug(tmp);
130 gchar* end = index(tmp, '#');
132 value = g_strndup(tmp, end - tmp);
134 value = g_strdup(g_strchomp(tmp));
135 if (strcmp(clamd_tokens[0], token) == 0) {
137 Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
139 Socket->socket.path = NULL;
140 Socket->socket.host = NULL;
141 Socket->socket.port = -1;
142 Socket->type = UNIX_SOCKET;
143 Socket->socket.path = g_strdup(value);
147 debug_print("clamctl: %s\n", Socket->socket.path);
151 else if (strcmp(clamd_tokens[1], token) == 0) {
154 Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
156 Socket->socket.path = NULL;
157 Socket->socket.host = NULL;
158 Socket->socket.port = -1;
159 Socket->type = INET_SOCKET;
160 Socket->socket.port = atoi(value);
161 Socket->socket.host = g_strdup("localhost");
164 debug_print("clamctl: %s:%d\n",
165 Socket->socket.host, Socket->socket.port);
169 Socket->type = INET_SOCKET;
170 Socket->socket.port = atoi(value);
173 if (! Socket->socket.host)
174 Socket->socket.host = g_strdup("localhost");
175 debug_print("clamctl: %s:%d\n",
176 Socket->socket.host, Socket->socket.port);
178 /* We must continue since TCPAddr could also be configured */
180 else if (strcmp(clamd_tokens[2], token) == 0) {
182 Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
184 Socket->socket.path = NULL;
185 Socket->socket.host = NULL;
186 Socket->socket.port = 3310; /* default port */
187 Socket->type = INET_SOCKET;
188 Socket->socket.host = g_strdup(value);
191 debug_print("clamctl: %s:%d\n",
192 Socket->socket.host, Socket->socket.port);
196 Socket->type = INET_SOCKET;
197 if (Socket->socket.host)
198 g_free(Socket->socket.host);
199 Socket->socket.host = g_strdup(value);
202 if (Socket->socket.port == -1)
203 Socket->socket.port = 3310;
204 debug_print("clamctl: %s:%d\n",
205 Socket->socket.host, Socket->socket.port);
207 /* We must continue since TCPSocket could also be configured */
213 if (! (Socket && (Socket->socket.port || Socket->socket.path))) {
214 /*g_error("%s: Not able to find required information", path);*/
215 alertpanel_error(_("%s: Not able to find required information\nclamd will be disabled"), path);
217 /*debug_set_mode(FALSE);*/
220 void clamd_create_config_manual(const gchar* host, int port) {
221 if (! host || port < 1) {
222 g_warning("Missing host or port < 1");
225 if (config && config->ConfigType == MANUAL &&
226 config->manual.host && config->manual.port == port &&
227 strcmp(config->manual.host, host) == 0) {
228 debug_print("%s : %s and %d : %d - Identical. No need to read again\n",
229 config->manual.host, host, config->manual.port, port);
234 clamd_config_free(config);
235 config = clamd_config_new();
237 config->ConfigType = MANUAL;
238 config->manual.host = g_strdup(host);
239 config->manual.port = port;
241 Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
243 Socket->type = INET_SOCKET;
244 Socket->socket.port = port;
245 Socket->socket.host = g_strdup(host);
248 /*g_error("%s: Not able to find required information", path);*/
249 alertpanel_error(_("Could not create socket"));
253 gboolean clamd_find_socket() {
254 const gchar** config_dir = config_dirs;
255 gchar *clamd_conf = NULL;
257 while (*config_dir) {
258 clamd_conf = g_strdup_printf("%s/clamd.conf", *config_dir++);
259 debug_print("Looking for %s\n", clamd_conf);
260 if (g_file_test(clamd_conf, G_FILE_TEST_EXISTS))
268 debug_print("Using %s to find configuration\n", clamd_conf);
269 clamd_create_config_automatic(clamd_conf);
275 Config* clamd_get_config() {
279 Clamd_Socket* clamd_get_socket() {
283 static void close_socket() {
284 debug_print("Closing socket: %d\n", sock);
288 static void create_socket() {
289 struct sockaddr_un addr_u;
290 struct sockaddr_in addr_i;
293 /*debug_set_mode(TRUE);*/
298 memset(&addr_u, 0, sizeof(addr_u));
299 memset(&addr_i, 0, sizeof(addr_i));
300 debug_print("socket->type: %d\n", Socket->type);
301 switch (Socket->type) {
303 debug_print("socket path: %s\n", Socket->socket.path);
304 sock = socket(PF_UNIX, SOCK_STREAM, 0);
305 debug_print("socket file (create): %d\n", sock);
308 addr_u.sun_family = AF_UNIX;
309 memcpy(addr_u.sun_path, Socket->socket.path,
310 strlen(Socket->socket.path));
311 if (connect(sock, (struct sockaddr *) &addr_u, sizeof(addr_u)) < 0) {
312 perror("connect socket");
316 debug_print("socket file (connect): %d\n", sock);
319 addr_i.sin_family = AF_INET;
320 addr_i.sin_port = htons(Socket->socket.port);
321 hp = gethostbyname(Socket->socket.host);
322 bcopy((void *)hp->h_addr, (void *)&addr_i.sin_addr, hp->h_length);
323 sock = socket(PF_INET, SOCK_STREAM, 0);
326 if (connect(sock, (struct sockaddr *)&addr_i, sizeof(addr_i)) < 0) {
327 perror("connect socket");
333 /*debug_set_mode(FALSE);*/
336 static void copy_socket(Clamd_Socket* sock) {
337 Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
338 Socket->socket.path = NULL;
339 Socket->socket.host = NULL;
340 Socket->type = sock->type;
341 if (Socket->type == UNIX_SOCKET) {
342 Socket->socket.path = g_strdup(sock->socket.path);
345 Socket->socket.host = g_strdup(sock->socket.host);
346 Socket->socket.port = sock->socket.port;
350 Clamd_Stat clamd_init(Clamd_Socket* config) {
353 gboolean connect = FALSE;
355 /*debug_set_mode(TRUE);*/
356 if (config != NULL && Socket != NULL)
359 debug_print("socket: %s\n", config->socket.path);
364 debug_print("no connection\n");
365 return NO_CONNECTION;
367 if (write(sock, ping, strlen(ping)) == -1) {
368 debug_print("no connection\n");
369 return NO_CONNECTION;
371 memset(buf, '\0', sizeof(buf));
372 while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
373 if (buf[strlen(buf) - 1] == '\n')
374 buf[strlen(buf) - 1] = '\0';
375 debug_print("Ping result: %s\n", buf);
376 if (strcmp("PONG", buf) == 0)
382 debug_print("no connection\n");
383 return NO_CONNECTION;
385 if (write(sock, version, strlen(version)) == -1) {
386 debug_print("no connection\n");
387 return NO_CONNECTION;
389 memset(buf, '\0', sizeof(buf));
390 while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
391 if (buf[strlen(buf) - 1] == '\n')
392 buf[strlen(buf) - 1] = '\0';
393 debug_print("Version: %s\n", buf);
396 /*debug_set_mode(FALSE);*/
397 return (connect) ? OK : NO_CONNECTION;
400 static Clamd_Stat clamd_stream_scan(
401 const gchar* path, gchar** res, ssize_t size) {
408 debug_print("Scanning: %s\n", path);
410 memset(buf, '\0', sizeof(buf));
412 if (! res || size < 1) {
416 *res = g_new(gchar, size);
417 memset(*res, '\0', size);
419 if (! g_file_test(path, G_FILE_TEST_EXISTS)) {
420 *res = g_strconcat("ERROR -> ", path, _(": File does not exist"), NULL);
421 debug_print("res: %s\n", *res);
426 fd = open(path, O_RDONLY, O_LARGEFILE);
428 fd = open(path, O_RDONLY);
432 /*g_error("%s: Unable to open", path);*/
433 *res = g_strconcat("ERROR -> ", path, _(": Unable to open"), NULL);
437 debug_print("command: %s\n", instream);
438 if (write(sock, instream, strlen(instream) + 1) == -1) {
440 return NO_CONNECTION;
443 while ((count = read(fd, (void *) buf, sizeof(buf))) > 0) {
446 *res = g_strconcat("ERROR -> ", path, _("%s: Error reading"), NULL);
449 if (buf[strlen(buf) - 1] == '\n')
450 buf[strlen(buf) - 1] = '\0';
451 debug_print("read: %ld bytes\n", count);
453 debug_print("chunk size: %ld\n", count);
454 chunk = htonl(count);
455 if (write(sock, &chunk, 4) == -1) {
457 *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
460 if (write(sock, buf, count) == -1) {
462 *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
465 memset(buf, '\0', sizeof(buf));
470 if (write(sock, &chunk, 4) == -1) {
471 *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
475 debug_print("reading from socket\n");
476 n_read = read(sock, *res, size);
478 *res = g_strconcat("ERROR -> ", _("Socket read error"), NULL);
481 debug_print("received: %s\n", *res);
485 Clamd_Stat clamd_verify_email(const gchar* path, response* result) {
491 /*debug_set_mode(TRUE);*/
493 result = malloc(sizeof(response));
494 memset(result, '\0', sizeof(response));
498 debug_print("no connection\n");
499 return NO_CONNECTION;
501 memset(buf, '\0', sizeof(buf));
502 if (Socket->type == INET_SOCKET) {
503 gchar* tmp = g_new0(gchar, BUFSIZ);
504 stat = clamd_stream_scan(path, &tmp, BUFSIZ);
507 result->msg = g_strdup(tmp);
509 debug_print("result: %s\n", result->msg);
510 /*debug_set_mode(FALSE);*/
513 debug_print("copy to buf: %s\n", tmp);
514 memcpy(&buf, tmp, BUFSIZ);
518 command = g_strconcat(scan, " ", path, "\n", NULL);
519 debug_print("command: %s\n", command);
520 if (write(sock, command, strlen(command)) == -1) {
521 debug_print("no connection\n");
522 stat = NO_CONNECTION;
525 memset(buf, '\0', sizeof(buf));
526 while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
527 if (buf[strlen(buf) - 1] == '\n')
528 buf[strlen(buf) - 1] = '\0';
531 debug_print("response: %s\n", buf);
532 if (strstr(buf, "ERROR")) {
534 result->msg = g_strdup(buf);
536 else if (strstr(buf, "FOUND")) {
538 result->msg = g_strdup(buf);
545 /*debug_set_mode(FALSE);*/
550 GSList* clamd_verify_dir(const gchar* path) {
556 if (Socket->type == INET_SOCKET)
561 debug_print("No socket\n");
564 command = g_strconcat(contscan, path, "\n", NULL);
565 debug_print("command: %s\n", command);
566 if (write(sock, command, strlen(command)) == -1) {
567 debug_print("No socket\n");
571 memset(buf, '\0', sizeof(buf));
572 while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
573 gchar** tmp = g_strsplit(buf, "\n", 0);
576 gchar* file = *tmp++;
577 debug_print("%s\n", file);
578 if (strstr(file, "ERROR")) {
579 g_warning("%s", file);
580 /* dont report files with errors */
582 else if (strstr(file, "FOUND")) {
583 list = g_slist_append(list, g_strdup(file));
592 void clamd_free_gslist(GSList* list) {
596 tmp = g_slist_next(tmp);
601 gchar* clamd_get_virus_name(gchar* msg) {
602 gchar *head, *tail, *name;
604 tail = g_strrstr_len(msg, strlen(msg), "FOUND");
607 head = g_strstr_len(msg, strlen(msg), ":");
609 name = g_strndup(head, tail - head);
616 * struct _Clamd_Socket {
634 switch (Socket->type) {
636 if (Socket->socket.path) {
637 g_free(Socket->socket.path);
638 Socket->socket.path = NULL;
642 if (Socket->socket.host) {
643 g_free(Socket->socket.host);
644 Socket->socket.host = NULL;
652 clamd_config_free(config);
657 Config* clamd_config_new() {
658 return g_new0(Config, 1);
661 void clamd_config_free(Config* c) {
662 if (c->ConfigType == AUTOMATIC) {
663 g_free(c->automatic.folder);
664 c->automatic.folder = NULL;
667 g_free(c->manual.host);
668 c->manual.host = NULL;
673 gchar* int2char(int i) {
674 gchar* s = g_new0(gchar, 5);
681 gchar* long2char(long l) {
682 gchar* s = g_new0(gchar, 5);
684 debug_print("l: %ld\n", l);
685 sprintf(s, "%ld", l);
686 debug_print("s: %s\n", s);