static void bogofilter_do_filter(BogoFilterData *data) { GPid bogo_pid; gint bogo_stdin, bogo_stdout; GError *error = NULL; gboolean bogo_forked; int status = 0; MsgInfo *msginfo; GSList *cur = NULL; int total = 0, curnum = 1; gchar *file = NULL; gchar buf[BUFSIZ]; total = g_slist_length(data->msglist); bogo_forked = g_spawn_async_with_pipes( NULL, data->bogo_args,NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &bogo_pid, &bogo_stdin, &bogo_stdout, NULL, &error); if (bogo_forked == FALSE) { g_warning("%s", error ? error->message:"ERROR???"); g_error_free(error); error = NULL; status = -1; } else { if (config.whitelist_ab) { gchar *ab_folderpath; if (*config.whitelist_ab_folder == '\0' || strcasecmp(config.whitelist_ab_folder, "Any") == 0) { /* match the whole addressbook */ ab_folderpath = NULL; } else { /* match the specific book/folder of the addressbook */ ab_folderpath = config.whitelist_ab_folder; } start_address_completion(ab_folderpath); } for (cur = data->msglist; cur; cur = cur->next) { gboolean whitelisted = FALSE; msginfo = (MsgInfo *)cur->data; debug_print("Filtering message %d (%d/%d)\n", msginfo->msgnum, curnum, total); if (message_callback != NULL) message_callback(NULL, total, curnum++, data->in_thread); if (config.whitelist_ab && msginfo->from && found_in_addressbook(msginfo->from)) whitelisted = TRUE; /* can set flags (SCANNED, ATTACHMENT) but that's ok * as GUI updates are hooked not direct */ file = procmsg_get_message_file(msginfo); if (file) { gchar *tmp = g_strdup_printf("%s\n",file); /* send filename to bogofilter */ write_all(bogo_stdin, tmp, strlen(tmp)); g_free(tmp); memset(buf, 0, sizeof(buf)); /* get the result */ if (read(bogo_stdout, buf, sizeof(buf)-1) < 0) { g_warning("bogofilter short read"); debug_print("message %d is ham\n", msginfo->msgnum); data->mail_filtering_data->unfiltered = g_slist_prepend( data->mail_filtering_data->unfiltered, msginfo); data->new_hams = g_slist_prepend(data->new_hams, msginfo); } else { gchar **parts = NULL; buf[sizeof(buf) - 1] = '\0'; if (strchr(buf, '/')) { tmp = strrchr(buf, '/')+1; } else { tmp = buf; } parts = g_strsplit(tmp, " ", 0); debug_print("read %s\n", buf); /* note the result if the header if needed */ if (parts && parts[0] && parts[1] && parts[2] && FOLDER_TYPE(msginfo->folder->folder) == F_MH && config.insert_header) { gchar *tmpfile = get_tmp_file(); FILE *input = claws_fopen(file, "r"); FILE *output = claws_fopen(tmpfile, "w"); if (strstr(parts[2], "\n")) *(strstr(parts[2], "\n")) = '\0'; if (input && !output) claws_fclose (input); else if (!input && output) claws_fclose (output); else if (input && output) { gchar tmpbuf[BUFFSIZE]; gboolean err = FALSE; const gchar *bogosity = *parts[1] == 'S' ? "Spam": (*parts[1] == 'H' ? "Ham":"Unsure"); gchar *tmpstr = g_strdup_printf( "X-Bogosity: %s, spamicity=%s%s\n", bogosity, parts[2], whitelisted?" [whitelisted]":""); if (claws_fwrite(tmpstr, 1, strlen(tmpstr), output) < strlen(tmpstr)) { err = TRUE; } else { while (claws_fgets(tmpbuf, sizeof(buf), input)) { if (claws_fputs(tmpbuf, output) == EOF) { err = TRUE; break; } } } claws_fclose(input); if (claws_safe_fclose(output) == EOF) err = TRUE; if (!err) move_file(tmpfile, file, TRUE); g_free(tmpstr); } g_free(tmpfile); } /* file the mail */ if (!whitelisted && parts && parts[0] && parts[1] && *parts[1] == 'S') { debug_print("message %d is spam\n", msginfo->msgnum); /* Spam will be filtered away, unless we want "mark only". * In that case, we want it among unfiltered messages, so * it gets processed further. */ if (config.receive_spam == SPAM_MARK_ONLY) { data->mail_filtering_data->unfiltered = g_slist_prepend( data->mail_filtering_data->unfiltered, msginfo); } else { data->mail_filtering_data->filtered = g_slist_prepend( data->mail_filtering_data->filtered, msginfo); } data->new_spams = g_slist_prepend(data->new_spams, msginfo); } else if (whitelisted && parts && parts[0] && parts[1] && (*parts[1] == 'S' || *parts[1] == 'U')) { debug_print("message %d is whitelisted %s\n", msginfo->msgnum, *parts[1] == 'S' ? "spam":"unsure"); /* Whitelisted spam will *not* be filtered away, but continue * their trip through filtering as if it was ham. */ data->mail_filtering_data->unfiltered = g_slist_prepend( data->mail_filtering_data->unfiltered, msginfo); /* But it gets put in a different list, so that we * can still flag it and inform the user that it is * considered a spam (so that he can teach bogo that * it was not). */ data->whitelisted_new_spams = g_slist_prepend(data->whitelisted_new_spams, msginfo); } else if (config.save_unsure && parts && parts[0] && parts[1] && *parts[1] == 'U') { debug_print("message %d is unsure\n", msginfo->msgnum); /* Spam will be filtered away */ data->mail_filtering_data->filtered = g_slist_prepend( data->mail_filtering_data->filtered, msginfo); data->new_unsure = g_slist_prepend(data->new_unsure, msginfo); } else { debug_print("message %d is ham\n", msginfo->msgnum); data->mail_filtering_data->unfiltered = g_slist_prepend( data->mail_filtering_data->unfiltered, msginfo); data->new_hams = g_slist_prepend(data->new_hams, msginfo); } g_strfreev(parts); } g_free(file); } else { data->mail_filtering_data->unfiltered = g_slist_prepend( data->mail_filtering_data->unfiltered, msginfo); data->new_hams = g_slist_prepend(data->new_hams, msginfo); } } if (config.whitelist_ab) end_address_completion(); } if (status != -1) { close(bogo_stdout); close(bogo_stdin); waitpid(bogo_pid, &status, 0); if (!WIFEXITED(status)) status = -1; else status = WEXITSTATUS(status); } to_filter_data->status = status; }
/** * End address completion. Should be called when main window with address * completion entries terminates. NOTE: this function assumes that it is * called upon destruction of the window. * \param mainwindow Main window. */ void address_completion_end(GtkWidget *mainwindow) { /* if address_completion_end() is really called on closing the window, * we don't need to unregister the set_focus_cb */ end_address_completion(); }
static gboolean mail_filtering_hook(gpointer source, gpointer data) { MailFilteringData *mail_filtering_data = (MailFilteringData *) source; MsgInfo *msginfo = mail_filtering_data->msginfo; gboolean is_spam = FALSE, error = FALSE; static gboolean warned_error = FALSE; FILE *fp = NULL; int pid = 0; int status; /* SPAMASSASSIN_DISABLED : keep test for compatibility purpose */ if (!config.enable || config.transport == SPAMASSASSIN_DISABLED) { log_warning(LOG_PROTOCOL, _("SpamAssassin plugin is disabled by its preferences.\n")); return FALSE; } debug_print("Filtering message %d\n", msginfo->msgnum); if (message_callback != NULL) message_callback(_("SpamAssassin: filtering message...")); if ((fp = procmsg_open_message(msginfo)) == NULL) { debug_print("failed to open message file\n"); return FALSE; } if (config.whitelist_ab) { gchar *ab_folderpath; gboolean whitelisted = FALSE; if (*config.whitelist_ab_folder == '\0' || strcasecmp(config.whitelist_ab_folder, "Any") == 0) { /* match the whole addressbook */ ab_folderpath = NULL; } else { /* match the specific book/folder of the addressbook */ ab_folderpath = config.whitelist_ab_folder; } start_address_completion(ab_folderpath); if (msginfo->from && sa_found_in_addressbook(msginfo->from)) whitelisted = TRUE; end_address_completion(); if (whitelisted) { debug_print("message is ham (whitelisted)\n"); fclose(fp); return FALSE; } } pid = fork(); if (pid == 0) { _exit(msg_is_spam(fp)); } else { gint running = 0; running |= CHILD_RUNNING; g_timeout_add(50, timeout_func, &running); running |= TIMEOUT_RUNNING; while(running & CHILD_RUNNING) { int ret; ret = waitpid(pid, &status, WNOHANG); if (ret == pid) { if (WIFEXITED(status)) { MsgStatus result = MSG_IS_HAM; running &= ~CHILD_RUNNING; result = WEXITSTATUS(status); is_spam = (result == MSG_IS_SPAM) ? TRUE : FALSE; error = (result == MSG_FILTERING_ERROR); } } if (ret < 0) { running &= ~CHILD_RUNNING; } /* ret == 0 continue */ g_main_context_iteration(NULL, TRUE); } while (running & TIMEOUT_RUNNING) g_main_context_iteration(NULL, TRUE); } fclose(fp); if (is_spam) { debug_print("message is spam\n"); procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); if (config.receive_spam) { FolderItem *save_folder = NULL; if ((!config.save_folder) || (config.save_folder[0] == '\0') || ((save_folder = folder_find_item_from_identifier(config.save_folder)) == NULL)) { if (mail_filtering_data->account && mail_filtering_data->account->set_trash_folder) { save_folder = folder_find_item_from_identifier( mail_filtering_data->account->trash_folder); if (save_folder) debug_print("found trash folder from account's advanced settings\n"); } if (save_folder == NULL && mail_filtering_data->account && mail_filtering_data->account->folder) { save_folder = mail_filtering_data->account->folder->trash; if (save_folder) debug_print("found trash folder from account's trash\n"); } if (save_folder == NULL && mail_filtering_data->account && !mail_filtering_data->account->folder) { if (mail_filtering_data->account->inbox) { FolderItem *item = folder_find_item_from_identifier( mail_filtering_data->account->inbox); if (item && item->folder->trash) { save_folder = item->folder->trash; debug_print("found trash folder from account's inbox\n"); } } if (!save_folder && mail_filtering_data->account->local_inbox) { FolderItem *item = folder_find_item_from_identifier( mail_filtering_data->account->local_inbox); if (item && item->folder->trash) { save_folder = item->folder->trash; debug_print("found trash folder from account's local_inbox\n"); } } } if (save_folder == NULL) { debug_print("using default trash folder\n"); save_folder = folder_get_default_trash(); } } if (config.mark_as_read) procmsg_msginfo_unset_flags(msginfo, ~0, 0); procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); msginfo->filter_op = IS_MOVE; msginfo->to_filter_folder = save_folder; } else { folder_item_remove_msg(msginfo->folder, msginfo->msgnum); } return TRUE; } else { debug_print("message is ham\n"); procmsg_msginfo_unset_flags(msginfo, MSG_SPAM, 0); } if (error) { gchar *msg = _("The SpamAssassin plugin couldn't filter " "a message. The probable cause of the error " "is an unreachable spamd daemon. Please make " "sure spamd is running and accessible."); if (!prefs_common_get_prefs()->no_recv_err_panel) { if (!warned_error) { alertpanel_error("%s", msg); } warned_error = TRUE; } else { log_error(LOG_PROTOCOL, "%s\n", msg); } } return FALSE; }