/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Author: * Radek Doulik * * Copyright 2003 Ximian, Inc. (www.ximian.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include #include #include #include #include #include #include #include "mail-session.h" #include "em-junk-filter.h" #include #include #define d(x) x static pthread_mutex_t em_junk_sa_init_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t em_junk_sa_report_lock = PTHREAD_MUTEX_INITIALIZER; static const char *em_junk_sa_get_name (void); static gboolean em_junk_sa_check_junk (CamelMimeMessage *msg); static void em_junk_sa_report_junk (CamelMimeMessage *msg); static void em_junk_sa_report_notjunk (CamelMimeMessage *msg); static void em_junk_sa_commit_reports (void); static EMJunkPlugin spam_assassin_plugin = { { em_junk_sa_get_name, 1, em_junk_sa_check_junk, em_junk_sa_report_junk, em_junk_sa_report_notjunk, em_junk_sa_commit_reports, }, NULL, NULL }; static gboolean em_junk_sa_tested = FALSE; static gboolean em_junk_sa_spamd_tested = FALSE; static gboolean em_junk_sa_use_spamc = FALSE; static gboolean em_junk_sa_available = FALSE; static int em_junk_sa_spamd_port = -1; static GConfClient *em_junk_sa_gconf = NULL; static const char * em_junk_sa_get_name (void) { return _("Spamassassin (built-in)"); } static gboolean em_junk_sa_get_local_only () { return gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/local_only", NULL); } static gboolean em_junk_sa_get_use_daemon () { return gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/use_daemon", NULL); } static int em_junk_sa_get_daemon_port () { return gconf_client_get_int (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/daemon_port", NULL); } static int pipe_to_sa_with_error (CamelMimeMessage *msg, const char *in, char **argv, int rv_err) { int result, status, errnosav, fds[2]; CamelStream *stream; pid_t pid; #if d(!)0 { int i; printf ("pipe_to_sa "); for (i = 0; argv[i]; i++) printf ("%s ", argv[i]); printf ("\n"); } #endif if (pipe (fds) == -1) { errnosav = errno; d(printf ("failed to create a pipe (for use with spamassassin: %s\n", strerror (errno))); errno = errnosav; return rv_err; } if (!(pid = fork ())) { /* child process */ int maxfd, fd, nullfd; nullfd = open ("/dev/null", O_WRONLY); if (dup2 (fds[0], STDIN_FILENO) == -1 || dup2 (nullfd, STDOUT_FILENO) == -1 || dup2 (nullfd, STDERR_FILENO) == -1) _exit (rv_err & 0377); setsid (); maxfd = sysconf (_SC_OPEN_MAX); for (fd = 3; fd < maxfd; fd++) fcntl (fd, F_SETFD, FD_CLOEXEC); execvp (argv[0], argv); _exit (rv_err & 0377); } else if (pid < 0) { errnosav = errno; close (fds[0]); close (fds[1]); errno = errnosav; return rv_err; } /* parent process */ close (fds[0]); if (msg) { stream = camel_stream_fs_new_with_fd (fds[1]); camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream); camel_stream_flush (stream); camel_object_unref (stream); } else if (in) { camel_write (fds[1], in, strlen (in)); close (fds[1]); } result = waitpid (pid, &status, 0); if (result == -1 && errno == EINTR) { /* child process is hanging... */ kill (pid, SIGTERM); sleep (1); result = waitpid (pid, &status, WNOHANG); if (result == 0) { /* ...still hanging, set phasers to KILL */ kill (pid, SIGKILL); sleep (1); result = waitpid (pid, &status, WNOHANG); } } if (result != -1 && WIFEXITED (status)) return WEXITSTATUS (status); else return rv_err; } static int pipe_to_sa (CamelMimeMessage *msg, const char *in, char **argv) { return pipe_to_sa_with_error (msg, in, argv, -1); } static int em_junk_sa_test_spamd_running (int port) { char port_buf[12], *argv[5]; int i = 0; d(fprintf (stderr, "test if spamd is running (port %d)\n", port)); argv[i++] = "spamc"; argv[i++] = "-x"; if (port > 0) { sprintf (port_buf, "%d", port); argv[i++] = "-p"; argv[i++] = port_buf; } argv[i] = NULL; return pipe_to_sa (NULL, "From test@127.0.0.1", argv) == 0; } static void em_junk_sa_test_spamassassin (void) { char *argv [3] = { "spamassassin", "--version", NULL, }; if (pipe_to_sa (NULL, NULL, argv) != 0) em_junk_sa_available = FALSE; else em_junk_sa_available = TRUE; em_junk_sa_tested = TRUE; } #define MAX_SPAMD_PORTS 1 static void em_junk_sa_test_spamd (void) { int port = em_junk_sa_get_daemon_port (); int i; em_junk_sa_use_spamc = FALSE; if (em_junk_sa_test_spamd_running (-1)) { em_junk_sa_use_spamc = TRUE; em_junk_sa_spamd_port = -1; } else { for (i = 0; i < MAX_SPAMD_PORTS; i ++, port ++) { if (em_junk_sa_test_spamd_running (port)) { em_junk_sa_use_spamc = TRUE; em_junk_sa_spamd_port = port; break; } } } if (!em_junk_sa_use_spamc) { char *argv[6]; char port_buf[12]; int i = 0; d(fprintf (stderr, "looks like spamd is not running\n")); argv[i++] = "spamd"; argv[i++] = "--port"; argv[i++] = port_buf; if (em_junk_sa_get_local_only ()) argv[i++] = "--local"; argv[i++] = "--daemonize"; argv[i] = NULL; for (i = 0; i < MAX_SPAMD_PORTS; i++, port++) { d(fprintf (stderr, "trying to run spamd at port %d\n", port)); sprintf (port_buf, "%d", port); if (!pipe_to_sa (NULL, NULL, argv)) { em_junk_sa_use_spamc = TRUE; em_junk_sa_spamd_port = port; d(fprintf (stderr, "success at port %d\n", port)); break; } } } d(fprintf (stderr, "use spamd %d at port %d\n", em_junk_sa_use_spamc, em_junk_sa_spamd_port)); em_junk_sa_spamd_tested = TRUE; } static gboolean em_junk_sa_is_available (void) { pthread_mutex_lock (&em_junk_sa_init_lock); if (!em_junk_sa_gconf) { em_junk_sa_gconf = gconf_client_get_default(); gconf_client_add_dir (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); } if (!em_junk_sa_tested) em_junk_sa_test_spamassassin (); if (em_junk_sa_available && !em_junk_sa_spamd_tested && em_junk_sa_get_use_daemon ()) em_junk_sa_test_spamd (); pthread_mutex_unlock (&em_junk_sa_init_lock); return em_junk_sa_available; } static gboolean em_junk_sa_check_junk (CamelMimeMessage *msg) { char *argv[5], buf[12]; int i = 0, rv; d(fprintf (stderr, "em_junk_sa_check_junk\n")); if (!em_junk_sa_is_available ()) return FALSE; if (em_junk_sa_use_spamc && em_junk_sa_get_use_daemon ()) { argv[i++] = "spamc"; argv[i++] = "-c"; if (em_junk_sa_spamd_port != -1) { sprintf (buf, "%d", em_junk_sa_spamd_port); argv[i++] = "-p"; argv[i++] = buf; } } else { argv [i++] = "spamassassin"; argv [i++] = "--exit-code"; if (em_junk_sa_get_local_only ()) argv [i++] = "--local"; } argv[i] = NULL; return pipe_to_sa_with_error (msg, NULL, argv, 0) != 0; } static void em_junk_sa_report_junk (CamelMimeMessage *msg) { char *argv[6] = { "sa-learn", "--no-rebuild", "--spam", "--single", NULL, NULL }; d(fprintf (stderr, "em_junk_sa_report_junk\n")); if (em_junk_sa_is_available ()) { if (em_junk_sa_get_local_only ()) argv[4] = "--local"; pthread_mutex_lock (&em_junk_sa_report_lock); pipe_to_sa (msg, NULL, argv); pthread_mutex_unlock (&em_junk_sa_report_lock); } } static void em_junk_sa_report_notjunk (CamelMimeMessage *msg) { char *argv[6] = { "sa-learn", "--no-rebuild", "--ham", "--single", NULL, NULL }; d(fprintf (stderr, "em_junk_sa_report_notjunk\n")); if (em_junk_sa_is_available ()) { if (em_junk_sa_get_local_only ()) argv[4] = "--local"; pthread_mutex_lock (&em_junk_sa_report_lock); pipe_to_sa (msg, NULL, argv); pthread_mutex_unlock (&em_junk_sa_report_lock); } } static void em_junk_sa_commit_reports (void) { char *argv[4] = { "sa-learn", "--rebuild", NULL, NULL }; d(fprintf (stderr, "em_junk_sa_commit_reports\n")); if (em_junk_sa_is_available ()) { if (em_junk_sa_get_local_only ()) argv[2] = "--local"; pthread_mutex_lock (&em_junk_sa_report_lock); pipe_to_sa (NULL, NULL, argv); pthread_mutex_unlock (&em_junk_sa_report_lock); } } const EMJunkPlugin * em_junk_filter_get_plugin (void) { return &spam_assassin_plugin; }