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