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