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