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 /* CLAWS subject threading:
129 in the first round it inserts subject lines in a
130 relation (subject <-> node)
132 the second round finishes the threads by attaching
133 matching subject lines to the one found in the
134 relation. will use the oldest node with the same
135 subject that is not more then thread_by_subject_max_age
136 days old (see subject_relation_lookup)
139 static void subject_relation_insert(GRelation *relation, GNode *node)
144 g_return_if_fail(relation != NULL);
145 g_return_if_fail(node != NULL);
146 msginfo = (MsgInfo *) node->data;
147 g_return_if_fail(msginfo != NULL);
149 subject = msginfo->subject;
152 subject += subject_get_prefix_length(subject);
154 g_relation_insert(relation, subject, node);
157 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
164 g_return_val_if_fail(relation != NULL, NULL);
166 subject = msginfo->subject;
169 prefix_length = subject_get_prefix_length(subject);
170 if (prefix_length <= 0)
172 subject += prefix_length;
174 tuples = g_relation_select(relation, subject, 0);
178 if (tuples->len > 0) {
180 GNode *relation_node;
181 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
184 /* check all nodes with the same subject to find the best parent */
185 for (i = 0; i < tuples->len; i++) {
186 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
187 relation_msginfo = (MsgInfo *) relation_node->data;
190 /* best node should be the oldest in the found nodes */
191 /* parent node must not be older then msginfo */
192 if ((relation_msginfo->date_t < msginfo->date_t) &&
193 ((best_msginfo == NULL) ||
194 (best_msginfo->date_t > relation_msginfo->date_t)))
197 /* parent node must not be more then thread_by_subject_max_age
198 days older then msginfo */
199 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
200 prefs_common.thread_by_subject_max_age * 3600 * 24)
203 /* can add new tests for all matching
204 nodes found by subject */
207 node = relation_node;
208 best_msginfo = relation_msginfo;
213 g_tuples_destroy(tuples);
217 /* return the reversed thread tree */
218 GNode *procmsg_get_thread_tree(GSList *mlist)
220 GNode *root, *parent, *node, *next, *last;
221 GNode *prev; /* CLAWS */
222 GHashTable *msgid_table;
223 GRelation *subject_relation;
226 const gchar *subject;
228 root = g_node_new(NULL);
229 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
230 subject_relation = g_relation_new(2);
231 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
233 for (; mlist != NULL; mlist = mlist->next) {
234 msginfo = (MsgInfo *)mlist->data;
237 if (msginfo->inreplyto) {
238 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
239 if (parent == NULL) {
243 node = g_node_insert_data_before
244 (parent, parent == root ? parent->children : NULL,
246 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
247 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
249 /* CLAWS: add subject to relation (without prefix) */
250 if (prefs_common.thread_by_subject) {
251 subject_relation_insert(subject_relation, node);
255 /* complete the unfinished threads */
256 for (node = root->children; node != NULL; ) {
257 prev = node->prev; /* CLAWS: need the last node */
260 msginfo = (MsgInfo *)node->data;
261 if (msginfo->inreplyto) {
262 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
263 /* node should not be the parent, and node should not
264 be an ancestor of parent (circular reference) */
265 if (parent && parent != node &&
266 !g_node_is_ancestor(node, parent)) {
269 (parent, parent->children, node);
272 last = (next == NULL) ? prev : node;
276 if (prefs_common.thread_by_subject) {
277 for (node = last; node && node != NULL;) {
279 msginfo = (MsgInfo *) node->data;
281 /* may not parentize if parent was delivered after childs */
282 if (subject != msginfo->subject)
283 parent = subject_relation_lookup(subject_relation, msginfo);
287 /* the node may already be threaded by IN-REPLY-TO, so go up in the tree to
288 find the parent node */
289 if (parent != NULL) {
290 if (g_node_is_ancestor(node, parent))
298 g_node_append(parent, node);
305 g_relation_destroy(subject_relation);
306 g_hash_table_destroy(msgid_table);
311 void procmsg_move_messages(GSList *mlist)
313 GSList *cur, *movelist = NULL;
315 FolderItem *dest = NULL;
319 folder_item_update_freeze();
321 for (cur = mlist; cur != NULL; cur = cur->next) {
322 msginfo = (MsgInfo *)cur->data;
324 dest = msginfo->to_folder;
325 movelist = g_slist_append(movelist, msginfo);
326 } else if (dest == msginfo->to_folder) {
327 movelist = g_slist_append(movelist, msginfo);
329 folder_item_move_msgs(dest, movelist);
330 g_slist_free(movelist);
332 dest = msginfo->to_folder;
333 movelist = g_slist_append(movelist, msginfo);
335 procmsg_msginfo_set_to_folder(msginfo, NULL);
339 folder_item_move_msgs(dest, movelist);
340 g_slist_free(movelist);
343 folder_item_update_thaw();
346 void procmsg_copy_messages(GSList *mlist)
348 GSList *cur, *copylist = NULL;
350 FolderItem *dest = NULL;
354 folder_item_update_freeze();
356 for (cur = mlist; cur != NULL; cur = cur->next) {
357 msginfo = (MsgInfo *)cur->data;
359 dest = msginfo->to_folder;
360 copylist = g_slist_append(copylist, msginfo);
361 } else if (dest == msginfo->to_folder) {
362 copylist = g_slist_append(copylist, msginfo);
364 folder_item_copy_msgs(dest, copylist);
365 g_slist_free(copylist);
367 dest = msginfo->to_folder;
368 copylist = g_slist_append(copylist, msginfo);
370 procmsg_msginfo_set_to_folder(msginfo, NULL);
374 folder_item_copy_msgs(dest, copylist);
375 g_slist_free(copylist);
378 folder_item_update_thaw();
381 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
385 g_return_val_if_fail(msginfo != NULL, NULL);
387 if (msginfo->plaintext_file)
388 file = g_strdup(msginfo->plaintext_file);
390 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
396 gchar *procmsg_get_message_file(MsgInfo *msginfo)
398 gchar *filename = NULL;
400 g_return_val_if_fail(msginfo != NULL, NULL);
402 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
404 debug_print("can't fetch message %d\n", msginfo->msgnum);
409 GSList *procmsg_get_message_file_list(GSList *mlist)
411 GSList *file_list = NULL;
413 MsgFileInfo *fileinfo;
416 while (mlist != NULL) {
417 msginfo = (MsgInfo *)mlist->data;
418 file = procmsg_get_message_file(msginfo);
420 procmsg_message_file_list_free(file_list);
423 fileinfo = g_new(MsgFileInfo, 1);
424 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
425 fileinfo->file = file;
426 fileinfo->flags = g_new(MsgFlags, 1);
427 *fileinfo->flags = msginfo->flags;
428 file_list = g_slist_prepend(file_list, fileinfo);
432 file_list = g_slist_reverse(file_list);
437 void procmsg_message_file_list_free(MsgInfoList *file_list)
440 MsgFileInfo *fileinfo;
442 for (cur = file_list; cur != NULL; cur = cur->next) {
443 fileinfo = (MsgFileInfo *)cur->data;
444 procmsg_msginfo_free(fileinfo->msginfo);
445 g_free(fileinfo->file);
446 g_free(fileinfo->flags);
450 g_slist_free(file_list);
453 FILE *procmsg_open_message(MsgInfo *msginfo)
458 g_return_val_if_fail(msginfo != NULL, NULL);
460 file = procmsg_get_message_file_path(msginfo);
461 g_return_val_if_fail(file != NULL, NULL);
463 if (!is_file_exist(file)) {
465 file = procmsg_get_message_file(msginfo);
470 if ((fp = fopen(file, "rb")) == NULL) {
471 FILE_OP_ERROR(file, "fopen");
478 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
481 while (fgets(buf, sizeof(buf), fp) != NULL)
482 if (buf[0] == '\r' || buf[0] == '\n') break;
488 gboolean procmsg_msg_exist(MsgInfo *msginfo)
493 if (!msginfo) return FALSE;
495 path = folder_item_get_path(msginfo->folder);
497 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
503 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
504 PrefsFilterType type)
506 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
507 {"X-ML-Name:", NULL, TRUE},
508 {"X-List:", NULL, TRUE},
509 {"X-Mailing-list:", NULL, TRUE},
510 {"List-Id:", NULL, TRUE},
511 {"X-Sequence:", NULL, TRUE},
512 {NULL, NULL, FALSE}};
518 H_X_MAILING_LIST = 3,
525 g_return_if_fail(msginfo != NULL);
526 g_return_if_fail(header != NULL);
527 g_return_if_fail(key != NULL);
536 if ((fp = procmsg_open_message(msginfo)) == NULL)
538 procheader_get_header_fields(fp, hentry);
541 #define SET_FILTER_KEY(hstr, idx) \
543 *header = g_strdup(hstr); \
544 *key = hentry[idx].body; \
545 hentry[idx].body = NULL; \
548 if (hentry[H_X_BEENTHERE].body != NULL) {
549 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
550 } else if (hentry[H_X_ML_NAME].body != NULL) {
551 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
552 } else if (hentry[H_X_LIST].body != NULL) {
553 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
554 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
555 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
556 } else if (hentry[H_LIST_ID].body != NULL) {
557 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
558 extract_list_id_str(*key);
559 } else if (hentry[H_X_SEQUENCE].body != NULL) {
562 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
565 while (*p != '\0' && !isspace(*p)) p++;
566 while (isspace(*p)) p++;
573 } else if (msginfo->subject) {
574 *header = g_strdup("subject");
575 *key = g_strdup(msginfo->subject);
578 #undef SET_FILTER_KEY
580 g_free(hentry[H_X_BEENTHERE].body);
581 hentry[H_X_BEENTHERE].body = NULL;
582 g_free(hentry[H_X_ML_NAME].body);
583 hentry[H_X_ML_NAME].body = NULL;
584 g_free(hentry[H_X_LIST].body);
585 hentry[H_X_LIST].body = NULL;
586 g_free(hentry[H_X_MAILING_LIST].body);
587 hentry[H_X_MAILING_LIST].body = NULL;
588 g_free(hentry[H_LIST_ID].body);
589 hentry[H_LIST_ID].body = NULL;
593 *header = g_strdup("from");
594 *key = g_strdup(msginfo->from);
597 *header = g_strdup("to");
598 *key = g_strdup(msginfo->to);
600 case FILTER_BY_SUBJECT:
601 *header = g_strdup("subject");
602 *key = g_strdup(msginfo->subject);
609 void procmsg_empty_trash(void)
614 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
615 trash = FOLDER(cur->data)->trash;
616 if (trash && trash->total_msgs > 0)
617 folder_item_remove_all_msg(trash);
622 *\brief Send messages in queue
624 *\param queue Queue folder to process
625 *\param save_msgs Unused
627 *\return Number of messages sent, negative if an error occurred
628 * positive if no error occurred
630 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
632 gint ret = 1, count = 0;
636 queue = folder_get_default_queue();
637 g_return_val_if_fail(queue != NULL, -1);
639 folder_item_scan(queue);
640 list = folder_item_get_msg_list(queue);
642 for (elem = list; elem != NULL; elem = elem->next) {
646 msginfo = (MsgInfo *)(elem->data);
647 if (!MSG_IS_LOCKED(msginfo->flags)) {
648 file = folder_item_fetch_msg(queue, msginfo->msgnum);
650 if (procmsg_send_message_queue(file) < 0) {
651 g_warning("Sending queued message %d failed.\n",
656 * We save in procmsg_send_message_queue because
657 * we need the destination folder from the queue
661 procmsg_save_to_outbox
662 (queue->folder->outbox,
666 folder_item_remove_msg(queue, msginfo->msgnum);
671 /* FIXME: supposedly if only one message is locked, and queue
672 * is being flushed, the following free says something like
673 * "freeing msg ## in folder (nil)". */
674 procmsg_msginfo_free(msginfo);
680 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
685 if ((fp = fopen(in, "rb")) == NULL) {
686 FILE_OP_ERROR(in, "fopen");
689 if ((outfp = fopen(out, "wb")) == NULL) {
690 FILE_OP_ERROR(out, "fopen");
694 while (fgets(buf, sizeof(buf), fp) != NULL)
695 if (buf[0] == '\r' || buf[0] == '\n') break;
696 while (fgets(buf, sizeof(buf), fp) != NULL)
703 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
707 MsgInfo *msginfo, *tmp_msginfo;
708 MsgFlags flag = {0, 0};
710 debug_print("saving sent message...\n");
713 outbox = folder_get_default_outbox();
714 g_return_val_if_fail(outbox != NULL, -1);
716 /* remove queueing headers */
718 gchar tmp[MAXPATHLEN + 1];
720 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
721 get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
723 if (procmsg_remove_special_headers(file, tmp) !=0)
726 folder_item_scan(outbox);
727 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
728 g_warning("can't save message\n");
733 folder_item_scan(outbox);
734 if ((num = folder_item_add_msg
735 (outbox, file, &flag, FALSE)) < 0) {
736 g_warning("can't save message\n");
741 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
742 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
743 if (msginfo != NULL) {
744 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
745 procmsg_msginfo_free(msginfo); /* refcnt-- */
746 /* tmp_msginfo == msginfo */
747 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
748 msginfo->returnreceiptto)) {
749 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
750 procmsg_msginfo_free(msginfo); /* refcnt-- */
753 folder_item_update(outbox, TRUE);
758 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
760 static const gchar *def_cmd = "lpr %s";
767 g_return_if_fail(msginfo);
769 if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
770 g_warning("Can't get text part\n");
774 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
775 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
777 if ((prfp = fopen(prtmp, "wb")) == NULL) {
778 FILE_OP_ERROR(prtmp, "fopen");
784 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
785 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
786 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
787 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
788 if (msginfo->newsgroups)
789 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
790 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
793 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
799 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
801 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
804 g_warning("Print command line is invalid: `%s'\n",
806 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
812 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
816 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
823 MsgInfo *procmsg_msginfo_new(void)
827 newmsginfo = g_new0(MsgInfo, 1);
828 newmsginfo->refcnt = 1;
833 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
837 if (msginfo == NULL) return NULL;
839 newmsginfo = g_new0(MsgInfo, 1);
841 newmsginfo->refcnt = 1;
843 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
844 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
845 g_strdup(msginfo->mmb) : NULL
869 MEMBDUP(dispositionnotificationto);
870 MEMBDUP(returnreceiptto);
874 MEMBCOPY(threadscore);
879 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
881 MsgInfo *full_msginfo;
884 if (msginfo == NULL) return NULL;
886 file = procmsg_get_message_file(msginfo);
888 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
892 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
894 if (!full_msginfo) return NULL;
896 /* CLAWS: make sure we add the missing members; see:
897 * procheader.c::procheader_get_headernames() */
899 msginfo->xface = g_strdup(full_msginfo->xface);
900 if (!msginfo->dispositionnotificationto)
901 msginfo->dispositionnotificationto =
902 g_strdup(full_msginfo->dispositionnotificationto);
903 if (!msginfo->returnreceiptto)
904 msginfo->returnreceiptto = g_strdup
905 (full_msginfo->returnreceiptto);
906 procmsg_msginfo_free(full_msginfo);
908 return procmsg_msginfo_new_ref(msginfo);
911 void procmsg_msginfo_free(MsgInfo *msginfo)
913 if (msginfo == NULL) return;
916 if (msginfo->refcnt > 0)
919 debug_print("freeing msginfo %d in %s\n", msginfo->msgnum, msginfo->folder ? msginfo->folder->path : "(nil)");
921 if (msginfo->to_folder) {
922 msginfo->to_folder->op_count--;
923 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
926 g_free(msginfo->fromspace);
927 g_free(msginfo->references);
928 g_free(msginfo->returnreceiptto);
929 g_free(msginfo->dispositionnotificationto);
930 g_free(msginfo->xface);
932 g_free(msginfo->fromname);
934 g_free(msginfo->date);
935 g_free(msginfo->from);
938 g_free(msginfo->newsgroups);
939 g_free(msginfo->subject);
940 g_free(msginfo->msgid);
941 g_free(msginfo->inreplyto);
942 g_free(msginfo->xref);
947 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
951 memusage += sizeof(MsgInfo);
952 if (msginfo->fromname)
953 memusage += strlen(msginfo->fromname);
955 memusage += strlen(msginfo->date);
957 memusage += strlen(msginfo->from);
959 memusage += strlen(msginfo->to);
961 memusage += strlen(msginfo->cc);
962 if (msginfo->newsgroups)
963 memusage += strlen(msginfo->newsgroups);
964 if (msginfo->subject)
965 memusage += strlen(msginfo->subject);
967 memusage += strlen(msginfo->msgid);
968 if (msginfo->inreplyto)
969 memusage += strlen(msginfo->inreplyto);
971 memusage += strlen(msginfo->xface);
972 if (msginfo->dispositionnotificationto)
973 memusage += strlen(msginfo->dispositionnotificationto);
974 if (msginfo->returnreceiptto)
975 memusage += strlen(msginfo->returnreceiptto);
976 if (msginfo->references)
977 memusage += strlen(msginfo->references);
978 if (msginfo->fromspace)
979 memusage += strlen(msginfo->fromspace);
984 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
986 const MsgInfo *msginfo1 = a;
987 const MsgInfo *msginfo2 = b;
994 return msginfo1->msgnum - msginfo2->msgnum;
1003 Q_MAIL_ACCOUNT_ID = 4,
1004 Q_NEWS_ACCOUNT_ID = 5,
1005 Q_SAVE_COPY_FOLDER = 6,
1006 Q_REPLY_MESSAGE_ID = 7,
1007 Q_FWD_MESSAGE_ID = 8
1010 gint procmsg_send_message_queue(const gchar *file)
1012 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1013 {"SSV:", NULL, FALSE},
1014 {"R:", NULL, FALSE},
1015 {"NG:", NULL, FALSE},
1016 {"MAID:", NULL, FALSE},
1017 {"NAID:", NULL, FALSE},
1018 {"SCF:", NULL, FALSE},
1019 {"RMID:", NULL, FALSE},
1020 {"FMID:", NULL, FALSE},
1021 {NULL, NULL, FALSE}};
1024 gint mailval = 0, newsval = 0;
1026 gchar *smtpserver = NULL;
1027 GSList *to_list = NULL;
1028 GSList *newsgroup_list = NULL;
1029 gchar *savecopyfolder = NULL;
1030 gchar *replymessageid = NULL;
1031 gchar *fwdmessageid = NULL;
1032 gchar buf[BUFFSIZE];
1034 PrefsAccount *mailac = NULL, *newsac = NULL;
1037 g_return_val_if_fail(file != NULL, -1);
1039 if ((fp = fopen(file, "rb")) == NULL) {
1040 FILE_OP_ERROR(file, "fopen");
1044 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1046 gchar *p = buf + strlen(qentry[hnum].name);
1050 if (!from) from = g_strdup(p);
1053 if (!smtpserver) smtpserver = g_strdup(p);
1056 to_list = address_list_append(to_list, p);
1059 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1061 case Q_MAIL_ACCOUNT_ID:
1062 mailac = account_find_from_id(atoi(p));
1064 case Q_NEWS_ACCOUNT_ID:
1065 newsac = account_find_from_id(atoi(p));
1067 case Q_SAVE_COPY_FOLDER:
1068 if (!savecopyfolder) savecopyfolder = g_strdup(p);
1070 case Q_REPLY_MESSAGE_ID:
1071 if (!replymessageid) replymessageid = g_strdup(p);
1073 case Q_FWD_MESSAGE_ID:
1074 if (!fwdmessageid) fwdmessageid = g_strdup(p);
1078 filepos = ftell(fp);
1081 debug_print("Sending message by mail\n");
1083 g_warning("Queued message header is broken.\n");
1085 } else if (mailac && mailac->use_mail_command &&
1086 mailac->mail_command && (* mailac->mail_command)) {
1087 mailval = send_message_local(mailac->mail_command, fp);
1091 mailac = account_find_from_smtp_server(from, smtpserver);
1093 g_warning("Account not found. "
1094 "Using current account...\n");
1095 mailac = cur_account;
1100 mailval = send_message_smtp(mailac, to_list, fp);
1102 PrefsAccount tmp_ac;
1104 g_warning("Account not found.\n");
1106 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1107 tmp_ac.address = from;
1108 tmp_ac.smtp_server = smtpserver;
1109 tmp_ac.smtpport = SMTP_PORT;
1110 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1115 fseek(fp, filepos, SEEK_SET);
1116 if (newsgroup_list && (newsval == 0)) {
1121 /* write to temporary file */
1122 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1123 G_DIR_SEPARATOR, (gint)file);
1124 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1125 FILE_OP_ERROR(tmp, "fopen");
1127 alertpanel_error(_("Could not create temporary file for news sending."));
1129 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1130 FILE_OP_ERROR(tmp, "chmod");
1131 g_warning("can't change file mode\n");
1134 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1135 if (fputs(buf, tmpfp) == EOF) {
1136 FILE_OP_ERROR(tmp, "fputs");
1138 alertpanel_error(_("Error when writing temporary file for news sending."));
1144 debug_print("Sending message by news\n");
1146 folder = FOLDER(newsac->folder);
1148 newsval = news_post(folder, tmp);
1150 alertpanel_error(_("Error occurred while posting the message to %s ."),
1151 newsac->nntp_server);
1159 slist_free_strings(to_list);
1160 g_slist_free(to_list);
1161 slist_free_strings(newsgroup_list);
1162 g_slist_free(newsgroup_list);
1167 /* save message to outbox */
1168 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1171 debug_print("saving sent message...\n");
1173 outbox = folder_find_item_from_identifier(savecopyfolder);
1175 outbox = folder_get_default_outbox();
1177 procmsg_save_to_outbox(outbox, file, TRUE);
1180 if (replymessageid != NULL || fwdmessageid != NULL) {
1184 if (replymessageid != NULL)
1185 tokens = g_strsplit(replymessageid, "\x7f", 0);
1187 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1188 item = folder_find_item_from_identifier(tokens[0]);
1190 /* check if queued message has valid folder and message id */
1191 if (item != NULL && tokens[2] != NULL) {
1194 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1196 /* check if referring message exists and has a message id */
1197 if ((msginfo != NULL) &&
1198 (msginfo->msgid != NULL) &&
1199 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1200 procmsg_msginfo_free(msginfo);
1204 if (msginfo == NULL) {
1205 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1208 if (msginfo != NULL) {
1209 if (replymessageid != NULL) {
1210 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1211 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1214 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1215 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1217 procmsg_msginfo_free(msginfo);
1223 g_free(savecopyfolder);
1224 g_free(replymessageid);
1225 g_free(fwdmessageid);
1227 return (newsval != 0 ? newsval : mailval);
1230 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1232 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1235 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1239 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1244 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1245 item->unread_msgs++;
1246 if (procmsg_msg_has_marked_parent(msginfo))
1247 item->unreadmarked_msgs++;
1250 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1251 item->unread_msgs--;
1252 if (procmsg_msg_has_marked_parent(msginfo))
1253 item->unreadmarked_msgs--;
1257 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1258 procmsg_update_unread_children(msginfo, TRUE);
1261 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1262 procmsg_update_unread_children(msginfo, FALSE);
1266 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1269 MsgInfoUpdate msginfo_update;
1270 MsgPermFlags perm_flags_new, perm_flags_old;
1272 g_return_if_fail(msginfo != NULL);
1273 item = msginfo->folder;
1274 g_return_if_fail(item != NULL);
1276 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1278 /* Perm Flags handling */
1279 perm_flags_old = msginfo->flags.perm_flags;
1280 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1281 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1282 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1285 if (perm_flags_old != perm_flags_new) {
1286 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1288 update_folder_msg_counts(item, msginfo, perm_flags_old);
1290 msginfo_update.msginfo = msginfo;
1291 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1292 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1293 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1296 /* Tmp flags hanlding */
1297 msginfo->flags.tmp_flags |= tmp_flags;
1300 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1303 MsgInfoUpdate msginfo_update;
1304 MsgPermFlags perm_flags_new, perm_flags_old;
1306 g_return_if_fail(msginfo != NULL);
1307 item = msginfo->folder;
1308 g_return_if_fail(item != NULL);
1310 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1312 /* Perm Flags handling */
1313 perm_flags_old = msginfo->flags.perm_flags;
1314 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1316 if (perm_flags_old != perm_flags_new) {
1317 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1319 update_folder_msg_counts(item, msginfo, perm_flags_old);
1321 msginfo_update.msginfo = msginfo;
1322 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1323 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1324 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1327 /* Tmp flags hanlding */
1328 msginfo->flags.tmp_flags &= ~tmp_flags;
1332 *\brief check for flags (e.g. mark) in prior msgs of current thread
1334 *\param info Current message
1335 *\param perm_flags Flags to be checked
1336 *\param parentmsgs Hash of prior msgs to avoid loops
1338 *\return gboolean TRUE if perm_flags are found
1340 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1341 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1345 g_return_val_if_fail(info != NULL, FALSE);
1347 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1348 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1350 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1351 procmsg_msginfo_free(tmp);
1353 } else if (tmp != NULL) {
1356 if (g_hash_table_lookup(parentmsgs, info)) {
1357 debug_print("loop detected: %s%c%d\n",
1358 folder_item_get_path(info->folder),
1359 G_DIR_SEPARATOR, info->msgnum);
1362 g_hash_table_insert(parentmsgs, info, "1");
1363 result = procmsg_msg_has_flagged_parent_real(
1364 tmp, perm_flags, parentmsgs);
1366 procmsg_msginfo_free(tmp);
1376 *\brief Callback for cleaning up hash of parentmsgs
1378 gboolean parentmsgs_hash_remove(gpointer key,
1386 *\brief Set up list of parentmsgs
1387 * See procmsg_msg_has_flagged_parent_real()
1389 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1392 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1394 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1395 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1396 g_hash_table_destroy(parentmsgs);
1401 *\brief Check if msgs prior in thread are marked
1402 * See procmsg_msg_has_flagged_parent_real()
1404 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1406 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1410 GSList *procmsg_find_children_func(MsgInfo *info,
1411 GSList *children, GSList *all)
1415 g_return_val_if_fail(info!=NULL, children);
1416 if (info->msgid == NULL)
1419 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1420 MsgInfo *tmp = (MsgInfo *)cur->data;
1421 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1422 /* Check if message is already in the list */
1423 if ((children == NULL) ||
1424 (g_slist_index(children, tmp) == -1)) {
1425 children = g_slist_prepend(children,
1426 procmsg_msginfo_new_ref(tmp));
1427 children = procmsg_find_children_func(tmp,
1436 GSList *procmsg_find_children (MsgInfo *info)
1441 g_return_val_if_fail(info!=NULL, NULL);
1442 all = folder_item_get_msg_list(info->folder);
1443 children = procmsg_find_children_func(info, NULL, all);
1444 if (children != NULL) {
1445 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1446 /* this will not free the used pointers
1447 created with procmsg_msginfo_new_ref */
1448 procmsg_msginfo_free((MsgInfo *)cur->data);
1456 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1458 GSList *children = procmsg_find_children(info);
1460 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1461 MsgInfo *tmp = (MsgInfo *)cur->data;
1462 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1464 info->folder->unreadmarked_msgs++;
1466 info->folder->unreadmarked_msgs--;
1467 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1469 procmsg_msginfo_free(tmp);
1471 g_slist_free(children);
1475 * Set the destination folder for a copy or move operation
1477 * \param msginfo The message which's destination folder is changed
1478 * \param to_folder The destination folder for the operation
1480 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1482 if(msginfo->to_folder != NULL) {
1483 msginfo->to_folder->op_count--;
1484 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1486 msginfo->to_folder = to_folder;
1487 if(to_folder != NULL) {
1488 to_folder->op_count++;
1489 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1494 * Apply filtering actions to the msginfo
1496 * \param msginfo The MsgInfo describing the message that should be filtered
1497 * \return TRUE if the message was moved and MsgInfo is now invalid,
1500 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1502 MailFilteringData mail_filtering_data;
1504 mail_filtering_data.msginfo = msginfo;
1505 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1508 /* filter if enabled in prefs or move to inbox if not */
1509 if((filtering_rules != NULL) &&
1510 filter_message_by_msginfo(filtering_rules, msginfo))