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