fix bug 4253, 'Claws metadata included in MBOX exports'
[claws.git] / src / plugins / spamassassin / spamassassin.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 the Claws Mail Team
4  *
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.
9  *
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.
14  *
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/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #include <sys/types.h>
28 #include <sys/wait.h>
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32
33 #if HAVE_LOCALE_H
34 #  include <locale.h>
35 #endif
36
37 #include "common/claws.h"
38 #include "common/version.h"
39 #include "plugin.h"
40 #include "common/utils.h"
41 #include "hooks.h"
42 #include "procmsg.h"
43 #include "folder.h"
44 #include "prefs.h"
45 #include "prefs_gtk.h"
46
47 #include "libspamc.h"
48 #include "spamassassin.h"
49 #include "inc.h"
50 #include "log.h"
51 #include "prefs_common.h"
52 #include "alertpanel.h"
53 #include "addr_compl.h"
54 #include "file-utils.h"
55
56 #ifdef HAVE_SYSEXITS_H
57 #include <sysexits.h>
58 #endif
59 #ifdef HAVE_ERRNO_H
60 #include <errno.h>
61 #endif
62 #ifdef HAVE_SYS_ERRNO_H
63 #include <sys/errno.h>
64 #endif
65 #ifdef HAVE_TIME_H
66 #include <time.h>
67 #endif
68 #ifdef HAVE_SYS_TIME_H
69 #include <sys/time.h>
70 #endif
71 #ifdef HAVE_SIGNAL_H
72 #include <signal.h>
73 #endif
74 #ifdef HAVE_PWD_H
75 #include <pwd.h>
76 #endif
77
78 #define PLUGIN_NAME (_("SpamAssassin"))
79
80 enum {
81     CHILD_RUNNING = 1 << 0,
82     TIMEOUT_RUNNING = 1 << 1,
83 };
84
85 static gulong hook_id = HOOK_NONE;
86 static int flags = SPAMC_RAW_MODE | SPAMC_SAFE_FALLBACK | SPAMC_CHECK_ONLY;
87 static MessageCallback message_callback;
88
89 static SpamAssassinConfig config;
90
91 static PrefParam param[] = {
92         {"enable", "FALSE", &config.enable, P_BOOL,
93         NULL, NULL, NULL},
94         {"transport", "0", &config.transport, P_INT,
95          NULL, NULL, NULL},
96         {"hostname", "localhost", &config.hostname, P_STRING,
97          NULL, NULL, NULL},
98         {"port", "783", &config.port, P_INT,
99          NULL, NULL, NULL},
100         {"socket", "", &config.socket, P_STRING,
101          NULL, NULL, NULL},
102         {"process_emails", "TRUE", &config.process_emails, P_BOOL,
103          NULL, NULL, NULL},
104         {"receive_spam", "TRUE", &config.receive_spam, P_BOOL,
105          NULL, NULL, NULL},
106         {"save_folder", NULL, &config.save_folder, P_STRING,
107          NULL, NULL, NULL},
108         {"max_size", "250", &config.max_size, P_INT,
109          NULL, NULL, NULL},
110         {"timeout", "30", &config.timeout, P_INT,
111          NULL, NULL, NULL},
112         {"username", "", &config.username, P_STRING,
113          NULL, NULL, NULL},
114         {"mark_as_read", "TRUE", &config.mark_as_read, P_BOOL,
115          NULL, NULL, NULL},
116         {"whitelist_ab", "FALSE", &config.whitelist_ab, P_BOOL,
117          NULL, NULL, NULL},
118         {"whitelist_ab_folder", N_("Any"), &config.whitelist_ab_folder, P_STRING,
119          NULL, NULL, NULL},
120         {"compress", "FALSE", &config.compress, P_BOOL,
121          NULL, NULL, NULL},
122
123         {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
124 };
125
126 gboolean timeout_func(gpointer data)
127 {
128         gint *running = (gint *) data;
129
130         if (*running & CHILD_RUNNING)
131                 return TRUE;
132
133         *running &= ~TIMEOUT_RUNNING;
134         return FALSE;
135 }
136
137 typedef enum {
138         MSG_IS_HAM = 0,
139         MSG_IS_SPAM = 1,
140         MSG_FILTERING_ERROR = 2
141 } MsgStatus;
142
143 static void update_flags(void)
144 {
145         /* set the SPAMC_USE_ZLIB flag according to config */
146         if (config.compress)
147                 flags |= SPAMC_USE_ZLIB;
148         else
149                 flags &= ~SPAMC_USE_ZLIB;
150 }
151
152 static MsgStatus msg_is_spam(FILE *fp)
153 {
154         struct transport trans;
155         struct message m;
156         gboolean is_spam = FALSE;
157
158         if (!config.enable)
159                 return MSG_IS_HAM;
160
161         update_flags();
162         transport_init(&trans);
163         switch (config.transport) {
164         case SPAMASSASSIN_TRANSPORT_LOCALHOST:
165                 trans.type = TRANSPORT_LOCALHOST;
166                 trans.port = config.port;
167                 break;
168         case SPAMASSASSIN_TRANSPORT_TCP:
169                 trans.type = TRANSPORT_TCP;
170                 trans.hostname = config.hostname;
171                 trans.port = config.port;
172                 break;
173         case SPAMASSASSIN_TRANSPORT_UNIX:
174                 trans.type = TRANSPORT_UNIX;
175                 trans.socketpath = config.socket;
176                 break;
177         default:
178                 return MSG_IS_HAM;
179         }
180
181         if (transport_setup(&trans, flags) != EX_OK) {
182                 log_error(LOG_PROTOCOL, _("SpamAssassin plugin couldn't connect to spamd.\n"));
183                 debug_print("failed to setup transport\n");
184                 return MSG_FILTERING_ERROR;
185         }
186
187         m.type = MESSAGE_NONE;
188         m.max_len = config.max_size * 1024;
189         m.timeout = config.timeout;
190
191         if (message_read(fileno(fp), flags, &m) != EX_OK) {
192                 debug_print("failed to read message\n");
193                 message_cleanup(&m);
194                 return MSG_FILTERING_ERROR;
195         }
196
197         if (message_filter(&trans, config.username, flags, &m) != EX_OK) {
198                 log_error(LOG_PROTOCOL, _("SpamAssassin plugin filtering failed.\n"));
199                 debug_print("filtering the message failed\n");
200                 message_cleanup(&m);
201                 return MSG_FILTERING_ERROR;
202         }
203
204         if (m.is_spam == EX_ISSPAM)
205                 is_spam = TRUE;
206
207         message_cleanup(&m);
208
209         return is_spam ? MSG_IS_SPAM:MSG_IS_HAM;
210 }
211
212 static gboolean mail_filtering_hook(gpointer source, gpointer data)
213 {
214         MailFilteringData *mail_filtering_data = (MailFilteringData *) source;
215         MsgInfo *msginfo = mail_filtering_data->msginfo;
216         gboolean is_spam = FALSE, error = FALSE;
217         static gboolean warned_error = FALSE;
218         FILE *fp = NULL;
219         int pid = 0;
220         int status;
221
222         /* SPAMASSASSIN_DISABLED : keep test for compatibility purpose */
223         if (!config.enable || config.transport == SPAMASSASSIN_DISABLED) {
224                 log_warning(LOG_PROTOCOL, _("SpamAssassin plugin is disabled by its preferences.\n"));
225                 return FALSE;
226         }
227         debug_print("Filtering message %d\n", msginfo->msgnum);
228         if (message_callback != NULL)
229                 message_callback(_("SpamAssassin: filtering message..."));
230
231         if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL) {
232                 debug_print("failed to open message file\n");
233                 return FALSE;
234         }
235
236         if (config.whitelist_ab) {
237                 gchar *ab_folderpath;
238                 gboolean whitelisted = FALSE;
239
240                 if (*config.whitelist_ab_folder == '\0' ||
241                         strcasecmp(config.whitelist_ab_folder, "Any") == 0) {
242                         /* match the whole addressbook */
243                         ab_folderpath = NULL;
244                 } else {
245                         /* match the specific book/folder of the addressbook */
246                         ab_folderpath = config.whitelist_ab_folder;
247                 }
248
249                 start_address_completion(ab_folderpath);
250                 if (msginfo->from && 
251                     found_in_addressbook(msginfo->from))
252                                 whitelisted = TRUE;
253                 end_address_completion();
254                 
255                 if (whitelisted) {
256                         debug_print("message is ham (whitelisted)\n");
257                         claws_fclose(fp);
258                         return FALSE;
259                 }
260         }
261         pid = fork();
262         if (pid == 0) {
263                 _exit(msg_is_spam(fp));
264         } else {
265                 gint running = 0;
266
267                 running |= CHILD_RUNNING;
268
269                 g_timeout_add(50, timeout_func, &running);
270                 running |= TIMEOUT_RUNNING;
271
272                 while(running & CHILD_RUNNING) {
273                         int ret;
274
275                         ret = waitpid(pid, &status, WNOHANG);
276                         if (ret == pid) {
277                                 if (WIFEXITED(status)) {
278                                         MsgStatus result = MSG_IS_HAM;
279                                         running &= ~CHILD_RUNNING;
280                                         result = WEXITSTATUS(status);
281                                         is_spam = (result == MSG_IS_SPAM) ? TRUE : FALSE;
282                                         error = (result == MSG_FILTERING_ERROR);
283                                 }
284                         } if (ret < 0) {
285                                 running &= ~CHILD_RUNNING;
286                         } /* ret == 0 continue */
287             
288                         g_main_context_iteration(NULL, TRUE);
289                 }
290
291                 while (running & TIMEOUT_RUNNING)
292                         g_main_context_iteration(NULL, TRUE);
293         }
294
295         claws_fclose(fp);
296
297         if (is_spam) {
298                 debug_print("message is spam\n");
299                 procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0);
300                 if (config.receive_spam) {
301                         FolderItem *save_folder = NULL;
302
303                         if ((!config.save_folder) ||
304                             (config.save_folder[0] == '\0') ||
305                             ((save_folder = folder_find_item_from_identifier(config.save_folder)) == NULL)) {
306                                 if (mail_filtering_data->account && mail_filtering_data->account->set_trash_folder) {
307                                         save_folder = folder_find_item_from_identifier(
308                                                 mail_filtering_data->account->trash_folder);
309                                         if (save_folder)
310                                                 debug_print("found trash folder from account's advanced settings\n");
311                                 }
312                                 if (save_folder == NULL && mail_filtering_data->account &&
313                                     mail_filtering_data->account->folder) {
314                                         save_folder = mail_filtering_data->account->folder->trash;
315                                         if (save_folder)
316                                                 debug_print("found trash folder from account's trash\n");
317                                 }
318                                 if (save_folder == NULL && mail_filtering_data->account &&
319                                     !mail_filtering_data->account->folder)  {
320                                         if (mail_filtering_data->account->inbox) {
321                                                 FolderItem *item = folder_find_item_from_identifier(
322                                                         mail_filtering_data->account->inbox);
323                                                 if (item && item->folder->trash) {
324                                                         save_folder = item->folder->trash;
325                                                         debug_print("found trash folder from account's inbox\n");
326                                                 }
327                                         } 
328                                         if (!save_folder && mail_filtering_data->account->local_inbox) {
329                                                 FolderItem *item = folder_find_item_from_identifier(
330                                                         mail_filtering_data->account->local_inbox);
331                                                 if (item && item->folder->trash) {
332                                                         save_folder = item->folder->trash;
333                                                         debug_print("found trash folder from account's local_inbox\n");
334                                                 }
335                                         }
336                                 }
337                                 if (save_folder == NULL) {
338                                         debug_print("using default trash folder\n");
339                                         save_folder = folder_get_default_trash();
340                                 }
341                         }
342                         if (config.mark_as_read)
343                                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
344                         procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0);
345                         msginfo->filter_op = IS_MOVE;
346                         msginfo->to_filter_folder = save_folder;
347                 } else {
348                         folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
349                 }
350
351                 return TRUE;
352         } else {
353                 debug_print("message is ham\n");
354                 procmsg_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
355         }
356         
357         if (error) {
358                 gchar *msg = _("The SpamAssassin plugin couldn't filter "
359                                            "a message. The probable cause of the error "
360                                            "is an unreachable spamd daemon. Please make "
361                                            "sure spamd is running and accessible.");
362                 if (!prefs_common_get_prefs()->no_recv_err_panel) {
363                         if (!warned_error) {
364                                 alertpanel_error("%s", msg);
365                         }
366                         warned_error = TRUE;
367                 } else {
368                         log_error(LOG_PROTOCOL, "%s\n", msg);
369                 }
370         }
371         
372         return FALSE;
373 }
374
375 SpamAssassinConfig *spamassassin_get_config(void)
376 {
377         return &config;
378 }
379
380 gchar* spamassassin_create_tmp_spamc_wrapper(gboolean spam)
381 {
382         gchar *contents;
383         gchar *fname = get_tmp_file();
384
385         if (fname != NULL) {
386                 contents = g_strdup_printf(
387                                                 "spamc -d %s -p %u -u %s -t %u -s %u %s -L %s<\"$*\";exit $?",
388                                                 config.hostname, config.port, 
389                                                 config.username, config.timeout,
390                                                 config.max_size * 1024, config.compress?"-z":"",
391                                                 spam?"spam":"ham");
392                 if (str_write_to_file(contents, fname, TRUE) < 0) {
393                         g_free(fname);
394                         fname = NULL;
395                 }
396                 g_free(contents);
397         }
398         /* returned pointer must be free'ed by caller */
399         return fname;
400 }
401
402 int spamassassin_learn(MsgInfo *msginfo, GSList *msglist, gboolean spam)
403 {
404         gchar *cmd = NULL;
405         gchar *file = NULL;
406         const gchar *shell = g_getenv("SHELL");
407         gchar *spamc_wrapper = NULL;
408
409         if (msginfo == NULL && msglist == NULL) {
410                 return -1;
411         }
412
413         if (config.transport == SPAMASSASSIN_TRANSPORT_TCP
414         &&  prefs_common_get_prefs()->work_offline
415         &&  !inc_offline_should_override(TRUE,
416                 _("Claws Mail needs network access in order "
417                   "to feed the mail to the remote learner."))) {
418                 return -1;
419         }
420
421         if (msginfo) {
422                 file = procmsg_get_message_file(msginfo);
423                 if (file == NULL) {
424                         return -1;
425                 }
426                 if (config.transport == SPAMASSASSIN_TRANSPORT_TCP) {
427                         spamc_wrapper = spamassassin_create_tmp_spamc_wrapper(spam);
428                         if (spamc_wrapper != NULL) {
429                                 cmd = g_strconcat(shell?shell:"sh", " ",
430                                                                 spamc_wrapper, " ", file, NULL);
431                         }
432                 } else {
433                         cmd = g_strdup_printf("sa-learn -u %s%s %s %s",
434                                                         config.username,
435                                                         prefs_common_get_prefs()->work_offline?" -L":"",
436                                                         spam?"--spam":"--ham", file);
437                 }
438         }
439         if (msglist) {
440                 GSList *cur = msglist;
441                 MsgInfo *info;
442
443                 if (config.transport == SPAMASSASSIN_TRANSPORT_TCP) {
444                         /* execute n-times the spamc command */
445                         for (; cur; cur = cur->next) {
446                                 info = (MsgInfo *)cur->data;
447                                 gchar *tmpcmd = NULL;
448                                 gchar *tmpfile = get_tmp_file();
449
450                                 if (spamc_wrapper == NULL) {
451                                         spamc_wrapper = spamassassin_create_tmp_spamc_wrapper(spam);
452                                 }
453
454                                 if (spamc_wrapper && tmpfile &&
455                                 copy_file(procmsg_get_message_file(info), tmpfile, TRUE) == 0) {
456                                         tmpcmd = g_strconcat(shell?shell:"sh", " ", spamc_wrapper, " ",
457                                                                                 tmpfile, NULL);
458                                         debug_print("%s\n", tmpcmd);
459                                         execute_command_line(tmpcmd, FALSE, NULL);
460                                         g_free(tmpcmd);
461                                 }
462                                 g_free(tmpfile);
463                         }
464                         g_free(spamc_wrapper);
465                         return 0;
466                 } else {
467                         cmd = g_strdup_printf("sa-learn -u %s%s %s",
468                                         config.username,
469                                         prefs_common_get_prefs()->work_offline?" -L":"",
470                                         spam?"--spam":"--ham");
471
472                         /* concatenate all message tmpfiles to the sa-learn command-line */
473                         for (; cur; cur = cur->next) {
474                                 info = (MsgInfo *)cur->data;
475                                 gchar *tmpcmd = NULL;
476                                 gchar *tmpfile = get_tmp_file();
477
478                                 if (tmpfile &&
479                                 copy_file(procmsg_get_message_file(info), tmpfile, TRUE) == 0) {                        
480                                         tmpcmd = g_strconcat(cmd, " ", tmpfile, NULL);
481                                         g_free(cmd);
482                                         cmd = tmpcmd;
483                                 }
484                                 g_free(tmpfile);
485                         }
486                 }
487         }
488         if (cmd == NULL) {
489                 return -1;
490         }
491         debug_print("%s\n", cmd);
492         /* only run sync calls to sa-learn/spamc to prevent system lockdown */
493         execute_command_line(cmd, FALSE, NULL);
494         g_free(cmd);
495         g_free(spamc_wrapper);
496
497         return 0;
498 }
499
500 void spamassassin_save_config(void)
501 {
502         PrefFile *pfile;
503         gchar *rcpath;
504
505         debug_print("Saving SpamAssassin Page\n");
506
507         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
508         pfile = prefs_write_open(rcpath);
509         g_free(rcpath);
510         if (!pfile || (prefs_set_block_label(pfile, "SpamAssassin") < 0))
511                 return;
512
513         if (prefs_write_param(param, pfile->fp) < 0) {
514                 g_warning("Failed to write SpamAssassin configuration to file");
515                 prefs_file_close_revert(pfile);
516                 return;
517         }
518         if (fprintf(pfile->fp, "\n") < 0) {
519                 FILE_OP_ERROR(rcpath, "fprintf");
520                 prefs_file_close_revert(pfile);
521         } else
522                 prefs_file_close(pfile);
523 }
524
525 gboolean spamassassin_check_username(void)
526 {
527         if (config.username == NULL || config.username[0] == '\0') {
528                 config.username = (gchar*)g_get_user_name();
529                 if (config.username == NULL) {
530                         if (hook_id != HOOK_NONE) {
531                                 spamassassin_unregister_hook();
532                         }
533                         procmsg_unregister_spam_learner(spamassassin_learn);
534                         procmsg_spam_set_folder(NULL, NULL);
535                         return FALSE;
536                 }
537         }
538         return TRUE;
539 }
540
541 void spamassassin_set_message_callback(MessageCallback callback)
542 {
543         message_callback = callback;
544 }
545
546 gint plugin_init(gchar **error)
547 {
548         gchar *rcpath;
549
550         hook_id = HOOK_NONE;
551
552         if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
553                                 VERSION_NUMERIC, PLUGIN_NAME, error))
554                 return -1;
555
556         prefs_set_default(param);
557         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
558         prefs_read_config(param, "SpamAssassin", rcpath, NULL);
559         g_free(rcpath);
560         if (!spamassassin_check_username()) {
561                 *error = g_strdup(_("Failed to get username"));
562                 return -1;
563         }
564 #ifdef G_OS_WIN32
565         /* no Unix socket in Windows, and in case our config comes from Unix, switch to TCP */
566         if (config.transport == SPAMASSASSIN_TRANSPORT_UNIX)
567                 config.transport = SPAMASSASSIN_TRANSPORT_TCP;
568 #endif
569         spamassassin_gtk_init();
570                 
571         debug_print("SpamAssassin plugin loaded\n");
572
573         if (config.process_emails) {
574                 spamassassin_register_hook();
575         }
576
577         if (!config.enable || config.transport == SPAMASSASSIN_DISABLED) {
578                 log_warning(LOG_PROTOCOL, _("SpamAssassin plugin is loaded but disabled by its preferences.\n"));
579         }
580         else {
581                 if (config.transport == SPAMASSASSIN_TRANSPORT_TCP)
582                         debug_print("Enabling learner with a remote spamassassin server requires spamc/spamd 3.1.x\n");
583                 procmsg_register_spam_learner(spamassassin_learn);
584                 procmsg_spam_set_folder(config.save_folder, spamassassin_get_spam_folder);
585         }
586
587         return 0;
588         
589 }
590
591 gboolean plugin_done(void)
592 {
593         if (hook_id != HOOK_NONE) {
594                 spamassassin_unregister_hook();
595         }
596         g_free(config.hostname);
597         g_free(config.save_folder);
598         spamassassin_gtk_done();
599         procmsg_unregister_spam_learner(spamassassin_learn);
600         procmsg_spam_set_folder(NULL, NULL);
601         debug_print("SpamAssassin plugin unloaded\n");
602         return TRUE;
603 }
604
605 const gchar *plugin_name(void)
606 {
607         return PLUGIN_NAME;
608 }
609
610 const gchar *plugin_desc(void)
611 {
612         return _("This plugin can check all messages that are received from an "
613                          "IMAP, LOCAL or POP account for spam using a SpamAssassin "
614                          "server. You will need a SpamAssassin Server (spamd) running "
615                          "somewhere.\n"
616                          "\n"
617                          "It can also be used for marking messages as Ham or Spam.\n"
618                          "\n"
619                          "When a message is identified as spam it can be deleted or "
620                          "saved in a specially designated folder.\n"
621                          "\n"
622                          "Options can be found in /Configuration/Preferences/Plugins/SpamAssassin");
623 }
624
625 const gchar *plugin_type(void)
626 {
627         return "GTK2";
628 }
629
630 const gchar *plugin_licence(void)
631 {
632         return "GPL3+";
633 }
634
635 const gchar *plugin_version(void)
636 {
637         return VERSION;
638 }
639
640 struct PluginFeature *plugin_provides(void)
641 {
642         static struct PluginFeature features[] = 
643                 { {PLUGIN_FILTERING, N_("Spam detection")},
644                   {PLUGIN_FILTERING, N_("Spam learning")},
645                   {PLUGIN_NOTHING, NULL}};
646         return features;
647 }
648
649 void spamassassin_register_hook(void)
650 {
651         if (hook_id == HOOK_NONE)
652                 hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST, mail_filtering_hook, NULL);
653         if (hook_id == HOOK_NONE) {
654                 g_warning("Failed to register mail filtering hook");
655                 config.process_emails = FALSE;
656         }
657 }
658
659 void spamassassin_unregister_hook(void)
660 {
661         if (hook_id != HOOK_NONE) {
662                 hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id);
663         }
664         hook_id = HOOK_NONE;
665 }
666
667 FolderItem *spamassassin_get_spam_folder(MsgInfo *msginfo)
668 {
669         FolderItem *item = folder_find_item_from_identifier(config.save_folder);
670
671         if (item || msginfo == NULL || msginfo->folder == NULL)
672                 return item;
673
674         if (msginfo->folder->folder &&
675             msginfo->folder->folder->account && 
676             msginfo->folder->folder->account->set_trash_folder) {
677                 item = folder_find_item_from_identifier(
678                         msginfo->folder->folder->account->trash_folder);
679         }
680
681         if (item == NULL && 
682             msginfo->folder->folder &&
683             msginfo->folder->folder->trash)
684                 item = msginfo->folder->folder->trash;
685                 
686         if (item == NULL)
687                 item = folder_get_default_trash();
688                 
689         debug_print("SA spam dir: %s\n", folder_item_get_path(item));
690         return item;
691 }