2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
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 2 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
45 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
47 GHashTable *msg_table;
49 if (mlist == NULL) return NULL;
51 msg_table = g_hash_table_new(NULL, g_direct_equal);
52 procmsg_msg_hash_table_append(msg_table, mlist);
57 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
62 if (msg_table == NULL || mlist == NULL) return;
64 for (cur = mlist; cur != NULL; cur = cur->next) {
65 msginfo = (MsgInfo *)cur->data;
67 g_hash_table_insert(msg_table,
68 GUINT_TO_POINTER(msginfo->msgnum),
73 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
75 GHashTable *msg_table;
79 if (mlist == NULL) return NULL;
81 msg_table = g_hash_table_new(NULL, g_direct_equal);
83 for (cur = mlist; cur != NULL; cur = cur->next) {
84 msginfo = (MsgInfo *)cur->data;
85 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
91 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
97 for (cur = mlist; cur != NULL; cur = cur->next) {
98 msginfo = (MsgInfo *)cur->data;
99 if (msginfo && msginfo->msgnum > last)
100 last = msginfo->msgnum;
106 void procmsg_msg_list_free(GSList *mlist)
111 for (cur = mlist; cur != NULL; cur = cur->next) {
112 msginfo = (MsgInfo *)cur->data;
113 procmsg_msginfo_free(msginfo);
127 static gboolean procmsg_ignore_node(GNode *node, gpointer data)
129 MsgInfo *msginfo = (MsgInfo *)node->data;
131 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
132 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
137 /* CLAWS subject threading:
139 in the first round it inserts subject lines in a
140 relation (subject <-> node)
142 the second round finishes the threads by attaching
143 matching subject lines to the one found in the
144 relation. will use the oldest node with the same
145 subject that is not more then thread_by_subject_max_age
146 days old (see subject_relation_lookup)
149 static void subject_relation_insert(GRelation *relation, GNode *node)
154 g_return_if_fail(relation != NULL);
155 g_return_if_fail(node != NULL);
156 msginfo = (MsgInfo *) node->data;
157 g_return_if_fail(msginfo != NULL);
159 subject = msginfo->subject;
162 subject += subject_get_prefix_length(subject);
164 g_relation_insert(relation, subject, node);
167 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
174 g_return_val_if_fail(relation != NULL, NULL);
176 subject = msginfo->subject;
179 prefix_length = subject_get_prefix_length(subject);
180 if (prefix_length <= 0)
182 subject += prefix_length;
184 tuples = g_relation_select(relation, subject, 0);
188 if (tuples->len > 0) {
190 GNode *relation_node;
191 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
194 /* check all nodes with the same subject to find the best parent */
195 for (i = 0; i < tuples->len; i++) {
196 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
197 relation_msginfo = (MsgInfo *) relation_node->data;
200 /* best node should be the oldest in the found nodes */
201 /* parent node must not be older then msginfo */
202 if ((relation_msginfo->date_t < msginfo->date_t) &&
203 ((best_msginfo == NULL) ||
204 (best_msginfo->date_t > relation_msginfo->date_t)))
207 /* parent node must not be more then thread_by_subject_max_age
208 days older then msginfo */
209 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
210 prefs_common.thread_by_subject_max_age * 3600 * 24)
213 /* can add new tests for all matching
214 nodes found by subject */
217 node = relation_node;
218 best_msginfo = relation_msginfo;
223 g_tuples_destroy(tuples);
227 /* return the reversed thread tree */
228 GNode *procmsg_get_thread_tree(GSList *mlist)
230 GNode *root, *parent, *node, *next, *last;
231 GNode *prev; /* CLAWS */
232 GHashTable *msgid_table;
233 GRelation *subject_relation;
236 const gchar *subject;
238 root = g_node_new(NULL);
239 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
240 subject_relation = g_relation_new(2);
241 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
243 for (; mlist != NULL; mlist = mlist->next) {
244 msginfo = (MsgInfo *)mlist->data;
247 if (msginfo->inreplyto) {
248 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
249 if (parent == NULL) {
253 node = g_node_insert_data_before
254 (parent, parent == root ? parent->children : NULL,
256 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
257 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
259 /* CLAWS: add subject to relation (without prefix) */
260 if (prefs_common.thread_by_subject) {
261 subject_relation_insert(subject_relation, node);
265 /* complete the unfinished threads */
266 for (node = root->children; node != NULL; ) {
267 prev = node->prev; /* CLAWS: need the last node */
270 msginfo = (MsgInfo *)node->data;
271 if (msginfo->inreplyto) {
272 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
273 /* node should not be the parent, and node should not
274 be an ancestor of parent (circular reference) */
275 if (parent && parent != node &&
276 !g_node_is_ancestor(node, parent)) {
279 (parent, parent->children, node);
282 last = (next == NULL) ? prev : node;
286 if (prefs_common.thread_by_subject) {
287 for (node = last; node && node != NULL;) {
289 msginfo = (MsgInfo *) node->data;
291 /* may not parentize if parent was delivered after childs */
292 if (subject != msginfo->subject)
293 parent = subject_relation_lookup(subject_relation, msginfo);
297 /* the node may already be threaded by IN-REPLY-TO, so go up in the tree to
298 find the parent node */
299 if (parent != NULL) {
300 if (g_node_is_ancestor(node, parent))
308 g_node_append(parent, node);
315 g_relation_destroy(subject_relation);
316 g_hash_table_destroy(msgid_table);
321 void procmsg_move_messages(GSList *mlist)
323 GSList *cur, *movelist = NULL;
325 FolderItem *dest = NULL;
329 folder_item_update_freeze();
331 for (cur = mlist; cur != NULL; cur = cur->next) {
332 msginfo = (MsgInfo *)cur->data;
334 dest = msginfo->to_folder;
335 movelist = g_slist_append(movelist, msginfo);
336 } else if (dest == msginfo->to_folder) {
337 movelist = g_slist_append(movelist, msginfo);
339 folder_item_move_msgs(dest, movelist);
340 g_slist_free(movelist);
342 dest = msginfo->to_folder;
343 movelist = g_slist_append(movelist, msginfo);
345 procmsg_msginfo_set_to_folder(msginfo, NULL);
349 folder_item_move_msgs(dest, movelist);
350 g_slist_free(movelist);
353 folder_item_update_thaw();
356 void procmsg_copy_messages(GSList *mlist)
358 GSList *cur, *copylist = NULL;
360 FolderItem *dest = NULL;
364 folder_item_update_freeze();
366 for (cur = mlist; cur != NULL; cur = cur->next) {
367 msginfo = (MsgInfo *)cur->data;
369 dest = msginfo->to_folder;
370 copylist = g_slist_append(copylist, msginfo);
371 } else if (dest == msginfo->to_folder) {
372 copylist = g_slist_append(copylist, msginfo);
374 folder_item_copy_msgs(dest, copylist);
375 g_slist_free(copylist);
377 dest = msginfo->to_folder;
378 copylist = g_slist_append(copylist, msginfo);
380 procmsg_msginfo_set_to_folder(msginfo, NULL);
384 folder_item_copy_msgs(dest, copylist);
385 g_slist_free(copylist);
388 folder_item_update_thaw();
391 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
395 g_return_val_if_fail(msginfo != NULL, NULL);
397 if (msginfo->plaintext_file)
398 file = g_strdup(msginfo->plaintext_file);
400 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
406 gchar *procmsg_get_message_file(MsgInfo *msginfo)
408 gchar *filename = NULL;
410 g_return_val_if_fail(msginfo != NULL, NULL);
412 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
414 g_warning("can't fetch message %d\n", msginfo->msgnum);
419 GSList *procmsg_get_message_file_list(GSList *mlist)
421 GSList *file_list = NULL;
423 MsgFileInfo *fileinfo;
426 while (mlist != NULL) {
427 msginfo = (MsgInfo *)mlist->data;
428 file = procmsg_get_message_file(msginfo);
430 procmsg_message_file_list_free(file_list);
433 fileinfo = g_new(MsgFileInfo, 1);
434 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
435 fileinfo->file = file;
436 fileinfo->flags = g_new(MsgFlags, 1);
437 *fileinfo->flags = msginfo->flags;
438 file_list = g_slist_prepend(file_list, fileinfo);
442 file_list = g_slist_reverse(file_list);
447 void procmsg_message_file_list_free(MsgInfoList *file_list)
450 MsgFileInfo *fileinfo;
452 for (cur = file_list; cur != NULL; cur = cur->next) {
453 fileinfo = (MsgFileInfo *)cur->data;
454 procmsg_msginfo_free(fileinfo->msginfo);
455 g_free(fileinfo->file);
456 g_free(fileinfo->flags);
460 g_slist_free(file_list);
463 FILE *procmsg_open_message(MsgInfo *msginfo)
468 g_return_val_if_fail(msginfo != NULL, NULL);
470 file = procmsg_get_message_file_path(msginfo);
471 g_return_val_if_fail(file != NULL, NULL);
473 if (!is_file_exist(file)) {
475 file = procmsg_get_message_file(msginfo);
476 g_return_val_if_fail(file != NULL, NULL);
479 if ((fp = fopen(file, "rb")) == NULL) {
480 FILE_OP_ERROR(file, "fopen");
487 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
490 while (fgets(buf, sizeof(buf), fp) != NULL)
491 if (buf[0] == '\r' || buf[0] == '\n') break;
497 gboolean procmsg_msg_exist(MsgInfo *msginfo)
502 if (!msginfo) return FALSE;
504 path = folder_item_get_path(msginfo->folder);
506 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
512 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
513 PrefsFilterType type)
515 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
516 {"X-ML-Name:", NULL, TRUE},
517 {"X-List:", NULL, TRUE},
518 {"X-Mailing-list:", NULL, TRUE},
519 {"List-Id:", NULL, TRUE},
520 {"X-Sequence:", NULL, TRUE},
521 {NULL, NULL, FALSE}};
527 H_X_MAILING_LIST = 3,
534 g_return_if_fail(msginfo != NULL);
535 g_return_if_fail(header != NULL);
536 g_return_if_fail(key != NULL);
545 if ((fp = procmsg_open_message(msginfo)) == NULL)
547 procheader_get_header_fields(fp, hentry);
550 #define SET_FILTER_KEY(hstr, idx) \
552 *header = g_strdup(hstr); \
553 *key = hentry[idx].body; \
554 hentry[idx].body = NULL; \
557 if (hentry[H_X_BEENTHERE].body != NULL) {
558 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
559 } else if (hentry[H_X_ML_NAME].body != NULL) {
560 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
561 } else if (hentry[H_X_LIST].body != NULL) {
562 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
563 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
564 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
565 } else if (hentry[H_LIST_ID].body != NULL) {
566 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
567 extract_list_id_str(*key);
568 } else if (hentry[H_X_SEQUENCE].body != NULL) {
571 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
574 while (*p != '\0' && !isspace(*p)) p++;
575 while (isspace(*p)) p++;
582 } else if (msginfo->subject) {
583 *header = g_strdup("subject");
584 *key = g_strdup(msginfo->subject);
587 #undef SET_FILTER_KEY
589 g_free(hentry[H_X_BEENTHERE].body);
590 hentry[H_X_BEENTHERE].body = NULL;
591 g_free(hentry[H_X_ML_NAME].body);
592 hentry[H_X_ML_NAME].body = NULL;
593 g_free(hentry[H_X_LIST].body);
594 hentry[H_X_LIST].body = NULL;
595 g_free(hentry[H_X_MAILING_LIST].body);
596 hentry[H_X_MAILING_LIST].body = NULL;
597 g_free(hentry[H_LIST_ID].body);
598 hentry[H_LIST_ID].body = NULL;
602 *header = g_strdup("from");
603 *key = g_strdup(msginfo->from);
606 *header = g_strdup("to");
607 *key = g_strdup(msginfo->to);
609 case FILTER_BY_SUBJECT:
610 *header = g_strdup("subject");
611 *key = g_strdup(msginfo->subject);
618 void procmsg_empty_trash(void)
623 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
624 trash = FOLDER(cur->data)->trash;
625 if (trash && trash->total_msgs > 0)
626 folder_item_remove_all_msg(trash);
631 *\brief Send messages in queue
633 *\param queue Queue folder to process
634 *\param save_msgs Unused
636 *\return Number of messages sent, negative if an error occurred
637 * positive if no error occurred
639 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
641 gint ret = 1, count = 0;
645 queue = folder_get_default_queue();
646 g_return_val_if_fail(queue != NULL, -1);
648 folder_item_scan(queue);
649 list = folder_item_get_msg_list(queue);
651 for (elem = list; elem != NULL; elem = elem->next) {
655 msginfo = (MsgInfo *)(elem->data);
656 if (!MSG_IS_LOCKED(msginfo->flags)) {
657 file = folder_item_fetch_msg(queue, msginfo->msgnum);
659 if (procmsg_send_message_queue(file) < 0) {
660 g_warning("Sending queued message %d failed.\n",
665 * We save in procmsg_send_message_queue because
666 * we need the destination folder from the queue
670 procmsg_save_to_outbox
671 (queue->folder->outbox,
675 folder_item_remove_msg(queue, msginfo->msgnum);
680 /* FIXME: supposedly if only one message is locked, and queue
681 * is being flushed, the following free says something like
682 * "freeing msg ## in folder (nil)". */
683 procmsg_msginfo_free(msginfo);
689 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
694 if ((fp = fopen(in, "rb")) == NULL) {
695 FILE_OP_ERROR(in, "fopen");
698 if ((outfp = fopen(out, "wb")) == NULL) {
699 FILE_OP_ERROR(out, "fopen");
703 while (fgets(buf, sizeof(buf), fp) != NULL)
704 if (buf[0] == '\r' || buf[0] == '\n') break;
705 while (fgets(buf, sizeof(buf), fp) != NULL)
712 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
716 MsgInfo *msginfo, *tmp_msginfo;
717 MsgFlags flag = {0, 0};
719 debug_print("saving sent message...\n");
722 outbox = folder_get_default_outbox();
723 g_return_val_if_fail(outbox != NULL, -1);
725 /* remove queueing headers */
727 gchar tmp[MAXPATHLEN + 1];
729 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
730 get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
732 if (procmsg_remove_special_headers(file, tmp) !=0)
735 folder_item_scan(outbox);
736 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
737 g_warning("can't save message\n");
742 folder_item_scan(outbox);
743 if ((num = folder_item_add_msg
744 (outbox, file, &flag, FALSE)) < 0) {
745 g_warning("can't save message\n");
750 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
751 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
752 if (msginfo != NULL) {
753 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
754 procmsg_msginfo_free(msginfo); /* refcnt-- */
755 /* tmp_msginfo == msginfo */
756 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
757 msginfo->returnreceiptto)) {
758 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
759 procmsg_msginfo_free(msginfo); /* refcnt-- */
762 folder_item_update(outbox, TRUE);
767 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
769 static const gchar *def_cmd = "lpr %s";
776 g_return_if_fail(msginfo);
778 if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
779 g_warning("Can't get text part\n");
783 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
784 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
786 if ((prfp = fopen(prtmp, "wb")) == NULL) {
787 FILE_OP_ERROR(prtmp, "fopen");
793 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
794 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
795 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
796 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
797 if (msginfo->newsgroups)
798 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
799 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
802 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
808 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
810 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
813 g_warning("Print command line is invalid: `%s'\n",
815 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
821 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
825 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
832 MsgInfo *procmsg_msginfo_new(void)
836 newmsginfo = g_new0(MsgInfo, 1);
837 newmsginfo->refcnt = 1;
842 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
846 if (msginfo == NULL) return NULL;
848 newmsginfo = g_new0(MsgInfo, 1);
850 newmsginfo->refcnt = 1;
852 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
853 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
854 g_strdup(msginfo->mmb) : NULL
878 MEMBDUP(dispositionnotificationto);
879 MEMBDUP(returnreceiptto);
883 MEMBCOPY(threadscore);
888 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
890 MsgInfo *full_msginfo;
893 if (msginfo == NULL) return NULL;
895 file = procmsg_get_message_file(msginfo);
897 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
901 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
903 if (!full_msginfo) return NULL;
905 /* CLAWS: make sure we add the missing members; see:
906 * procheader.c::procheader_get_headernames() */
908 msginfo->xface = g_strdup(full_msginfo->xface);
909 if (!msginfo->dispositionnotificationto)
910 msginfo->dispositionnotificationto =
911 g_strdup(full_msginfo->dispositionnotificationto);
912 if (!msginfo->returnreceiptto)
913 msginfo->returnreceiptto = g_strdup
914 (full_msginfo->returnreceiptto);
915 procmsg_msginfo_free(full_msginfo);
917 return procmsg_msginfo_new_ref(msginfo);
920 void procmsg_msginfo_free(MsgInfo *msginfo)
922 if (msginfo == NULL) return;
925 if (msginfo->refcnt > 0)
928 debug_print("freeing msginfo %d in %s\n", msginfo->msgnum, msginfo->folder ? msginfo->folder->path : "(nil)");
930 if (msginfo->to_folder) {
931 msginfo->to_folder->op_count--;
932 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
935 g_free(msginfo->fromspace);
936 g_free(msginfo->references);
937 g_free(msginfo->returnreceiptto);
938 g_free(msginfo->dispositionnotificationto);
939 g_free(msginfo->xface);
941 g_free(msginfo->fromname);
943 g_free(msginfo->date);
944 g_free(msginfo->from);
947 g_free(msginfo->newsgroups);
948 g_free(msginfo->subject);
949 g_free(msginfo->msgid);
950 g_free(msginfo->inreplyto);
951 g_free(msginfo->xref);
956 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
960 memusage += sizeof(MsgInfo);
961 if (msginfo->fromname)
962 memusage += strlen(msginfo->fromname);
964 memusage += strlen(msginfo->date);
966 memusage += strlen(msginfo->from);
968 memusage += strlen(msginfo->to);
970 memusage += strlen(msginfo->cc);
971 if (msginfo->newsgroups)
972 memusage += strlen(msginfo->newsgroups);
973 if (msginfo->subject)
974 memusage += strlen(msginfo->subject);
976 memusage += strlen(msginfo->msgid);
977 if (msginfo->inreplyto)
978 memusage += strlen(msginfo->inreplyto);
980 memusage += strlen(msginfo->xface);
981 if (msginfo->dispositionnotificationto)
982 memusage += strlen(msginfo->dispositionnotificationto);
983 if (msginfo->returnreceiptto)
984 memusage += strlen(msginfo->returnreceiptto);
985 if (msginfo->references)
986 memusage += strlen(msginfo->references);
987 if (msginfo->fromspace)
988 memusage += strlen(msginfo->fromspace);
993 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
995 const MsgInfo *msginfo1 = a;
996 const MsgInfo *msginfo2 = b;
1003 return msginfo1->msgnum - msginfo2->msgnum;
1012 Q_MAIL_ACCOUNT_ID = 4,
1013 Q_NEWS_ACCOUNT_ID = 5,
1014 Q_SAVE_COPY_FOLDER = 6,
1015 Q_REPLY_MESSAGE_ID = 7,
1016 Q_FWD_MESSAGE_ID = 8
1019 gint procmsg_send_message_queue(const gchar *file)
1021 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1022 {"SSV:", NULL, FALSE},
1023 {"R:", NULL, FALSE},
1024 {"NG:", NULL, FALSE},
1025 {"MAID:", NULL, FALSE},
1026 {"NAID:", NULL, FALSE},
1027 {"SCF:", NULL, FALSE},
1028 {"RMID:", NULL, FALSE},
1029 {"FMID:", NULL, FALSE},
1030 {NULL, NULL, FALSE}};
1033 gint mailval = 0, newsval = 0;
1035 gchar *smtpserver = NULL;
1036 GSList *to_list = NULL;
1037 GSList *newsgroup_list = NULL;
1038 gchar *savecopyfolder = NULL;
1039 gchar *replymessageid = NULL;
1040 gchar *fwdmessageid = NULL;
1041 gchar buf[BUFFSIZE];
1043 PrefsAccount *mailac = NULL, *newsac = NULL;
1046 g_return_val_if_fail(file != NULL, -1);
1048 if ((fp = fopen(file, "rb")) == NULL) {
1049 FILE_OP_ERROR(file, "fopen");
1053 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1055 gchar *p = buf + strlen(qentry[hnum].name);
1059 if (!from) from = g_strdup(p);
1062 if (!smtpserver) smtpserver = g_strdup(p);
1065 to_list = address_list_append(to_list, p);
1068 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1070 case Q_MAIL_ACCOUNT_ID:
1071 mailac = account_find_from_id(atoi(p));
1073 case Q_NEWS_ACCOUNT_ID:
1074 newsac = account_find_from_id(atoi(p));
1076 case Q_SAVE_COPY_FOLDER:
1077 if (!savecopyfolder) savecopyfolder = g_strdup(p);
1079 case Q_REPLY_MESSAGE_ID:
1080 if (!replymessageid) replymessageid = g_strdup(p);
1082 case Q_FWD_MESSAGE_ID:
1083 if (!fwdmessageid) fwdmessageid = g_strdup(p);
1087 filepos = ftell(fp);
1090 debug_print("Sending message by mail\n");
1092 g_warning("Queued message header is broken.\n");
1094 } else if (mailac && mailac->use_mail_command &&
1095 mailac->mail_command && (* mailac->mail_command)) {
1096 mailval = send_message_local(mailac->mail_command, fp);
1100 mailac = account_find_from_smtp_server(from, smtpserver);
1102 g_warning("Account not found. "
1103 "Using current account...\n");
1104 mailac = cur_account;
1109 mailval = send_message_smtp(mailac, to_list, fp);
1111 PrefsAccount tmp_ac;
1113 g_warning("Account not found.\n");
1115 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1116 tmp_ac.address = from;
1117 tmp_ac.smtp_server = smtpserver;
1118 tmp_ac.smtpport = SMTP_PORT;
1119 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1124 fseek(fp, filepos, SEEK_SET);
1125 if (newsgroup_list && (newsval == 0)) {
1130 /* write to temporary file */
1131 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1132 G_DIR_SEPARATOR, (gint)file);
1133 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1134 FILE_OP_ERROR(tmp, "fopen");
1136 alertpanel_error(_("Could not create temporary file for news sending."));
1138 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1139 FILE_OP_ERROR(tmp, "chmod");
1140 g_warning("can't change file mode\n");
1143 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1144 if (fputs(buf, tmpfp) == EOF) {
1145 FILE_OP_ERROR(tmp, "fputs");
1147 alertpanel_error(_("Error when writing temporary file for news sending."));
1153 debug_print("Sending message by news\n");
1155 folder = FOLDER(newsac->folder);
1157 newsval = news_post(folder, tmp);
1159 alertpanel_error(_("Error occurred while posting the message to %s ."),
1160 newsac->nntp_server);
1168 slist_free_strings(to_list);
1169 g_slist_free(to_list);
1170 slist_free_strings(newsgroup_list);
1171 g_slist_free(newsgroup_list);
1176 /* save message to outbox */
1177 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1180 debug_print("saving sent message...\n");
1182 outbox = folder_find_item_from_identifier(savecopyfolder);
1184 outbox = folder_get_default_outbox();
1186 procmsg_save_to_outbox(outbox, file, TRUE);
1189 if (replymessageid != NULL || fwdmessageid != NULL) {
1193 if (replymessageid != NULL)
1194 tokens = g_strsplit(replymessageid, "\x7f", 0);
1196 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1197 item = folder_find_item_from_identifier(tokens[0]);
1199 /* check if queued message has valid folder and message id */
1200 if (item != NULL && tokens[2] != NULL) {
1203 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1205 /* check if referring message exists and has a message id */
1206 if ((msginfo != NULL) &&
1207 (msginfo->msgid != NULL) &&
1208 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1209 procmsg_msginfo_free(msginfo);
1213 if (msginfo == NULL) {
1214 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1217 if (msginfo != NULL) {
1218 if (replymessageid != NULL) {
1219 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1220 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1223 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1224 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1226 procmsg_msginfo_free(msginfo);
1232 g_free(savecopyfolder);
1233 g_free(replymessageid);
1234 g_free(fwdmessageid);
1236 return (newsval != 0 ? newsval : mailval);
1239 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1241 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1244 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1248 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1253 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1254 item->unread_msgs++;
1255 if (procmsg_msg_has_marked_parent(msginfo))
1256 item->unreadmarked_msgs++;
1259 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1260 item->unread_msgs--;
1261 if (procmsg_msg_has_marked_parent(msginfo))
1262 item->unreadmarked_msgs--;
1266 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1267 procmsg_update_unread_children(msginfo, TRUE);
1270 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1271 procmsg_update_unread_children(msginfo, FALSE);
1275 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1278 MsgInfoUpdate msginfo_update;
1279 MsgPermFlags perm_flags_new, perm_flags_old;
1281 g_return_if_fail(msginfo != NULL);
1282 item = msginfo->folder;
1283 g_return_if_fail(item != NULL);
1285 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1287 /* Perm Flags handling */
1288 perm_flags_old = msginfo->flags.perm_flags;
1289 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1290 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1291 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1294 if (perm_flags_old != perm_flags_new) {
1295 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1297 update_folder_msg_counts(item, msginfo, perm_flags_old);
1299 msginfo_update.msginfo = msginfo;
1300 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1301 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1302 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1305 /* Tmp flags hanlding */
1306 msginfo->flags.tmp_flags |= tmp_flags;
1309 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1312 MsgInfoUpdate msginfo_update;
1313 MsgPermFlags perm_flags_new, perm_flags_old;
1315 g_return_if_fail(msginfo != NULL);
1316 item = msginfo->folder;
1317 g_return_if_fail(item != NULL);
1319 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1321 /* Perm Flags handling */
1322 perm_flags_old = msginfo->flags.perm_flags;
1323 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1325 if (perm_flags_old != perm_flags_new) {
1326 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1328 update_folder_msg_counts(item, msginfo, perm_flags_old);
1330 msginfo_update.msginfo = msginfo;
1331 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1332 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1333 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1336 /* Tmp flags hanlding */
1337 msginfo->flags.tmp_flags &= ~tmp_flags;
1341 *\brief check for flags (e.g. mark) in prior msgs of current thread
1343 *\param info Current message
1344 *\param perm_flags Flags to be checked
1345 *\param parentmsgs Hash of prior msgs to avoid loops
1347 *\return gboolean TRUE if perm_flags are found
1349 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1350 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1354 g_return_val_if_fail(info != NULL, FALSE);
1356 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1357 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1359 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1360 procmsg_msginfo_free(tmp);
1362 } else if (tmp != NULL) {
1365 if (g_hash_table_lookup(parentmsgs, info)) {
1366 debug_print("loop detected: %s%c%d\n",
1367 folder_item_get_path(info->folder),
1368 G_DIR_SEPARATOR, info->msgnum);
1371 g_hash_table_insert(parentmsgs, info, "1");
1372 result = procmsg_msg_has_flagged_parent_real(
1373 tmp, perm_flags, parentmsgs);
1375 procmsg_msginfo_free(tmp);
1385 *\brief Callback for cleaning up hash of parentmsgs
1387 gboolean parentmsgs_hash_remove(gpointer key,
1395 *\brief Set up list of parentmsgs
1396 * See procmsg_msg_has_flagged_parent_real()
1398 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1401 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1403 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1404 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1405 g_hash_table_destroy(parentmsgs);
1410 *\brief Check if msgs prior in thread are marked
1411 * See procmsg_msg_has_flagged_parent_real()
1413 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1415 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1419 GSList *procmsg_find_children_func(MsgInfo *info,
1420 GSList *children, GSList *all)
1424 g_return_val_if_fail(info!=NULL, children);
1425 if (info->msgid == NULL)
1428 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1429 MsgInfo *tmp = (MsgInfo *)cur->data;
1430 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1431 /* Check if message is already in the list */
1432 if ((children == NULL) ||
1433 (g_slist_index(children, tmp) == -1)) {
1434 children = g_slist_prepend(children,
1435 procmsg_msginfo_new_ref(tmp));
1436 children = procmsg_find_children_func(tmp,
1445 GSList *procmsg_find_children (MsgInfo *info)
1450 g_return_val_if_fail(info!=NULL, NULL);
1451 all = folder_item_get_msg_list(info->folder);
1452 children = procmsg_find_children_func(info, NULL, all);
1453 if (children != NULL) {
1454 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1455 /* this will not free the used pointers
1456 created with procmsg_msginfo_new_ref */
1457 procmsg_msginfo_free((MsgInfo *)cur->data);
1465 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1467 GSList *children = procmsg_find_children(info);
1469 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1470 MsgInfo *tmp = (MsgInfo *)cur->data;
1471 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1473 info->folder->unreadmarked_msgs++;
1475 info->folder->unreadmarked_msgs--;
1476 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1478 procmsg_msginfo_free(tmp);
1480 g_slist_free(children);
1484 * Set the destination folder for a copy or move operation
1486 * \param msginfo The message which's destination folder is changed
1487 * \param to_folder The destination folder for the operation
1489 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1491 if(msginfo->to_folder != NULL) {
1492 msginfo->to_folder->op_count--;
1493 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1495 msginfo->to_folder = to_folder;
1496 if(to_folder != NULL) {
1497 to_folder->op_count++;
1498 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1503 * Apply filtering actions to the msginfo
1505 * \param msginfo The MsgInfo describing the message that should be filtered
1506 * \return TRUE if the message was moved and MsgInfo is now invalid,
1509 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1511 MailFilteringData mail_filtering_data;
1513 mail_filtering_data.msginfo = msginfo;
1514 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1517 /* filter if enabled in prefs or move to inbox if not */
1518 if((global_processing != NULL) &&
1519 filter_message_by_msginfo(global_processing, msginfo))