New hooklist to collect avatar data from headers
[claws.git] / src / procmsg.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and 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 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27
28 #include "main.h"
29 #include "utils.h"
30 #include "procmsg.h"
31 #include "procheader.h"
32 #include "send_message.h"
33 #include "procmime.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
37 #include "folder.h"
38 #include "prefs_common.h"
39 #include "account.h"
40 #include "alertpanel.h"
41 #include "news.h"
42 #include "hooks.h"
43 #include "msgcache.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
47 #include "log.h"
48 #include "tags.h"
49 #include "timing.h"
50 #include "inc.h"
51 #include "privacy.h"
52
53 extern SessionStats session_stats;
54
55 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
56                                             FolderItem *queue, gint msgnum, gboolean *queued_removed);
57 static void procmsg_update_unread_children      (MsgInfo        *info,
58                                          gboolean        newly_marked);
59 enum
60 {
61         Q_SENDER           = 0,
62         Q_SMTPSERVER       = 1,
63         Q_RECIPIENTS       = 2,
64         Q_NEWSGROUPS       = 3,
65         Q_MAIL_ACCOUNT_ID  = 4,
66         Q_NEWS_ACCOUNT_ID  = 5,
67         Q_SAVE_COPY_FOLDER = 6,
68         Q_REPLY_MESSAGE_ID = 7,
69         Q_FWD_MESSAGE_ID   = 8,
70         Q_PRIVACY_SYSTEM   = 9,
71         Q_ENCRYPT          = 10,
72         Q_ENCRYPT_DATA     = 11,
73         Q_CLAWS_HDRS       = 12,
74         Q_PRIVACY_SYSTEM_OLD = 13,
75         Q_ENCRYPT_OLD        = 14,
76         Q_ENCRYPT_DATA_OLD   = 15,
77         Q_CLAWS_HDRS_OLD     = 16,
78 };
79
80 void procmsg_msg_list_free(GSList *mlist)
81 {
82         GSList *cur;
83         MsgInfo *msginfo;
84
85         for (cur = mlist; cur != NULL; cur = cur->next) {
86                 msginfo = (MsgInfo *)cur->data;
87                 procmsg_msginfo_free(msginfo);
88         }
89         g_slist_free(mlist);
90 }
91
92 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
93 {
94         GSList *cur = NULL;
95         GSList *nums = NULL;
96
97         for (cur = msglist; cur; cur = cur->next) {
98                 MsgInfo *msg = (MsgInfo *)cur->data;
99                 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
100         }
101
102         return g_slist_reverse(nums);
103 }
104
105 struct MarkSum {
106         gint *new_msgs;
107         gint *unread_msgs;
108         gint *total_msgs;
109         gint *min;
110         gint *max;
111         gint first;
112 };
113
114 /* CLAWS subject threading:
115   
116   in the first round it inserts subject lines in a 
117   hashtable (subject <-> node)
118
119   the second round finishes the threads by attaching
120   matching subject lines to the one found in the
121   hashtable. will use the oldest node with the same
122   subject that is not more then thread_by_subject_max_age
123   days old (see subject_hashtable_lookup)
124 */  
125
126 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
127 {
128         gchar *subject;
129         MsgInfo *msginfo;
130         GSList *list = NULL;
131
132         cm_return_if_fail(hashtable != NULL);
133         cm_return_if_fail(node != NULL);
134         msginfo = (MsgInfo *) node->data;
135         cm_return_if_fail(msginfo != NULL);
136
137         subject = msginfo->subject;
138         if (subject == NULL)
139                 return;
140
141         subject += subject_get_prefix_length(subject);
142
143         list = g_hash_table_lookup(hashtable, subject);
144         list = g_slist_prepend(list, node);
145         g_hash_table_insert(hashtable, subject, list);
146 }
147
148 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
149 {
150         gchar *subject;
151         GSList *list, *cur;
152         GNode *node = NULL, *hashtable_node = NULL;
153         gint prefix_length;
154         MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
155         gboolean match;
156     
157         cm_return_val_if_fail(hashtable != NULL, NULL);
158
159         subject = msginfo->subject;
160         if (subject == NULL)
161                 return NULL;
162         prefix_length = subject_get_prefix_length(subject);
163         if (prefix_length <= 0)
164                 return NULL;
165         subject += prefix_length;
166         
167         list = g_hash_table_lookup(hashtable, subject);
168         if (list == NULL)
169                 return NULL;
170
171         /* check all nodes with the same subject to find the best parent */
172         for (cur = list; cur; cur = cur->next) {
173                 hashtable_node = (GNode *)cur->data;
174                 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
175                 match = FALSE;
176
177                 /* best node should be the oldest in the found nodes */
178                 /* parent node must not be older then msginfo */
179                 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
180                     ((best_msginfo == NULL) ||
181                      (best_msginfo->date_t > hashtable_msginfo->date_t)))
182                         match = TRUE;
183
184                 /* parent node must not be more then thread_by_subject_max_age
185                    days older then msginfo */
186                 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
187                     prefs_common.thread_by_subject_max_age * 3600 * 24)
188                         match = FALSE;
189
190                 /* can add new tests for all matching
191                    nodes found by subject */
192
193                 if (match) {
194                         node = hashtable_node;
195                         best_msginfo = hashtable_msginfo;
196                 }
197         }
198
199         return node;
200 }
201
202 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
203 {
204         g_slist_free(value);
205 }
206
207 /* return the reversed thread tree */
208 GNode *procmsg_get_thread_tree(GSList *mlist)
209 {
210         GNode *root, *parent, *node, *next;
211         GHashTable *msgid_table;
212         GHashTable *subject_hashtable = NULL;
213         MsgInfo *msginfo;
214         const gchar *msgid;
215         GSList *reflist;
216         START_TIMING("");
217         root = g_node_new(NULL);
218         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
219         
220         if (prefs_common.thread_by_subject) {
221                 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
222         }
223
224         for (; mlist != NULL; mlist = mlist->next) {
225                 msginfo = (MsgInfo *)mlist->data;
226                 parent = root;
227
228                 if (msginfo->inreplyto) {
229                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
230                         if (parent == NULL) {
231                                 parent = root;
232                         }
233                 }
234                 node = g_node_insert_data_before
235                         (parent, parent == root ? parent->children : NULL,
236                          msginfo);
237                 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
238                         g_hash_table_insert(msgid_table, (gchar *)msgid, node);
239
240                 /* CLAWS: add subject to hashtable (without prefix) */
241                 if (prefs_common.thread_by_subject) {
242                         subject_hashtable_insert(subject_hashtable, node);
243                 }
244         }
245
246         /* complete the unfinished threads */
247         for (node = root->children; node != NULL; ) {
248                 next = node->next;
249                 msginfo = (MsgInfo *)node->data;
250                 parent = NULL;
251                 
252                 if (msginfo->inreplyto)
253                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
254
255                 /* try looking for the indirect parent */
256                 if (!parent && msginfo->references) {
257                         for (reflist = msginfo->references;
258                              reflist != NULL; reflist = reflist->next)
259                                 if ((parent = g_hash_table_lookup
260                                         (msgid_table, reflist->data)) != NULL)
261                                         break;
262                 }                                        
263               
264                 /* node should not be the parent, and node should not
265                    be an ancestor of parent (circular reference) */
266                 if (parent && parent != node &&
267                     !g_node_is_ancestor(node, parent)) {
268                         g_node_unlink(node);
269                         g_node_insert_before
270                                 (parent, parent->children, node);
271                 }
272                
273                 node = next;
274         }
275
276         if (prefs_common.thread_by_subject) {
277                 START_TIMING("thread by subject");
278                 for (node = root->children; node && node != NULL;) {
279                         next = node->next;
280                         msginfo = (MsgInfo *) node->data;
281                         
282                         parent = subject_hashtable_lookup(subject_hashtable, msginfo);
283                         
284                         /* the node may already be threaded by IN-REPLY-TO, so go up 
285                          * in the tree to 
286                            find the parent node */
287                         if (parent != NULL) {
288                                 if (g_node_is_ancestor(node, parent))
289                                         parent = NULL;
290                                 if (parent == node)
291                                         parent = NULL;
292                         }
293                         
294                         if (parent) {
295                                 g_node_unlink(node);
296                                 g_node_append(parent, node);
297                         }
298
299                         node = next;
300                 }       
301                 END_TIMING();
302         }
303         
304         if (prefs_common.thread_by_subject)
305         {
306                 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
307                 g_hash_table_destroy(subject_hashtable);
308         }
309
310         g_hash_table_destroy(msgid_table);
311         END_TIMING();
312         return root;
313 }
314
315 gint procmsg_move_messages(GSList *mlist)
316 {
317         GSList *cur, *movelist = NULL;
318         MsgInfo *msginfo;
319         FolderItem *dest = NULL;
320         gint retval = 0;
321         gboolean finished = TRUE;
322         if (!mlist) return 0;
323
324         folder_item_update_freeze();
325
326 next_folder:
327         for (cur = mlist; cur != NULL; cur = cur->next) {
328                 msginfo = (MsgInfo *)cur->data;
329                 if (!msginfo->to_folder) {
330                         continue;
331                 } else {
332                         finished = FALSE;
333                 }
334                 if (!dest) {
335                         dest = msginfo->to_folder;
336                         movelist = g_slist_prepend(movelist, msginfo);
337                 } else if (dest == msginfo->to_folder) {
338                         movelist = g_slist_prepend(movelist, msginfo);
339                 } else {
340                         continue;
341                 }
342                 procmsg_msginfo_set_to_folder(msginfo, NULL);
343         }
344         if (movelist) {
345                 movelist = g_slist_reverse(movelist);
346                 retval |= folder_item_move_msgs(dest, movelist);
347                 g_slist_free(movelist);
348                 movelist = NULL;
349         }
350         if (finished == FALSE) {
351                 finished = TRUE;
352                 dest = NULL;
353                 goto next_folder;
354         }
355
356         folder_item_update_thaw();
357         return retval;
358 }
359
360 void procmsg_copy_messages(GSList *mlist)
361 {
362         GSList *cur, *copylist = NULL;
363         MsgInfo *msginfo;
364         FolderItem *dest = NULL;
365         gboolean finished = TRUE;
366         if (!mlist) return;
367
368         folder_item_update_freeze();
369
370 next_folder:
371         for (cur = mlist; cur != NULL; cur = cur->next) {
372                 msginfo = (MsgInfo *)cur->data;
373                 if (!msginfo->to_folder) {
374                         continue;
375                 } else {
376                         finished = FALSE;
377                 }
378                 if (!dest) {
379                         dest = msginfo->to_folder;
380                         copylist = g_slist_prepend(copylist, msginfo);
381                 } else if (dest == msginfo->to_folder) {
382                         copylist = g_slist_prepend(copylist, msginfo);
383                 } else {
384                         continue;
385                 }
386                 procmsg_msginfo_set_to_folder(msginfo, NULL);
387         }
388         if (copylist) {
389                 copylist = g_slist_reverse(copylist);
390                 folder_item_copy_msgs(dest, copylist);
391                 g_slist_free(copylist);
392                 copylist = NULL;
393         }
394         if (finished == FALSE) {
395                 finished = TRUE;
396                 dest = NULL;
397                 goto next_folder;
398         }
399
400         folder_item_update_thaw();
401 }
402
403 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
404 {
405         gchar *file;
406
407         cm_return_val_if_fail(msginfo != NULL, NULL);
408
409         if (msginfo->plaintext_file)
410                 file = g_strdup(msginfo->plaintext_file);
411         else {
412                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
413         }
414
415         return file;
416 }
417
418 gchar *procmsg_get_message_file(MsgInfo *msginfo)
419 {
420         gchar *filename = NULL;
421
422         cm_return_val_if_fail(msginfo != NULL, NULL);
423
424         filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
425         if (!filename)
426                 debug_print("can't fetch message %d\n", msginfo->msgnum);
427
428         return filename;
429 }
430
431 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
432 {
433         gchar *filename = NULL;
434
435         cm_return_val_if_fail(msginfo != NULL, NULL);
436
437         filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
438                                                 headers, body);
439         if (!filename)
440                 debug_print("can't fetch message %d\n", msginfo->msgnum);
441
442         return filename;
443 }
444
445 GSList *procmsg_get_message_file_list(GSList *mlist)
446 {
447         GSList *file_list = NULL;
448         MsgInfo *msginfo;
449         MsgFileInfo *fileinfo;
450         gchar *file;
451
452         while (mlist != NULL) {
453                 msginfo = (MsgInfo *)mlist->data;
454                 file = procmsg_get_message_file(msginfo);
455                 if (!file) {
456                         procmsg_message_file_list_free(file_list);
457                         return NULL;
458                 }
459                 fileinfo = g_new(MsgFileInfo, 1);
460                 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
461                 fileinfo->file = file;
462                 fileinfo->flags = g_new(MsgFlags, 1);
463                 *fileinfo->flags = msginfo->flags;
464                 file_list = g_slist_prepend(file_list, fileinfo);
465                 mlist = mlist->next;
466         }
467
468         file_list = g_slist_reverse(file_list);
469
470         return file_list;
471 }
472
473 void procmsg_message_file_list_free(MsgInfoList *file_list)
474 {
475         GSList *cur;
476         MsgFileInfo *fileinfo;
477
478         for (cur = file_list; cur != NULL; cur = cur->next) {
479                 fileinfo = (MsgFileInfo *)cur->data;
480                 procmsg_msginfo_free(fileinfo->msginfo);
481                 g_free(fileinfo->file);
482                 g_free(fileinfo->flags);
483                 g_free(fileinfo);
484         }
485
486         g_slist_free(file_list);
487 }
488
489 FILE *procmsg_open_message(MsgInfo *msginfo)
490 {
491         FILE *fp;
492         gchar *file;
493
494         cm_return_val_if_fail(msginfo != NULL, NULL);
495         
496         file = procmsg_get_message_file_path(msginfo);
497         cm_return_val_if_fail(file != NULL, NULL);
498
499         if (!is_file_exist(file)) {
500                 g_free(file);
501                 file = procmsg_get_message_file(msginfo);
502                 if (!file)
503                         return NULL;
504         }
505
506         if ((fp = g_fopen(file, "rb")) == NULL) {
507                 FILE_OP_ERROR(file, "fopen");
508                 g_free(file);
509                 return NULL;
510         }
511
512         g_free(file);
513
514         if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
515                 gchar buf[BUFFSIZE];
516
517                 while (fgets(buf, sizeof(buf), fp) != NULL) {
518                         /* new way */
519                         if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
520                                 strlen("X-Claws-End-Special-Headers:"))) ||
521                             (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
522                                 strlen("X-Sylpheed-End-Special-Headers:"))))
523                                 break;
524                         /* old way */
525                         if (buf[0] == '\r' || buf[0] == '\n') break;
526                         /* from other mailers */
527                         if (!strncmp(buf, "Date: ", 6)
528                         ||  !strncmp(buf, "To: ", 4)
529                         ||  !strncmp(buf, "From: ", 6)
530                         ||  !strncmp(buf, "Subject: ", 9)) {
531                                 rewind(fp);
532                                 break;
533                         }
534                 }
535         }
536
537         return fp;
538 }
539
540 gboolean procmsg_msg_exist(MsgInfo *msginfo)
541 {
542         gchar *path;
543         gboolean ret;
544
545         if (!msginfo) return FALSE;
546
547         path = folder_item_get_path(msginfo->folder);
548         change_dir(path);
549         ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
550         g_free(path);
551
552         return ret;
553 }
554
555 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
556                                 PrefsFilterType type)
557 {
558         static HeaderEntry hentry[] = {{"X-BeenThere:",    NULL, TRUE},
559                                        {"X-ML-Name:",      NULL, TRUE},
560                                        {"X-List:",         NULL, TRUE},
561                                        {"X-Mailing-list:", NULL, TRUE},
562                                        {"List-Id:",        NULL, TRUE},
563                                        {"X-Sequence:",     NULL, TRUE},
564                                        {"Sender:",         NULL, TRUE},
565                                        {"List-Post:",      NULL, TRUE},
566                                        {NULL,              NULL, FALSE}};
567         enum
568         {
569                 H_X_BEENTHERE    = 0,
570                 H_X_ML_NAME      = 1,
571                 H_X_LIST         = 2,
572                 H_X_MAILING_LIST = 3,
573                 H_LIST_ID        = 4,
574                 H_X_SEQUENCE     = 5,
575                 H_SENDER         = 6,
576                 H_LIST_POST      = 7
577         };
578
579         FILE *fp;
580
581         cm_return_if_fail(msginfo != NULL);
582         cm_return_if_fail(header != NULL);
583         cm_return_if_fail(key != NULL);
584
585         *header = NULL;
586         *key = NULL;
587
588         switch (type) {
589         case FILTER_BY_NONE:
590                 return;
591         case FILTER_BY_AUTO:
592                 if ((fp = procmsg_open_message(msginfo)) == NULL)
593                         return;
594                 procheader_get_header_fields(fp, hentry);
595                 fclose(fp);
596
597 #define SET_FILTER_KEY(hstr, idx)       \
598 {                                       \
599         *header = g_strdup(hstr);       \
600         *key = hentry[idx].body;        \
601         hentry[idx].body = NULL;        \
602 }
603
604                 if (hentry[H_LIST_ID].body != NULL) {
605                         SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
606                         extract_list_id_str(*key);
607                 } else if (hentry[H_X_BEENTHERE].body != NULL) {
608                         SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
609                 } else if (hentry[H_X_ML_NAME].body != NULL) {
610                         SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
611                 } else if (hentry[H_X_LIST].body != NULL) {
612                         SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
613                 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
614                         SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
615                 } else  if (hentry[H_X_SEQUENCE].body != NULL) {
616                         gchar *p;
617
618                         SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
619                         p = *key;
620                         while (*p != '\0') {
621                                 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
622                                 while (g_ascii_isspace(*p)) p++;
623                                 if (g_ascii_isdigit(*p)) {
624                                         *p = '\0';
625                                         break;
626                                 }
627                         }
628                         g_strstrip(*key);
629                 } else if (hentry[H_SENDER].body != NULL) {
630                         SET_FILTER_KEY("header \"Sender\"", H_SENDER);
631                 } else if (hentry[H_LIST_POST].body != NULL) {
632                         SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
633                 } else if (msginfo->to) {
634                         *header = g_strdup("to");
635                         *key = g_strdup(msginfo->to);
636                 } else if (msginfo->subject) {
637                         *header = g_strdup("subject");
638                         *key = g_strdup(msginfo->subject);
639                 }
640
641 #undef SET_FILTER_KEY
642
643                 g_free(hentry[H_X_BEENTHERE].body);
644                 hentry[H_X_BEENTHERE].body = NULL;
645                 g_free(hentry[H_X_ML_NAME].body);
646                 hentry[H_X_ML_NAME].body = NULL;
647                 g_free(hentry[H_X_LIST].body);
648                 hentry[H_X_LIST].body = NULL;
649                 g_free(hentry[H_X_MAILING_LIST].body);
650                 hentry[H_X_MAILING_LIST].body = NULL;
651                 g_free(hentry[H_LIST_ID].body);
652                 hentry[H_LIST_ID].body = NULL;
653                 g_free(hentry[H_SENDER].body);
654                 hentry[H_SENDER].body = NULL;
655                 g_free(hentry[H_LIST_POST].body);
656                 hentry[H_LIST_POST].body = NULL;
657
658                 break;
659         case FILTER_BY_FROM:
660                 *header = g_strdup("from");
661                 *key = g_strdup(msginfo->from);
662                 break;
663         case FILTER_BY_TO:
664                 *header = g_strdup("to");
665                 *key = g_strdup(msginfo->to);
666                 break;
667         case FILTER_BY_SUBJECT:
668                 *header = g_strdup("subject");
669                 *key = g_strdup(msginfo->subject);
670                 break;
671         default:
672                 break;
673         }
674 }
675
676 static void procmsg_empty_trash(FolderItem *trash)
677 {
678         GNode *node, *next;
679
680         if (!trash || 
681             (trash->stype != F_TRASH && 
682              !folder_has_parent_of_type(trash, F_TRASH)))
683                 return;
684
685         if (trash && trash->total_msgs > 0) {
686                 GSList *mlist = folder_item_get_msg_list(trash);
687                 GSList *cur;
688                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
689                         MsgInfo * msginfo = (MsgInfo *) cur->data;
690                         if (MSG_IS_LOCKED(msginfo->flags)) {
691                                 procmsg_msginfo_free(msginfo);
692                                 continue;
693                         }
694                         if (msginfo->total_size != 0 && 
695                             msginfo->size != (off_t)msginfo->total_size)
696                                 partial_mark_for_delete(msginfo);
697
698                         procmsg_msginfo_free(msginfo);
699                 }
700                 g_slist_free(mlist);
701                 folder_item_remove_all_msg(trash);
702         }
703
704         if (!trash->node || !trash->node->children)
705                 return;
706
707         node = trash->node->children;
708         while (node != NULL) {
709                 next = node->next;
710                 procmsg_empty_trash(FOLDER_ITEM(node->data));
711                 node = next;
712         }
713 }
714
715 void procmsg_empty_all_trash(void)
716 {
717         FolderItem *trash;
718         GList *cur;
719
720         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
721                 Folder *folder = FOLDER(cur->data);
722                 trash = folder->trash;
723                 procmsg_empty_trash(trash);
724                 if (folder->account && folder->account->set_trash_folder && 
725                     folder_find_item_from_identifier(folder->account->trash_folder))
726                         procmsg_empty_trash(
727                                 folder_find_item_from_identifier(folder->account->trash_folder));
728         }
729 }
730
731 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
732 {
733         PrefsAccount *mailac = NULL;
734         FILE *fp;
735         int hnum;
736         gchar buf[BUFFSIZE];
737         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
738                                        {"SSV:",  NULL, FALSE},
739                                        {"R:",    NULL, FALSE},
740                                        {"NG:",   NULL, FALSE},
741                                        {"MAID:", NULL, FALSE},
742                                        {"NAID:", NULL, FALSE},
743                                        {"SCF:",  NULL, FALSE},
744                                        {"RMID:", NULL, FALSE},
745                                        {"FMID:", NULL, FALSE},
746                                        {"X-Claws-Privacy-System:", NULL, FALSE},
747                                        {"X-Claws-Encrypt:", NULL, FALSE},
748                                        {"X-Claws-Encrypt-Data:", NULL, FALSE},
749                                        {"X-Claws-End-Special-Headers",    NULL, FALSE},
750                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
751                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
752                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
753                                        {NULL,    NULL, FALSE}};
754         
755         cm_return_val_if_fail(file != NULL, NULL);
756
757         if ((fp = g_fopen(file, "rb")) == NULL) {
758                 FILE_OP_ERROR(file, "fopen");
759                 return NULL;
760         }
761
762         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
763                != -1) {
764                 gchar *p = buf + strlen(qentry[hnum].name);
765
766                 if (hnum == Q_MAIL_ACCOUNT_ID) {
767                         mailac = account_find_from_id(atoi(p));
768                         break;
769                 }
770         }
771         fclose(fp);
772         return mailac;
773 }
774
775 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
776 {
777         GSList *mia;
778
779         if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
780                 return NULL;
781
782         for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
783                 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
784                 if (avatar->avatar_id == type)
785                         return avatar->avatar_src;
786         }
787
788         return NULL;
789 }
790
791 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
792 {
793         MsgInfoAvatar *av;
794
795         if (!msginfo->extradata)
796                 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
797
798         av = g_new0(MsgInfoAvatar, 1);
799         av->avatar_id = type;
800         av->avatar_src = g_strdup(data);
801
802         msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
803 }
804
805 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
806 {
807         gchar *folder_id;
808         const gchar *msgid;
809         gchar *id;
810
811         cm_return_val_if_fail(msginfo != NULL, NULL);
812         folder_id = folder_item_get_identifier(msginfo->folder);
813         msgid = msginfo->msgid;
814
815         id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
816
817         g_free(folder_id);
818
819         return id;
820 }
821
822 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
823 {
824         gchar *folder_id = g_strdup(id);
825         gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
826         const gchar *msgid;
827         FolderItem *item;
828         MsgInfo *msginfo;
829
830         if (separator == NULL) {
831                 g_free(folder_id);
832                 return NULL;
833         }
834
835         *separator = '\0';
836         msgid = separator + 1;
837
838         item = folder_find_item_from_identifier(folder_id);
839
840         if (item == NULL) {
841                 g_free(folder_id);
842                 return NULL;
843         }
844
845         msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
846         g_free(folder_id);
847
848         return msginfo;
849 }
850
851 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
852 {
853         GSList *result = NULL;
854         GSList *orig = NULL;
855         PrefsAccount *last_account = NULL;
856         MsgInfo *msg = NULL;
857         GSList *cur = NULL;
858         gboolean nothing_to_sort = TRUE;
859
860         if (!list)
861                 return NULL;
862
863         orig = g_slist_copy(list);
864         
865         msg = (MsgInfo *)orig->data;
866         
867         for (cur = orig; cur; cur = cur->next)
868                 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
869         
870         debug_print("\n");
871
872 parse_again:    
873         nothing_to_sort = TRUE;
874         cur = orig;
875         while (cur) {
876                 gchar *file = NULL;
877                 PrefsAccount *ac = NULL;
878                 msg = (MsgInfo *)cur->data;
879                 file = folder_item_fetch_msg(queue, msg->msgnum);
880                 ac = procmsg_get_account_from_file(file);
881                 g_free(file);
882
883                 if (last_account == NULL || (ac != NULL && ac == last_account)) {
884                         result = g_slist_append(result, msg);
885                         orig = g_slist_remove(orig, msg);
886                         last_account = ac;
887                         nothing_to_sort = FALSE;
888                         goto parse_again;
889                 }
890                 cur = cur->next;
891         }
892         
893         if (orig || g_slist_length(orig)) {
894                 if (!last_account && nothing_to_sort) {
895                         /* can't find an account for the rest of the list */
896                         cur = orig;
897                         while (cur) {
898                                 result = g_slist_append(result, cur->data);
899                                 cur = cur->next;
900                         }
901                 } else {
902                         last_account = NULL;
903                         goto parse_again;
904                 }
905         }
906         
907         g_slist_free(orig);
908         
909         for (cur = result; cur; cur = cur->next)
910                 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
911
912         debug_print("\n");
913
914         return result;
915 }
916
917 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
918 {
919         gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
920         PrefsAccount *ac = procmsg_get_account_from_file(file);
921         GSList *cur;
922         g_free(file);
923         for (cur = elem; cur; cur = cur->next) {
924                 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
925                 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
926                 
927                 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
928                         if (procmsg_get_account_from_file(file) == ac) {
929                                 g_free(file);
930                                 return FALSE;
931                         }
932                 }
933                 
934                 g_free(file);
935         }
936         return TRUE;
937 }
938
939 static gboolean send_queue_lock = FALSE;
940
941 gboolean procmsg_queue_lock(char **errstr)
942 {
943         if (send_queue_lock) {
944                 /* Avoid having to translate two similar strings */
945                 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
946                 if (errstr) {
947                         if (*errstr) g_free(*errstr);
948                         *errstr = g_strdup_printf(_("Already trying to send."));
949                 }
950                 return FALSE;
951         }
952         send_queue_lock = TRUE;
953         return TRUE;
954 }
955 void procmsg_queue_unlock(void)
956 {
957         send_queue_lock = FALSE;
958 }
959 /*!
960  *\brief        Send messages in queue
961  *
962  *\param        queue Queue folder to process
963  *\param        save_msgs Unused
964  *
965  *\return       Number of messages sent, negative if an error occurred
966  *              positive if no error occurred
967  */
968 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
969 {
970         gint sent = 0, err = 0;
971         GSList *list, *elem;
972         GSList *sorted_list = NULL;
973         GNode *node, *next;
974         
975         if (!procmsg_queue_lock(errstr)) {
976                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
977                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
978                 return -1;
979         }
980         inc_lock();
981         if (!queue)
982                 queue = folder_get_default_queue();
983         
984         if (queue == NULL) {
985                 procmsg_queue_unlock();
986                 inc_unlock();
987                 return -1;
988         }
989
990         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
991         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
992
993         folder_item_scan(queue);
994         list = folder_item_get_msg_list(queue);
995
996         /* sort the list per sender account; this helps reusing the same SMTP server */
997         sorted_list = procmsg_list_sort_by_account(queue, list);
998         
999         for (elem = sorted_list; elem != NULL; elem = elem->next) {
1000                 gchar *file;
1001                 MsgInfo *msginfo;
1002                         
1003                 msginfo = (MsgInfo *)(elem->data);
1004                 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
1005                         file = folder_item_fetch_msg(queue, msginfo->msgnum);
1006                         if (file) {
1007                                 gboolean queued_removed = FALSE;
1008                                 if (procmsg_send_message_queue_full(file, 
1009                                                 !procmsg_is_last_for_account(queue, msginfo, elem),
1010                                                 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1011                                         g_warning("Sending queued message %d failed.\n", 
1012                                                   msginfo->msgnum);
1013                                         err++;
1014                                 } else {
1015                                         sent++; 
1016                                         if (!queued_removed)
1017                                                 folder_item_remove_msg(queue, msginfo->msgnum);
1018                                 }
1019                                 g_free(file);
1020                         }
1021                 }
1022                 /* FIXME: supposedly if only one message is locked, and queue
1023                  * is being flushed, the following free says something like 
1024                  * "freeing msg ## in folder (nil)". */
1025                 procmsg_msginfo_free(msginfo);
1026         }
1027
1028         g_slist_free(sorted_list);
1029         folder_item_scan(queue);
1030
1031         if (queue->node && queue->node->children) {
1032                 node = queue->node->children;
1033                 while (node != NULL) {
1034                         int res = 0;
1035                         next = node->next;
1036                         send_queue_lock = FALSE;
1037                         res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1038                         send_queue_lock = TRUE;
1039                         if (res < 0) 
1040                                 err = -res;
1041                         else
1042                                 sent += res;
1043                         node = next;
1044                 }
1045         }
1046         procmsg_queue_unlock();
1047         inc_unlock();
1048         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1049         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1050
1051         return (err != 0 ? -err : sent);
1052 }
1053
1054 gboolean procmsg_is_sending(void)
1055 {
1056         return send_queue_lock;
1057 }
1058
1059 /*!
1060  *\brief        Determine if a queue folder is empty
1061  *
1062  *\param        queue Queue folder to process
1063  *
1064  *\return       TRUE if the queue folder is empty, otherwise return FALSE
1065  */
1066 gboolean procmsg_queue_is_empty(FolderItem *queue)
1067 {
1068         GSList *list;
1069         gboolean res = FALSE;
1070         if (!queue)
1071                 queue = folder_get_default_queue();
1072         cm_return_val_if_fail(queue != NULL, TRUE);
1073
1074         folder_item_scan(queue);
1075         list = folder_item_get_msg_list(queue);
1076         res = (list == NULL);
1077         procmsg_msg_list_free(list);
1078
1079         if (res == TRUE) {
1080                 GNode *node, *next;
1081                 if (queue->node && queue->node->children) {
1082                         node = queue->node->children;
1083                         while (node != NULL) {
1084                                 next = node->next;
1085                                 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1086                                         return FALSE;
1087                                 node = next;
1088                         }
1089                 }
1090         }
1091         return res;
1092 }
1093
1094 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1095 {
1096         FILE *fp, *outfp;
1097         gchar buf[BUFFSIZE];
1098         
1099         if ((fp = g_fopen(in, "rb")) == NULL) {
1100                 FILE_OP_ERROR(in, "fopen");
1101                 return -1;
1102         }
1103         if ((outfp = g_fopen(out, "wb")) == NULL) {
1104                 FILE_OP_ERROR(out, "fopen");
1105                 fclose(fp);
1106                 return -1;
1107         }
1108         while (fgets(buf, sizeof(buf), fp) != NULL) {
1109                 /* new way */
1110                 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1111                         strlen("X-Claws-End-Special-Headers:"))) ||
1112                     (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1113                         strlen("X-Sylpheed-End-Special-Headers:"))))
1114                         break;
1115                 /* old way */
1116                 if (buf[0] == '\r' || buf[0] == '\n') break;
1117                 /* from other mailers */
1118                 if (!strncmp(buf, "Date: ", 6)
1119                 ||  !strncmp(buf, "To: ", 4)
1120                 ||  !strncmp(buf, "From: ", 6)
1121                 ||  !strncmp(buf, "Subject: ", 9)) {
1122                         rewind(fp);
1123                         break;
1124                 }
1125         }
1126         while (fgets(buf, sizeof(buf), fp) != NULL)
1127                 fputs(buf, outfp);
1128         fclose(outfp);
1129         fclose(fp);
1130         return 0;
1131 }
1132
1133 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1134                             gboolean is_queued)
1135 {
1136         gint num;
1137         MsgInfo *msginfo, *tmp_msginfo;
1138         MsgFlags flag = {0, 0};
1139
1140         debug_print("saving sent message...\n");
1141
1142         if (!outbox)
1143                 outbox = folder_get_default_outbox();
1144         cm_return_val_if_fail(outbox != NULL, -1);
1145
1146         /* remove queueing headers */
1147         if (is_queued) {
1148                 gchar tmp[MAXPATHLEN + 1];
1149
1150                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1151                            get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1152                 
1153                 if (procmsg_remove_special_headers(file, tmp) !=0)
1154                         return -1;
1155
1156                 folder_item_scan(outbox);
1157                 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1158                         g_warning("can't save message\n");
1159                         claws_unlink(tmp);
1160                         return -1;
1161                 }
1162         } else {
1163                 folder_item_scan(outbox);
1164                 if ((num = folder_item_add_msg
1165                         (outbox, file, &flag, FALSE)) < 0) {
1166                         g_warning("can't save message\n");
1167                         return -1;
1168                 }
1169         }
1170         msginfo = folder_item_get_msginfo(outbox, num);         /* refcnt++ */
1171         tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);   /* refcnt++ */ 
1172         if (msginfo != NULL) {
1173                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1174                 procmsg_msginfo_free(msginfo);                  /* refcnt-- */
1175                 /* tmp_msginfo == msginfo */
1176                 if (tmp_msginfo && msginfo->extradata && 
1177                     (msginfo->extradata->dispositionnotificationto || 
1178                      msginfo->extradata->returnreceiptto)) {
1179                         procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 
1180                 }       
1181                 procmsg_msginfo_free(tmp_msginfo);              /* refcnt-- */
1182         }
1183
1184         return 0;
1185 }
1186
1187
1188 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1189 {
1190         static const gchar *def_cmd = "lpr %s";
1191         static guint id = 0;
1192         gchar *prtmp;
1193         FILE *tmpfp, *prfp;
1194         gchar buf[1024];
1195         gchar *p;
1196         int r;
1197
1198         cm_return_if_fail(msginfo);
1199
1200         if (procmime_msginfo_is_encrypted(msginfo))
1201                 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1202         else
1203                 tmpfp = procmime_get_first_text_content(msginfo);
1204         if (tmpfp == NULL) {
1205                 g_warning("Can't get text part\n");
1206                 return;
1207         }
1208
1209         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1210                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1211
1212         if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1213                 FILE_OP_ERROR(prtmp, "fopen");
1214                 g_free(prtmp);
1215                 fclose(tmpfp);
1216                 return;
1217         }
1218
1219         if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1220         if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1221         if (msginfo->to)   { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1222         if (msginfo->cc)   { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1223         if (msginfo->newsgroups) {
1224                 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1225         }
1226         if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1227         if (fputc('\n', prfp) == EOF) goto fpferr;
1228
1229         while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1230                 r = fputs(buf, prfp);
1231                 if (r == EOF) goto fpferr;
1232         }
1233
1234         fclose(prfp);
1235         fclose(tmpfp);
1236
1237         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1238             !strchr(p + 2, '%'))
1239                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1240         else {
1241                 if (cmdline)
1242                         g_warning("Print command-line is invalid: '%s'\n",
1243                                   cmdline);
1244                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1245         }
1246
1247         g_free(prtmp);
1248
1249         g_strchomp(buf);
1250         if (buf[strlen(buf) - 1] != '&')
1251                 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1252         if (system(buf) == -1)
1253                 g_warning("system(%s) failed.", buf);
1254         return;
1255 fpferr:
1256         FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1257         g_free(prtmp);
1258         fclose(tmpfp);
1259         fclose(prfp);
1260 }
1261
1262 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1263 {
1264         msginfo->refcnt++;
1265         
1266         return msginfo;
1267 }
1268
1269 MsgInfo *procmsg_msginfo_new(void)
1270 {
1271         MsgInfo *newmsginfo;
1272
1273         newmsginfo = g_new0(MsgInfo, 1);
1274         newmsginfo->refcnt = 1;
1275         
1276         return newmsginfo;
1277 }
1278
1279 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1280 {
1281         MsgInfoAvatar *newavatar;
1282
1283         if (avatar == NULL) return NULL;
1284
1285         newavatar = g_new0(MsgInfoAvatar, 1);
1286         newavatar->avatar_id = avatar->avatar_id;
1287         newavatar->avatar_src = g_strdup(avatar->avatar_src);
1288
1289         return newavatar;
1290 }
1291
1292 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1293 {
1294         if (avatar != NULL) {
1295                 if (avatar->avatar_src != NULL)
1296                         g_free(avatar->avatar_src);
1297                 g_free(avatar);
1298         }
1299 }
1300
1301 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1302 {
1303         MsgInfo *newmsginfo;
1304         GSList *refs;
1305
1306         if (msginfo == NULL) return NULL;
1307
1308         newmsginfo = g_new0(MsgInfo, 1);
1309
1310         newmsginfo->refcnt = 1;
1311
1312 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1313 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1314                         g_strdup(msginfo->mmb) : NULL
1315
1316         MEMBCOPY(msgnum);
1317         MEMBCOPY(size);
1318         MEMBCOPY(mtime);
1319         MEMBCOPY(date_t);
1320
1321         MEMBCOPY(flags);
1322
1323         MEMBDUP(fromname);
1324
1325         MEMBDUP(date);
1326         MEMBDUP(from);
1327         MEMBDUP(to);
1328         MEMBDUP(cc);
1329         MEMBDUP(newsgroups);
1330         MEMBDUP(subject);
1331         MEMBDUP(msgid);
1332         MEMBDUP(inreplyto);
1333         MEMBDUP(xref);
1334
1335         MEMBCOPY(folder);
1336         MEMBCOPY(to_folder);
1337
1338         if (msginfo->extradata) {
1339                 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1340                 if (msginfo->extradata->avatars) {
1341                         newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1342                                                                 (GCopyFunc) procmsg_msginfoavatar_copy);
1343                 }
1344                 MEMBDUP(extradata->dispositionnotificationto);
1345                 MEMBDUP(extradata->returnreceiptto);
1346                 MEMBDUP(extradata->partial_recv);
1347                 MEMBDUP(extradata->account_server);
1348                 MEMBDUP(extradata->account_login);
1349                 MEMBDUP(extradata->list_post);
1350                 MEMBDUP(extradata->list_subscribe);
1351                 MEMBDUP(extradata->list_unsubscribe);
1352                 MEMBDUP(extradata->list_help);
1353                 MEMBDUP(extradata->list_archive);
1354                 MEMBDUP(extradata->list_owner);
1355         }
1356
1357         refs = msginfo->references;
1358         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1359                 newmsginfo->references = g_slist_prepend
1360                         (newmsginfo->references, g_strdup(refs->data)); 
1361         }
1362         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1363
1364         MEMBCOPY(score);
1365         MEMBDUP(plaintext_file);
1366
1367         return newmsginfo;
1368 }
1369
1370 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1371 {
1372         MsgInfo *full_msginfo;
1373
1374         if (msginfo == NULL) return NULL;
1375
1376         if (!file || !is_file_exist(file)) {
1377                 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1378                 return NULL;
1379         }
1380
1381         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1382         if (!full_msginfo) return NULL;
1383
1384         msginfo->total_size = full_msginfo->total_size;
1385         msginfo->planned_download = full_msginfo->planned_download;
1386
1387         if (full_msginfo->extradata) {
1388                 if (!msginfo->extradata)
1389                         msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1390                 if (!msginfo->extradata->list_post)
1391                         msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1392                 if (!msginfo->extradata->list_subscribe)
1393                         msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1394                 if (!msginfo->extradata->list_unsubscribe)
1395                         msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1396                 if (!msginfo->extradata->list_help)
1397                         msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1398                 if (!msginfo->extradata->list_archive)
1399                         msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1400                 if (!msginfo->extradata->list_owner)
1401                         msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1402                 if (!msginfo->extradata->avatars)
1403                         msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1404                                                                         (GCopyFunc) procmsg_msginfoavatar_copy);
1405                 if (!msginfo->extradata->dispositionnotificationto)
1406                         msginfo->extradata->dispositionnotificationto = 
1407                                 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1408                 if (!msginfo->extradata->returnreceiptto)
1409                         msginfo->extradata->returnreceiptto = g_strdup
1410                                 (full_msginfo->extradata->returnreceiptto);
1411                 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1412                         msginfo->extradata->partial_recv = g_strdup
1413                                 (full_msginfo->extradata->partial_recv);
1414                 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1415                         msginfo->extradata->account_server = g_strdup
1416                                 (full_msginfo->extradata->account_server);
1417                 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1418                         msginfo->extradata->account_login = g_strdup
1419                                 (full_msginfo->extradata->account_login);
1420         }
1421         procmsg_msginfo_free(full_msginfo);
1422
1423         return procmsg_msginfo_new_ref(msginfo);
1424 }
1425
1426 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1427 {
1428         MsgInfo *full_msginfo;
1429         gchar *file;
1430
1431         if (msginfo == NULL) return NULL;
1432
1433         file = procmsg_get_message_file_path(msginfo);
1434         if (!file || !is_file_exist(file)) {
1435                 g_free(file);
1436                 file = procmsg_get_message_file(msginfo);
1437         }
1438         if (!file || !is_file_exist(file)) {
1439                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1440                 return NULL;
1441         }
1442
1443         full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1444         g_free(file);
1445         return full_msginfo;
1446 }
1447
1448 void procmsg_msginfo_free(MsgInfo *msginfo)
1449 {
1450         if (msginfo == NULL) return;
1451
1452         msginfo->refcnt--;
1453         if (msginfo->refcnt > 0)
1454                 return;
1455
1456         if (msginfo->to_folder) {
1457                 msginfo->to_folder->op_count--;
1458                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1459         }
1460
1461         g_free(msginfo->fromspace);
1462
1463         g_free(msginfo->fromname);
1464
1465         g_free(msginfo->date);
1466         g_free(msginfo->from);
1467         g_free(msginfo->to);
1468         g_free(msginfo->cc);
1469         g_free(msginfo->newsgroups);
1470         g_free(msginfo->subject);
1471         g_free(msginfo->msgid);
1472         g_free(msginfo->inreplyto);
1473         g_free(msginfo->xref);
1474
1475         if (msginfo->extradata) {
1476                 if (msginfo->extradata->avatars) {
1477                         g_slist_foreach(msginfo->extradata->avatars,
1478                                         (GFunc)procmsg_msginfoavatar_free,
1479                                         NULL);
1480                         g_slist_free(msginfo->extradata->avatars);
1481                 }
1482                 g_free(msginfo->extradata->returnreceiptto);
1483                 g_free(msginfo->extradata->dispositionnotificationto);
1484                 g_free(msginfo->extradata->list_post);
1485                 g_free(msginfo->extradata->list_subscribe);
1486                 g_free(msginfo->extradata->list_unsubscribe);
1487                 g_free(msginfo->extradata->list_help);
1488                 g_free(msginfo->extradata->list_archive);
1489                 g_free(msginfo->extradata->list_owner);
1490                 g_free(msginfo->extradata->partial_recv);
1491                 g_free(msginfo->extradata->account_server);
1492                 g_free(msginfo->extradata->account_login);
1493                 g_free(msginfo->extradata);
1494         }
1495         slist_free_strings_full(msginfo->references);
1496         g_slist_free(msginfo->tags);
1497
1498         g_free(msginfo->plaintext_file);
1499
1500         g_free(msginfo);
1501 }
1502
1503 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1504 {
1505         guint memusage = 0;
1506         GSList *tmp;
1507         
1508         memusage += sizeof(MsgInfo);
1509         if (msginfo->fromname)
1510                 memusage += strlen(msginfo->fromname);
1511         if (msginfo->date)
1512                 memusage += strlen(msginfo->date);
1513         if (msginfo->from)
1514                 memusage += strlen(msginfo->from);
1515         if (msginfo->to)
1516                 memusage += strlen(msginfo->to);
1517         if (msginfo->cc)
1518                 memusage += strlen(msginfo->cc);
1519         if (msginfo->newsgroups)
1520                 memusage += strlen(msginfo->newsgroups);
1521         if (msginfo->subject)
1522                 memusage += strlen(msginfo->subject);
1523         if (msginfo->msgid)
1524                 memusage += strlen(msginfo->msgid);
1525         if (msginfo->inreplyto)
1526                 memusage += strlen(msginfo->inreplyto);
1527
1528         for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1529                 gchar *r = (gchar *)tmp->data;
1530                 memusage += r?strlen(r):0 + sizeof(GSList);
1531         }
1532         if (msginfo->fromspace)
1533                 memusage += strlen(msginfo->fromspace);
1534
1535         for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1536                 memusage += sizeof(GSList);
1537         }
1538         if (msginfo->extradata) {
1539                 memusage += sizeof(MsgInfoExtraData);
1540                 if (msginfo->extradata->avatars) {
1541                         for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1542                                 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1543                                 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1544                                 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1545                         }
1546                 }
1547                 if (msginfo->extradata->dispositionnotificationto)
1548                         memusage += strlen(msginfo->extradata->dispositionnotificationto);
1549                 if (msginfo->extradata->returnreceiptto)
1550                         memusage += strlen(msginfo->extradata->returnreceiptto);
1551
1552                 if (msginfo->extradata->partial_recv)
1553                         memusage += strlen(msginfo->extradata->partial_recv);
1554                 if (msginfo->extradata->account_server)
1555                         memusage += strlen(msginfo->extradata->account_server);
1556                 if (msginfo->extradata->account_login)
1557                         memusage += strlen(msginfo->extradata->account_login);
1558
1559                 if (msginfo->extradata->list_post)
1560                         memusage += strlen(msginfo->extradata->list_post);
1561                 if (msginfo->extradata->list_subscribe)
1562                         memusage += strlen(msginfo->extradata->list_subscribe);
1563                 if (msginfo->extradata->list_unsubscribe)
1564                         memusage += strlen(msginfo->extradata->list_unsubscribe);
1565                 if (msginfo->extradata->list_help)
1566                         memusage += strlen(msginfo->extradata->list_help);
1567                 if (msginfo->extradata->list_archive)
1568                         memusage += strlen(msginfo->extradata->list_archive);
1569                 if (msginfo->extradata->list_owner)
1570                         memusage += strlen(msginfo->extradata->list_owner);
1571         }
1572         return memusage;
1573 }
1574
1575 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1576                                             FolderItem *queue, gint msgnum, gboolean *queued_removed)
1577 {
1578         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1579                                        {"SSV:",  NULL, FALSE},
1580                                        {"R:",    NULL, FALSE},
1581                                        {"NG:",   NULL, FALSE},
1582                                        {"MAID:", NULL, FALSE},
1583                                        {"NAID:", NULL, FALSE},
1584                                        {"SCF:",  NULL, FALSE},
1585                                        {"RMID:", NULL, FALSE},
1586                                        {"FMID:", NULL, FALSE},
1587                                        {"X-Claws-Privacy-System:", NULL, FALSE},
1588                                        {"X-Claws-Encrypt:", NULL, FALSE},
1589                                        {"X-Claws-Encrypt-Data:", NULL, FALSE},
1590                                        {"X-Claws-End-Special-Headers:", NULL, FALSE},
1591                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1592                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1593                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1594                                        {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1595                                        {NULL,    NULL, FALSE}};
1596         FILE *fp;
1597         gint filepos;
1598         gint mailval = 0, newsval = 0;
1599         gchar *from = NULL;
1600         gchar *smtpserver = NULL;
1601         GSList *to_list = NULL;
1602         GSList *newsgroup_list = NULL;
1603         gchar *savecopyfolder = NULL;
1604         gchar *replymessageid = NULL;
1605         gchar *fwdmessageid = NULL;
1606         gchar *privacy_system = NULL;
1607         gboolean encrypt = FALSE;
1608         gchar *encrypt_data = NULL;
1609         gchar buf[BUFFSIZE];
1610         gint hnum;
1611         PrefsAccount *mailac = NULL, *newsac = NULL;
1612         gboolean save_clear_text = TRUE;
1613         gchar *tmp_enc_file = NULL;
1614
1615         cm_return_val_if_fail(file != NULL, -1);
1616
1617         if ((fp = g_fopen(file, "rb")) == NULL) {
1618                 FILE_OP_ERROR(file, "fopen");
1619                 if (errstr) {
1620                         if (*errstr) g_free(*errstr);
1621                         *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1622                 }
1623                 return -1;
1624         }
1625
1626         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1627                != -1) {
1628                 gchar *p = buf + strlen(qentry[hnum].name);
1629
1630                 switch (hnum) {
1631                 case Q_SENDER:
1632                         if (from == NULL) 
1633                                 from = g_strdup(p);
1634                         break;
1635                 case Q_SMTPSERVER:
1636                         if (smtpserver == NULL) 
1637                                 smtpserver = g_strdup(p);
1638                         break;
1639                 case Q_RECIPIENTS:
1640                         to_list = address_list_append(to_list, p);
1641                         break;
1642                 case Q_NEWSGROUPS:
1643                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1644                         break;
1645                 case Q_MAIL_ACCOUNT_ID:
1646                         mailac = account_find_from_id(atoi(p));
1647                         break;
1648                 case Q_NEWS_ACCOUNT_ID:
1649                         newsac = account_find_from_id(atoi(p));
1650                         break;
1651                 case Q_SAVE_COPY_FOLDER:
1652                         if (savecopyfolder == NULL) 
1653                                 savecopyfolder = g_strdup(p);
1654                         break;
1655                 case Q_REPLY_MESSAGE_ID:
1656                         if (replymessageid == NULL) 
1657                                 replymessageid = g_strdup(p);
1658                         break;
1659                 case Q_FWD_MESSAGE_ID:
1660                         if (fwdmessageid == NULL) 
1661                                 fwdmessageid = g_strdup(p);
1662                         break;
1663                 case Q_PRIVACY_SYSTEM:
1664                 case Q_PRIVACY_SYSTEM_OLD:
1665                         if (privacy_system == NULL) 
1666                                 privacy_system = g_strdup(p);
1667                         break;
1668                 case Q_ENCRYPT:
1669                 case Q_ENCRYPT_OLD:
1670                         if (p[0] == '1') 
1671                                 encrypt = TRUE;
1672                         break;
1673                 case Q_ENCRYPT_DATA:
1674                 case Q_ENCRYPT_DATA_OLD:
1675                         if (encrypt_data == NULL) 
1676                                 encrypt_data = g_strdup(p);
1677                         break;
1678                 case Q_CLAWS_HDRS:
1679                 case Q_CLAWS_HDRS_OLD:
1680                         /* end of special headers reached */
1681                         goto send_mail; /* can't "break;break;" */
1682                 }
1683         }
1684 send_mail:
1685         filepos = ftell(fp);
1686
1687         if (encrypt) {
1688                 MimeInfo *mimeinfo;
1689
1690                 if (mailac && mailac->save_encrypted_as_clear_text 
1691                 &&  !mailac->encrypt_to_self)
1692                         save_clear_text = TRUE;
1693                 else
1694                         save_clear_text = FALSE;
1695
1696                 fclose(fp);
1697                 fp = NULL;
1698
1699                 mimeinfo = procmime_scan_queue_file(file);
1700                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1701                 || (fp = my_tmpfile()) == NULL
1702                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1703                         if (fp)
1704                                 fclose(fp);
1705                         procmime_mimeinfo_free_all(mimeinfo);
1706                         g_free(from);
1707                         g_free(smtpserver);
1708                         slist_free_strings_full(to_list);
1709                         slist_free_strings_full(newsgroup_list);
1710                         g_free(savecopyfolder);
1711                         g_free(replymessageid);
1712                         g_free(fwdmessageid);
1713                         g_free(privacy_system);
1714                         g_free(encrypt_data);
1715                         if (errstr) {
1716                                 if (*errstr) g_free(*errstr);
1717                                 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1718                                                 privacy_get_error());
1719                         }
1720                         return -1;
1721                 }
1722                 
1723                 rewind(fp);
1724                 if (!save_clear_text) {
1725                         gchar *content = NULL;
1726                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1727                         if (tmpfp) {
1728                                 fclose(tmpfp);
1729
1730                                 content = file_read_stream_to_str(fp);
1731                                 rewind(fp);
1732
1733                                 str_write_to_file(content, tmp_enc_file);
1734                                 g_free(content);
1735                         } else {
1736                                 g_warning("couldn't get tempfile\n");
1737                         }
1738                 } 
1739                 
1740                 procmime_mimeinfo_free_all(mimeinfo);
1741                 
1742                 filepos = 0;
1743         }
1744
1745         if (to_list) {
1746                 debug_print("Sending message by mail\n");
1747                 if (!from) {
1748                         if (errstr) {
1749                                 if (*errstr) g_free(*errstr);
1750                                 *errstr = g_strdup_printf(_("Queued message header is broken."));
1751                         }
1752                         mailval = -1;
1753                 } else if (mailac && mailac->use_mail_command &&
1754                            mailac->mail_command && (* mailac->mail_command)) {
1755                         mailval = send_message_local(mailac->mail_command, fp);
1756                 } else {
1757                         if (!mailac) {
1758                                 mailac = account_find_from_smtp_server(from, smtpserver);
1759                                 if (!mailac) {
1760                                         g_warning("Account not found. "
1761                                                     "Using current account...\n");
1762                                         mailac = cur_account;
1763                                 }
1764                         }
1765
1766                         if (mailac) {
1767                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1768                                 if (mailval == -1 && errstr) {
1769                                         if (*errstr) g_free(*errstr);
1770                                         *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1771                                 }
1772                         } else {
1773                                 PrefsAccount tmp_ac;
1774
1775                                 g_warning("Account not found.\n");
1776
1777                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1778                                 tmp_ac.address = from;
1779                                 tmp_ac.smtp_server = smtpserver;
1780                                 tmp_ac.smtpport = SMTP_PORT;
1781                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1782                                 if (mailval == -1 && errstr) {
1783                                         if (*errstr) g_free(*errstr);
1784                                         *errstr = g_strdup_printf(_("No specific account has been found to "
1785                                                         "send, and an error happened during SMTP session."));
1786                                 }
1787                         }
1788                 }
1789         } else if (!to_list && !newsgroup_list) {
1790                 if (errstr) {
1791                         if (*errstr) g_free(*errstr);
1792                         *errstr = g_strdup(_("Couldn't determine sending information. "
1793                                 "Maybe the email hasn't been generated by Claws Mail."));
1794                 }
1795                 mailval = -1;
1796         }
1797
1798         fseek(fp, filepos, SEEK_SET);
1799         if (newsgroup_list && newsac && (mailval == 0)) {
1800                 Folder *folder;
1801                 gchar *tmp = NULL;
1802                 FILE *tmpfp;
1803
1804                 /* write to temporary file */
1805                 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1806                             G_DIR_SEPARATOR, file);
1807                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1808                         FILE_OP_ERROR(tmp, "fopen");
1809                         newsval = -1;
1810                         alertpanel_error(_("Couldn't create temporary file for news sending."));
1811                 } else {
1812                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1813                                 FILE_OP_ERROR(tmp, "chmod");
1814                                 g_warning("can't change file mode\n");
1815                         }
1816
1817                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1818                                 if (fputs(buf, tmpfp) == EOF) {
1819                                         FILE_OP_ERROR(tmp, "fputs");
1820                                         newsval = -1;
1821                                         if (errstr) {
1822                                                 if (*errstr) g_free(*errstr);
1823                                                 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1824                                         }
1825                                 }
1826                         }
1827                         fclose(tmpfp);
1828
1829                         if (newsval == 0) {
1830                                 debug_print("Sending message by news\n");
1831
1832                                 folder = FOLDER(newsac->folder);
1833
1834                                 newsval = news_post(folder, tmp);
1835                                 if (newsval < 0 && errstr)  {
1836                                         if (*errstr) g_free(*errstr);
1837                                         *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1838                                          newsac->nntp_server);
1839                                 }
1840                         }
1841                         claws_unlink(tmp);
1842                 }
1843                 g_free(tmp);
1844         }
1845
1846         fclose(fp);
1847
1848         /* update session statistics */
1849         if (mailval == 0 && newsval == 0) {
1850                 /* update session stats */
1851                 if (replymessageid)
1852                         session_stats.replied++;
1853                 else if (fwdmessageid)
1854                         session_stats.forwarded++;
1855                 else
1856                         session_stats.sent++;
1857         }
1858
1859         /* save message to outbox */
1860         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1861                 FolderItem *outbox;
1862
1863                 debug_print("saving sent message...\n");
1864
1865                 outbox = folder_find_item_from_identifier(savecopyfolder);
1866                 if (!outbox)
1867                         outbox = folder_get_default_outbox();
1868                         
1869                 if (save_clear_text || tmp_enc_file == NULL) {
1870                         gboolean saved = FALSE;
1871                         *queued_removed = FALSE;
1872                         if (queue && msgnum > 0) {
1873                                 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1874                                 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1875                                         debug_print("moved queued mail %d to sent folder\n", msgnum);
1876                                         saved = TRUE;
1877                                         *queued_removed = TRUE;
1878                                 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1879                                         debug_print("copied queued mail %d to sent folder\n", msgnum);
1880                                         saved = TRUE;
1881                                 }
1882                                 procmsg_msginfo_free(queued_mail);
1883                         }
1884                         if (!saved) {
1885                                 debug_print("resaving clear text queued mail to sent folder\n");
1886                                 procmsg_save_to_outbox(outbox, file, TRUE);
1887                         }
1888                 } else {
1889                         debug_print("saving encrpyted queued mail to sent folder\n");
1890                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1891                 }
1892         }
1893
1894         if (tmp_enc_file != NULL) {
1895                 claws_unlink(tmp_enc_file);
1896                 free(tmp_enc_file);
1897                 tmp_enc_file = NULL;
1898         }
1899
1900         if (replymessageid != NULL || fwdmessageid != NULL) {
1901                 gchar **tokens;
1902                 FolderItem *item;
1903                 
1904                 if (replymessageid != NULL)
1905                         tokens = g_strsplit(replymessageid, "\t", 0);
1906                 else
1907                         tokens = g_strsplit(fwdmessageid, "\t", 0);
1908                 item = folder_find_item_from_identifier(tokens[0]);
1909
1910                 /* check if queued message has valid folder and message id */
1911                 if (item != NULL && tokens[2] != NULL) {
1912                         MsgInfo *msginfo;
1913                         
1914                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1915                 
1916                         /* check if referring message exists and has a message id */
1917                         if ((msginfo != NULL) && 
1918                             (msginfo->msgid != NULL) &&
1919                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1920                                 procmsg_msginfo_free(msginfo);
1921                                 msginfo = NULL;
1922                         }
1923                         
1924                         if (msginfo == NULL) {
1925                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1926                         }
1927                         
1928                         if (msginfo != NULL) {
1929                                 if (replymessageid != NULL) {
1930                                         MsgPermFlags to_unset = 0;
1931
1932                                         if (prefs_common.mark_as_read_on_new_window)
1933                                                 to_unset = (MSG_NEW|MSG_UNREAD);
1934
1935                                         procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1936                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1937                                 }  else {
1938                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1939                                 }
1940                                 procmsg_msginfo_free(msginfo);
1941                         }
1942                 }
1943                 g_strfreev(tokens);
1944         }
1945
1946         g_free(from);
1947         g_free(smtpserver);
1948         slist_free_strings_full(to_list);
1949         slist_free_strings_full(newsgroup_list);
1950         g_free(savecopyfolder);
1951         g_free(replymessageid);
1952         g_free(fwdmessageid);
1953         g_free(privacy_system);
1954         g_free(encrypt_data);
1955
1956         return (newsval != 0 ? newsval : mailval);
1957 }
1958
1959 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1960 {
1961         gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1962         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1963         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1964         return result;
1965 }
1966
1967 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1968 {
1969         gint val;
1970         if (procmsg_queue_lock(errstr)) {
1971                 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1972                 procmsg_queue_unlock();
1973                 return val;
1974         }
1975         return -1;
1976 }
1977
1978 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1979 {
1980         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1981
1982         /* NEW flag */
1983         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1984                 item->new_msgs++;
1985         }
1986
1987         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1988                 item->new_msgs--;
1989         }
1990
1991         /* UNREAD flag */
1992         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1993                 item->unread_msgs++;
1994                 if (procmsg_msg_has_marked_parent(msginfo))
1995                         item->unreadmarked_msgs++;
1996         }
1997
1998         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1999                 item->unread_msgs--;
2000                 if (procmsg_msg_has_marked_parent(msginfo))
2001                         item->unreadmarked_msgs--;
2002         }
2003         
2004         /* MARK flag */
2005         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
2006                 procmsg_update_unread_children(msginfo, TRUE);
2007                 item->marked_msgs++;
2008         }
2009
2010         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
2011                 procmsg_update_unread_children(msginfo, FALSE);
2012                 item->marked_msgs--;
2013         }
2014
2015         if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
2016                 item->replied_msgs++;
2017         }
2018
2019         if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
2020                 item->replied_msgs--;
2021         }
2022
2023         if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
2024                 item->forwarded_msgs++;
2025         }
2026
2027         if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
2028                 item->forwarded_msgs--;
2029         }
2030
2031         if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
2032                 item->locked_msgs++;
2033         }
2034
2035         if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
2036                 item->locked_msgs--;
2037         }
2038
2039         if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
2040                 item->ignored_msgs--;
2041         }
2042
2043         if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
2044                 item->ignored_msgs++;
2045         }
2046
2047         if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
2048                 item->watched_msgs--;
2049         }
2050
2051         if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2052                 item->watched_msgs++;
2053         }
2054 }
2055
2056 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2057 {
2058         FolderItem *item;
2059         MsgInfoUpdate msginfo_update;
2060         MsgPermFlags perm_flags_new, perm_flags_old;
2061         MsgTmpFlags tmp_flags_old;
2062
2063         cm_return_if_fail(msginfo != NULL);
2064         item = msginfo->folder;
2065         cm_return_if_fail(item != NULL);
2066         
2067         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2068
2069         /* Perm Flags handling */
2070         perm_flags_old = msginfo->flags.perm_flags;
2071         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2072         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2073                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2074         }
2075         if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2076                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2077         }
2078
2079         if (perm_flags_old != perm_flags_new) {
2080                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2081
2082                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2083                 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2084         }
2085
2086         /* Tmp flags handling */
2087         tmp_flags_old = msginfo->flags.tmp_flags;
2088         msginfo->flags.tmp_flags |= tmp_flags;
2089
2090         /* update notification */
2091         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2092                 msginfo_update.msginfo = msginfo;
2093                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2094                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2095                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2096         }
2097 }
2098
2099 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2100 {
2101         FolderItem *item;
2102         MsgInfoUpdate msginfo_update;
2103         MsgPermFlags perm_flags_new, perm_flags_old;
2104         MsgTmpFlags tmp_flags_old;
2105
2106         cm_return_if_fail(msginfo != NULL);
2107         item = msginfo->folder;
2108         cm_return_if_fail(item != NULL);
2109         
2110         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2111
2112         /* Perm Flags handling */
2113         perm_flags_old = msginfo->flags.perm_flags;
2114         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2115         
2116         if (perm_flags_old != perm_flags_new) {
2117                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2118
2119                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2120         }
2121
2122         /* Tmp flags hanlding */
2123         tmp_flags_old = msginfo->flags.tmp_flags;
2124         msginfo->flags.tmp_flags &= ~tmp_flags;
2125
2126         /* update notification */
2127         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2128                 msginfo_update.msginfo = msginfo;
2129                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2130                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2131                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2132         }
2133 }
2134
2135 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
2136                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2137                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2138 {
2139         FolderItem *item;
2140         MsgInfoUpdate msginfo_update;
2141         MsgPermFlags perm_flags_new, perm_flags_old;
2142         MsgTmpFlags tmp_flags_old;
2143
2144         cm_return_if_fail(msginfo != NULL);
2145         item = msginfo->folder;
2146         cm_return_if_fail(item != NULL);
2147         
2148         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2149
2150         /* Perm Flags handling */
2151         perm_flags_old = msginfo->flags.perm_flags;
2152         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2153         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2154                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2155         }
2156         if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2157                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2158         }
2159
2160         if (perm_flags_old != perm_flags_new) {
2161                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2162
2163                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2164
2165         }
2166
2167         /* Tmp flags handling */
2168         tmp_flags_old = msginfo->flags.tmp_flags;
2169         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2170         msginfo->flags.tmp_flags |= add_tmp_flags;
2171
2172         /* update notification */
2173         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2174                 msginfo_update.msginfo = msginfo;
2175                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2176                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2177                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2178         }
2179 }
2180
2181 /*!
2182  *\brief        check for flags (e.g. mark) in prior msgs of current thread
2183  *
2184  *\param        info Current message
2185  *\param        perm_flags Flags to be checked
2186  *\param        parentmsgs Hash of prior msgs to avoid loops
2187  *
2188  *\return       gboolean TRUE if perm_flags are found
2189  */
2190 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2191                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2192 {
2193         MsgInfo *tmp;
2194
2195         cm_return_val_if_fail(info != NULL, FALSE);
2196
2197         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2198                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2199                                 info->inreplyto);
2200                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2201                         procmsg_msginfo_free(tmp);
2202                         return TRUE;
2203                 } else if (tmp != NULL) {
2204                         gboolean result;
2205
2206                         if (g_hash_table_lookup(parentmsgs, info)) {
2207                                 debug_print("loop detected: %d\n",
2208                                         info->msgnum);
2209                                 result = FALSE;
2210                         } else {
2211                                 g_hash_table_insert(parentmsgs, info, "1");
2212                                 result = procmsg_msg_has_flagged_parent_real(
2213                                     tmp, perm_flags, parentmsgs);
2214                         }
2215                         procmsg_msginfo_free(tmp);
2216                         return result;
2217                 } else {
2218                         return FALSE;
2219                 }
2220         } else
2221                 return FALSE;
2222 }
2223
2224 /*!
2225  *\brief        Callback for cleaning up hash of parentmsgs
2226  */
2227 static gboolean parentmsgs_hash_remove(gpointer key,
2228                             gpointer value,
2229                             gpointer user_data)
2230 {
2231         return TRUE;
2232 }
2233
2234 /*!
2235  *\brief        Set up list of parentmsgs
2236  *              See procmsg_msg_has_flagged_parent_real()
2237  */
2238 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2239 {
2240         gboolean result;
2241         static GHashTable *parentmsgs = NULL;
2242         
2243         if (parentmsgs == NULL)
2244                 parentmsgs = g_hash_table_new(NULL, NULL); 
2245
2246         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2247         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2248
2249         return result;
2250 }
2251
2252 /*!
2253  *\brief        Check if msgs prior in thread are marked
2254  *              See procmsg_msg_has_flagged_parent_real()
2255  */
2256 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2257 {
2258         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2259 }
2260
2261
2262 static GSList *procmsg_find_children_func(MsgInfo *info, 
2263                                    GSList *children, GSList *all)
2264 {
2265         GSList *cur;
2266
2267         cm_return_val_if_fail(info!=NULL, children);
2268         if (info->msgid == NULL)
2269                 return children;
2270
2271         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2272                 MsgInfo *tmp = (MsgInfo *)cur->data;
2273                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2274                         /* Check if message is already in the list */
2275                         if ((children == NULL) || 
2276                             (g_slist_index(children, tmp) == -1)) {
2277                                 children = g_slist_prepend(children,
2278                                                 procmsg_msginfo_new_ref(tmp));
2279                                 children = procmsg_find_children_func(tmp, 
2280                                                         children, 
2281                                                         all);
2282                         }
2283                 }
2284         }
2285         return children;
2286 }
2287
2288 static GSList *procmsg_find_children (MsgInfo *info)
2289 {
2290         GSList *children;
2291         GSList *all, *cur;
2292
2293         cm_return_val_if_fail(info!=NULL, NULL);
2294         all = folder_item_get_msg_list(info->folder);
2295         children = procmsg_find_children_func(info, NULL, all);
2296         if (children != NULL) {
2297                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2298                         /* this will not free the used pointers
2299                            created with procmsg_msginfo_new_ref */
2300                         procmsg_msginfo_free((MsgInfo *)cur->data);
2301                 }
2302         }
2303         g_slist_free(all);
2304
2305         return children;
2306 }
2307
2308 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2309 {
2310         GSList *children = procmsg_find_children(info);
2311         GSList *cur;
2312         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2313                 MsgInfo *tmp = (MsgInfo *)cur->data;
2314                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2315                         if(newly_marked) 
2316                                 info->folder->unreadmarked_msgs++;
2317                         else
2318                                 info->folder->unreadmarked_msgs--;
2319                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2320                 }
2321                 procmsg_msginfo_free(tmp);
2322         }
2323         g_slist_free(children);
2324 }
2325
2326 /**
2327  * Set the destination folder for a copy or move operation
2328  *
2329  * \param msginfo The message which's destination folder is changed
2330  * \param to_folder The destination folder for the operation
2331  */
2332 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2333 {
2334         if(msginfo->to_folder != NULL) {
2335                 msginfo->to_folder->op_count--;
2336                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2337         }
2338         msginfo->to_folder = to_folder;
2339         if(to_folder != NULL) {
2340                 to_folder->op_count++;
2341                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2342         }
2343 }
2344
2345 /**
2346  * Apply filtering actions to the msginfo
2347  *
2348  * \param msginfo The MsgInfo describing the message that should be filtered
2349  * \return TRUE if the message was moved and MsgInfo is now invalid,
2350  *         FALSE otherwise
2351  */
2352 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2353 {
2354         MailFilteringData mail_filtering_data;
2355                         
2356         mail_filtering_data.msginfo = msginfo;                  
2357         mail_filtering_data.msglist = NULL;                     
2358         mail_filtering_data.filtered = NULL;                    
2359         mail_filtering_data.unfiltered = NULL;
2360         mail_filtering_data.account = ac_prefs; 
2361
2362         if (!ac_prefs || ac_prefs->filterhook_on_recv)
2363                 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2364                 return TRUE;
2365
2366         /* filter if enabled in prefs or move to inbox if not */
2367         if((filtering_rules != NULL) &&
2368                 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2369                                 FILTERING_INCORPORATION, NULL)) {
2370                 return TRUE;
2371         }
2372                 
2373         return FALSE;
2374 }
2375
2376 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac, 
2377                             GSList **filtered, GSList **unfiltered,
2378                             gboolean do_filter)
2379 {
2380         GSList *cur, *to_do = NULL;
2381         gint total = 0, curnum = 0;
2382         MailFilteringData mail_filtering_data;
2383                         
2384         cm_return_if_fail(filtered != NULL);
2385         cm_return_if_fail(unfiltered != NULL);
2386
2387         *filtered = NULL;
2388         *unfiltered = NULL;
2389         
2390         if (list == NULL)
2391                 return;
2392
2393         total = g_slist_length(list);
2394
2395         if (!do_filter) {
2396                 *filtered = NULL;
2397                 *unfiltered = g_slist_copy(list);
2398                 return;
2399         }
2400
2401         statusbar_print_all(_("Filtering messages...\n"));
2402
2403         mail_filtering_data.msginfo = NULL;                     
2404         mail_filtering_data.msglist = list;                     
2405         mail_filtering_data.filtered = NULL;                    
2406         mail_filtering_data.unfiltered = NULL;  
2407         mail_filtering_data.account = ac;       
2408                         
2409         if (!ac || ac->filterhook_on_recv)
2410         hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2411         
2412         if (mail_filtering_data.filtered == NULL &&
2413             mail_filtering_data.unfiltered == NULL) {
2414                 /* nothing happened */
2415                 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2416                 to_do = list;
2417         } 
2418         if (mail_filtering_data.filtered != NULL) {
2419                 /* keep track of what's been filtered by the hooks */
2420                 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2421                         g_slist_length(list),
2422                         g_slist_length(mail_filtering_data.filtered),
2423                         g_slist_length(mail_filtering_data.unfiltered));
2424
2425                 *filtered = g_slist_copy(mail_filtering_data.filtered);
2426         }
2427         if (mail_filtering_data.unfiltered != NULL) {
2428                 /* what the hooks didn't handle will go in filtered or 
2429                  * unfiltered in the next loop */
2430                 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2431                         g_slist_length(list),
2432                         g_slist_length(mail_filtering_data.filtered),
2433                         g_slist_length(mail_filtering_data.unfiltered));
2434                 to_do = mail_filtering_data.unfiltered;
2435         } 
2436
2437         for (cur = to_do; cur; cur = cur->next) {
2438                 MsgInfo *info = (MsgInfo *)cur->data;
2439                 if (procmsg_msginfo_filter(info, ac))
2440                         *filtered = g_slist_prepend(*filtered, info);
2441                 else
2442                         *unfiltered = g_slist_prepend(*unfiltered, info);
2443                 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2444         }
2445
2446         g_slist_free(mail_filtering_data.filtered);
2447         g_slist_free(mail_filtering_data.unfiltered);
2448         
2449         *filtered = g_slist_reverse(*filtered);
2450         *unfiltered = g_slist_reverse(*unfiltered);
2451
2452         statusbar_progress_all(0,0,0);
2453         statusbar_pop_all();
2454 }
2455
2456 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2457 {
2458         MsgInfo *tmp_msginfo = NULL;
2459         MsgFlags flags = {0, 0};
2460         gchar *tmpfile = get_tmp_file();
2461         FILE *fp = g_fopen(tmpfile, "wb");
2462         
2463         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2464             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2465                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2466                 if (fp) 
2467                         fclose(fp);
2468                 g_free(tmpfile);
2469                 return NULL;
2470         }
2471         
2472         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2473                 fclose(fp);
2474                 fp = NULL;
2475                 tmp_msginfo = procheader_parse_file(
2476                         tmpfile, flags, 
2477                         TRUE, FALSE);
2478         }
2479         if (fp)
2480                 fclose(fp);
2481
2482         if (tmp_msginfo != NULL) {
2483                 if (src_msginfo)
2484                         tmp_msginfo->folder = src_msginfo->folder;
2485                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2486         } else {
2487                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2488         }
2489
2490         g_free(tmpfile);
2491
2492         return tmp_msginfo;
2493 }
2494
2495 static GSList *spam_learners = NULL;
2496
2497 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2498 {
2499         if (!g_slist_find(spam_learners, learn_func))
2500                 spam_learners = g_slist_append(spam_learners, learn_func);
2501         if (mainwindow_get_mainwindow()) {
2502                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2503                 summary_set_menu_sensitive(
2504                         mainwindow_get_mainwindow()->summaryview);
2505                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2506         }
2507 }
2508
2509 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2510 {
2511         spam_learners = g_slist_remove(spam_learners, learn_func);
2512         if (mainwindow_get_mainwindow()) {
2513                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2514                 summary_set_menu_sensitive(
2515                         mainwindow_get_mainwindow()->summaryview);
2516                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2517         }
2518 }
2519
2520 gboolean procmsg_spam_can_learn(void)
2521 {
2522         return g_slist_length(spam_learners) > 0;
2523 }
2524
2525 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2526 {
2527         GSList *cur = spam_learners;
2528         int ret = 0;
2529         for (; cur; cur = cur->next) {
2530                 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2531                 ret |= func(info, list, spam);
2532         }
2533         return ret;
2534 }
2535
2536 static gchar *spam_folder_item = NULL;
2537 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2538 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2539 {
2540         g_free(spam_folder_item);
2541         if (item_identifier)
2542                 spam_folder_item = g_strdup(item_identifier);
2543         else
2544                 spam_folder_item = NULL;
2545         if (spam_get_folder_func != NULL)
2546                 procmsg_spam_get_folder_func = spam_get_folder_func;
2547         else
2548                 procmsg_spam_get_folder_func = NULL;
2549 }
2550
2551 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2552 {
2553         FolderItem *item = NULL;
2554         
2555         if (procmsg_spam_get_folder_func) 
2556                 item = procmsg_spam_get_folder_func(msginfo);
2557         if (item == NULL && spam_folder_item)
2558                 item = folder_find_item_from_identifier(spam_folder_item);
2559         if (item == NULL)
2560                 item = folder_get_default_trash();
2561         return item;
2562 }
2563
2564 static void item_has_queued_mails(FolderItem *item, gpointer data)
2565 {
2566         gboolean *result = (gboolean *)data;
2567         if (*result == TRUE)
2568                 return;
2569         if (folder_has_parent_of_type(item, F_QUEUE)) {
2570                 if (item->total_msgs == 0)
2571                         return;
2572                 else {
2573                         GSList *msglist = folder_item_get_msg_list(item);
2574                         GSList *cur;
2575                         for (cur = msglist; cur; cur = cur->next) {
2576                                 MsgInfo *msginfo = (MsgInfo *)cur->data;
2577                                 if (!MSG_IS_DELETED(msginfo->flags) &&
2578                                     !MSG_IS_LOCKED(msginfo->flags)) {
2579                                         *result = TRUE;
2580                                         break;
2581                                 }
2582                         }
2583                         procmsg_msg_list_free(msglist);
2584                 }
2585         }
2586 }
2587
2588 gboolean procmsg_have_queued_mails_fast (void)
2589 {
2590         gboolean result = FALSE;
2591         folder_func_to_all_folders(item_has_queued_mails, &result);
2592         return result;
2593 }
2594
2595 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2596 {
2597         gboolean *result = (gboolean *)data;
2598         if (*result == TRUE)
2599                 return;
2600         if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2601                 *result = TRUE;
2602 }
2603
2604 gboolean procmsg_have_trashed_mails_fast (void)
2605 {
2606         gboolean result = FALSE;
2607         folder_func_to_all_folders(item_has_trashed_mails, &result);
2608         return result;
2609 }
2610
2611 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2612 {
2613         GSList *cur = NULL;
2614         gchar *tags = NULL;
2615         
2616         if (!msginfo)
2617                 return NULL;
2618
2619         if (msginfo->tags == NULL)
2620                 return NULL;
2621         for (cur = msginfo->tags; cur; cur = cur->next) {
2622                 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2623                 if (!tag)
2624                         continue;
2625                 if (!tags)
2626                         tags = g_strdup(tag);
2627                 else {
2628                         int olen = strlen(tags);
2629                         int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2630                         tags = g_realloc(tags, nlen+1);
2631                         if (!tags)
2632                                 return NULL;
2633                         strcpy(tags+olen, ", ");
2634                         strcpy(tags+olen+2, tag);
2635                         tags[nlen]='\0';
2636                 }
2637         }
2638         return tags;
2639 }
2640
2641 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2642 {
2643         GSList changed;
2644
2645         if (id == 0)
2646                 return;
2647
2648         if (!set) {
2649                 msginfo->tags = g_slist_remove(
2650                                         msginfo->tags,
2651                                         GINT_TO_POINTER(id));
2652                 changed.data = GINT_TO_POINTER(id);
2653                 changed.next = NULL;
2654                 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2655         } else {
2656                 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2657                         msginfo->tags = g_slist_append(
2658                                         msginfo->tags,
2659                                         GINT_TO_POINTER(id));
2660                 }
2661                 changed.data = GINT_TO_POINTER(id);
2662                 changed.next = NULL;
2663                 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2664         }
2665         
2666 }
2667
2668 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2669 {
2670         GSList *unset = msginfo->tags;
2671         msginfo->tags = NULL;
2672         folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2673         g_slist_free(unset);
2674 }