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"
43 #include "alertpanel.h"
48 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
50 GHashTable *msg_table;
52 if (mlist == NULL) return NULL;
54 msg_table = g_hash_table_new(NULL, g_direct_equal);
55 procmsg_msg_hash_table_append(msg_table, mlist);
60 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
65 if (msg_table == NULL || mlist == NULL) return;
67 for (cur = mlist; cur != NULL; cur = cur->next) {
68 msginfo = (MsgInfo *)cur->data;
70 g_hash_table_insert(msg_table,
71 GUINT_TO_POINTER(msginfo->msgnum),
76 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
78 GHashTable *msg_table;
82 if (mlist == NULL) return NULL;
84 msg_table = g_hash_table_new(NULL, g_direct_equal);
86 for (cur = mlist; cur != NULL; cur = cur->next) {
87 msginfo = (MsgInfo *)cur->data;
88 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
94 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
100 for (cur = mlist; cur != NULL; cur = cur->next) {
101 msginfo = (MsgInfo *)cur->data;
102 if (msginfo && msginfo->msgnum > last)
103 last = msginfo->msgnum;
109 void procmsg_msg_list_free(GSList *mlist)
114 for (cur = mlist; cur != NULL; cur = cur->next) {
115 msginfo = (MsgInfo *)cur->data;
116 procmsg_msginfo_free(msginfo);
130 static gboolean procmsg_ignore_node(GNode *node, gpointer data)
132 MsgInfo *msginfo = (MsgInfo *)node->data;
134 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
135 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
140 /* CLAWS subject threading:
142 in the first round it inserts subject lines in a hash
143 table. a duplicate subject line replaces the one in
144 the table only if its older. (this round should actually
145 create a list of all duplicate subject lines)
147 the second round finishes the threads by attaching
148 duplicate subject lines to the one found in the
149 hash table. as soon as a subject line is found that
150 is too old, that one becomes the new parent for
151 the next iteration. (this fails when a parent arrived
152 later than its child.)
155 /* return the reversed thread tree */
156 GNode *procmsg_get_thread_tree(GSList *mlist)
158 GNode *root, *parent, *node, *next, *last;
159 GNode *prev; /* CLAWS */
160 GHashTable *msgid_table;
161 GHashTable *subject_table;
164 const gchar *subject;
166 root = g_node_new(NULL);
167 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
168 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
170 for (; mlist != NULL; mlist = mlist->next) {
171 msginfo = (MsgInfo *)mlist->data;
174 if (msginfo->inreplyto) {
175 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
176 if (parent == NULL) {
179 if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
180 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
181 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
185 node = g_node_insert_data_before
186 (parent, parent == root ? parent->children : NULL,
188 if ((msgid = msginfo->msgid) &&
189 g_hash_table_lookup(msgid_table, msgid) == NULL)
190 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
192 /* CLAWS: add subject to table (without prefix) */
193 if (prefs_common.thread_by_subject) {
194 GNode *found_subject = NULL;
196 subject = msginfo->subject;
197 subject += subject_get_reply_prefix_length(subject);
198 found_subject = subject_table_lookup_clean
199 (subject_table, (gchar *) subject);
201 if (found_subject == NULL)
202 subject_table_insert_clean(subject_table, (gchar *) subject,
204 else if ( ((MsgInfo*)(found_subject->data))->date_t >
205 ((MsgInfo*)(node->data))->date_t ) {
206 /* replace if msg in table is older than current one
207 TODO: should create a list of messages with same subject */
208 subject_table_remove_clean(subject_table, (gchar *) subject);
209 subject_table_insert_clean(subject_table, (gchar *) subject, node);
214 /* complete the unfinished threads */
215 for (node = root->children; node != NULL; ) {
216 prev = node->prev; /* CLAWS: need the last node */
219 msginfo = (MsgInfo *)node->data;
220 if (msginfo->inreplyto) {
221 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
222 /* node should not be the parent, and node should not
223 be an ancestor of parent (circular reference) */
224 if (parent && parent != node &&
225 !g_node_is_ancestor(node, parent)) {
228 (parent, parent->children, node);
229 /* CLAWS: ignore thread */
230 if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
231 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, procmsg_ignore_node, NULL);
234 last = (next == NULL) ? prev : node;
238 if (prefs_common.thread_by_subject) {
239 for (node = last; node && node != NULL;) {
241 msginfo = (MsgInfo *) node->data;
242 subject = msginfo->subject + subject_get_reply_prefix_length(msginfo->subject);
244 /* may not parentize if parent was delivered after childs */
245 if (subject != msginfo->subject)
246 parent = subject_table_lookup_clean(subject_table, (gchar *) subject);
250 /* the node may already be threaded by IN-REPLY-TO, so go up in the tree to
251 find the parent node */
252 if (parent != NULL) {
253 if (g_node_is_ancestor(node, parent))
257 /* make new thread parent if too old compared to previous one; probably
258 breaks ignoring threads for subject threading. not accurate because
259 the tree isn't sorted by date. */
260 if (parent && abs(difftime(msginfo->date_t, ((MsgInfo *)parent->data)->date_t)) >
261 prefs_common.thread_by_subject_max_age * 3600 * 24) {
262 subject_table_remove_clean(subject_table, (gchar *) subject);
263 subject_table_insert_clean(subject_table, (gchar *) subject, node);
270 g_node_append(parent, node);
271 /* CLAWS: ignore thread */
272 if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
273 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, procmsg_ignore_node, NULL);
281 g_hash_table_destroy(subject_table);
282 g_hash_table_destroy(msgid_table);
287 void procmsg_move_messages(GSList *mlist)
289 GSList *cur, *movelist = NULL;
291 FolderItem *dest = NULL;
295 folder_item_update_freeze();
297 for (cur = mlist; cur != NULL; cur = cur->next) {
298 msginfo = (MsgInfo *)cur->data;
300 dest = msginfo->to_folder;
301 movelist = g_slist_append(movelist, msginfo);
302 } else if (dest == msginfo->to_folder) {
303 movelist = g_slist_append(movelist, msginfo);
305 folder_item_move_msgs_with_dest(dest, movelist);
306 g_slist_free(movelist);
308 dest = msginfo->to_folder;
309 movelist = g_slist_append(movelist, msginfo);
311 procmsg_msginfo_set_to_folder(msginfo, NULL);
315 folder_item_move_msgs_with_dest(dest, movelist);
316 g_slist_free(movelist);
319 folder_item_update_thaw();
322 void procmsg_copy_messages(GSList *mlist)
324 GSList *cur, *copylist = NULL;
326 FolderItem *dest = NULL;
330 folder_item_update_freeze();
332 for (cur = mlist; cur != NULL; cur = cur->next) {
333 msginfo = (MsgInfo *)cur->data;
335 dest = msginfo->to_folder;
336 copylist = g_slist_append(copylist, msginfo);
337 } else if (dest == msginfo->to_folder) {
338 copylist = g_slist_append(copylist, msginfo);
340 folder_item_copy_msgs_with_dest(dest, copylist);
341 g_slist_free(copylist);
343 dest = msginfo->to_folder;
344 copylist = g_slist_append(copylist, msginfo);
346 procmsg_msginfo_set_to_folder(msginfo, NULL);
350 folder_item_copy_msgs_with_dest(dest, copylist);
351 g_slist_free(copylist);
354 folder_item_update_thaw();
357 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
361 g_return_val_if_fail(msginfo != NULL, NULL);
363 if (msginfo->plaintext_file)
364 file = g_strdup(msginfo->plaintext_file);
366 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
372 gchar *procmsg_get_message_file(MsgInfo *msginfo)
374 gchar *filename = NULL;
376 g_return_val_if_fail(msginfo != NULL, NULL);
378 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
380 g_warning("can't fetch message %d\n", msginfo->msgnum);
385 FILE *procmsg_open_message(MsgInfo *msginfo)
390 g_return_val_if_fail(msginfo != NULL, NULL);
392 file = procmsg_get_message_file_path(msginfo);
393 g_return_val_if_fail(file != NULL, NULL);
395 if (!is_file_exist(file)) {
397 file = procmsg_get_message_file(msginfo);
398 g_return_val_if_fail(file != NULL, NULL);
401 if ((fp = fopen(file, "rb")) == NULL) {
402 FILE_OP_ERROR(file, "fopen");
409 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
412 while (fgets(buf, sizeof(buf), fp) != NULL)
413 if (buf[0] == '\r' || buf[0] == '\n') break;
420 FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
426 g_return_val_if_fail(msginfo != NULL, NULL);
428 if (mimeinfo) *mimeinfo = NULL;
430 if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL;
432 mimeinfo_ = procmime_scan_mime_header(fp);
438 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
439 rfc2015_is_encrypted(mimeinfo_)) {
440 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
443 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
444 !msginfo->plaintext_file &&
445 !msginfo->decryption_failed) {
447 rfc2015_decrypt_message(msginfo, mimeinfo_, fp);
448 if (msginfo->plaintext_file &&
449 !msginfo->decryption_failed) {
451 procmime_mimeinfo_free_all(mimeinfo_);
452 if ((fp = procmsg_open_message(msginfo)) == NULL)
454 mimeinfo_ = procmime_scan_mime_header(fp);
460 if (fseek(fp, fpos, SEEK_SET) < 0)
465 if (mimeinfo) *mimeinfo = mimeinfo_;
470 gboolean procmsg_msg_exist(MsgInfo *msginfo)
475 if (!msginfo) return FALSE;
477 path = folder_item_get_path(msginfo->folder);
479 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
485 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
486 PrefsFilterType type)
488 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
489 {"X-ML-Name:", NULL, TRUE},
490 {"X-List:", NULL, TRUE},
491 {"X-Mailing-list:", NULL, TRUE},
492 {"List-Id:", NULL, TRUE},
493 {"X-Sequence:", NULL, TRUE},
494 {NULL, NULL, FALSE}};
500 H_X_MAILING_LIST = 3,
507 g_return_if_fail(msginfo != NULL);
508 g_return_if_fail(header != NULL);
509 g_return_if_fail(key != NULL);
518 if ((fp = procmsg_open_message(msginfo)) == NULL)
520 procheader_get_header_fields(fp, hentry);
523 #define SET_FILTER_KEY(hstr, idx) \
525 *header = g_strdup(hstr); \
526 *key = hentry[idx].body; \
527 hentry[idx].body = NULL; \
530 if (hentry[H_X_BEENTHERE].body != NULL) {
531 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
532 } else if (hentry[H_X_ML_NAME].body != NULL) {
533 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
534 } else if (hentry[H_X_LIST].body != NULL) {
535 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
536 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
537 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
538 } else if (hentry[H_LIST_ID].body != NULL) {
539 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
540 extract_list_id_str(*key);
541 } else if (hentry[H_X_SEQUENCE].body != NULL) {
544 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
547 while (*p != '\0' && !isspace(*p)) p++;
548 while (isspace(*p)) p++;
555 } else if (msginfo->subject) {
556 *header = g_strdup("subject");
557 *key = g_strdup(msginfo->subject);
560 #undef SET_FILTER_KEY
562 g_free(hentry[H_X_BEENTHERE].body);
563 hentry[H_X_BEENTHERE].body = NULL;
564 g_free(hentry[H_X_ML_NAME].body);
565 hentry[H_X_ML_NAME].body = NULL;
566 g_free(hentry[H_X_LIST].body);
567 hentry[H_X_LIST].body = NULL;
568 g_free(hentry[H_X_MAILING_LIST].body);
569 hentry[H_X_MAILING_LIST].body = NULL;
570 g_free(hentry[H_LIST_ID].body);
571 hentry[H_LIST_ID].body = NULL;
575 *header = g_strdup("from");
576 *key = g_strdup(msginfo->from);
579 *header = g_strdup("to");
580 *key = g_strdup(msginfo->to);
582 case FILTER_BY_SUBJECT:
583 *header = g_strdup("subject");
584 *key = g_strdup(msginfo->subject);
591 void procmsg_empty_trash(void)
596 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
597 trash = FOLDER(cur->data)->trash;
598 if (trash && trash->total_msgs > 0)
599 folder_item_remove_all_msg(trash);
604 *\brief Send messages in queue
606 *\param queue Queue folder to process
607 *\param save_msgs Unused
609 *\return Number of messages sent, negative if an error occurred
610 * positive if no error occurred
612 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
614 gint ret = 1, count = 0;
618 queue = folder_get_default_queue();
619 g_return_val_if_fail(queue != NULL, -1);
621 folder_item_scan(queue);
622 list = folder_item_get_msg_list(queue);
624 for (elem = list; elem != NULL; elem = elem->next) {
628 msginfo = (MsgInfo *)(elem->data);
629 if (!MSG_IS_LOCKED(msginfo->flags)) {
630 file = folder_item_fetch_msg(queue, msginfo->msgnum);
632 if (procmsg_send_message_queue(file) < 0) {
633 g_warning("Sending queued message %d failed.\n",
638 * We save in procmsg_send_message_queue because
639 * we need the destination folder from the queue
643 procmsg_save_to_outbox
644 (queue->folder->outbox,
648 folder_item_remove_msg(queue, msginfo->msgnum);
653 /* FIXME: supposedly if only one message is locked, and queue
654 * is being flushed, the following free says something like
655 * "freeing msg ## in folder (nil)". */
656 procmsg_msginfo_free(msginfo);
662 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
667 if ((fp = fopen(in, "rb")) == NULL) {
668 FILE_OP_ERROR(in, "fopen");
671 if ((outfp = fopen(out, "wb")) == NULL) {
672 FILE_OP_ERROR(out, "fopen");
676 while (fgets(buf, sizeof(buf), fp) != NULL)
677 if (buf[0] == '\r' || buf[0] == '\n') break;
678 while (fgets(buf, sizeof(buf), fp) != NULL)
685 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
691 debug_print("saving sent message...\n");
694 outbox = folder_get_default_outbox();
695 g_return_val_if_fail(outbox != NULL, -1);
697 /* remove queueing headers */
699 gchar tmp[MAXPATHLEN + 1];
701 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
702 get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
704 if (procmsg_remove_special_headers(file, tmp) !=0)
707 folder_item_scan(outbox);
708 if ((num = folder_item_add_msg(outbox, tmp, TRUE)) < 0) {
709 g_warning("can't save message\n");
714 folder_item_scan(outbox);
715 if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
716 g_warning("can't save message\n");
721 msginfo = folder_item_get_msginfo(outbox, num);
722 if (msginfo != NULL) {
723 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
724 procmsg_msginfo_free(msginfo);
726 folder_item_update(outbox, TRUE);
731 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
733 static const gchar *def_cmd = "lpr %s";
740 g_return_if_fail(msginfo);
742 if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
743 g_warning("Can't get text part\n");
747 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
748 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
750 if ((prfp = fopen(prtmp, "wb")) == NULL) {
751 FILE_OP_ERROR(prtmp, "fopen");
757 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
758 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
759 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
760 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
761 if (msginfo->newsgroups)
762 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
763 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
766 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
772 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
774 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
777 g_warning("Print command line is invalid: `%s'\n",
779 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
785 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
789 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
796 MsgInfo *procmsg_msginfo_new(void)
800 newmsginfo = g_new0(MsgInfo, 1);
801 newmsginfo->refcnt = 1;
806 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
810 if (msginfo == NULL) return NULL;
812 newmsginfo = g_new0(MsgInfo, 1);
814 newmsginfo->refcnt = 1;
816 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
817 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
818 g_strdup(msginfo->mmb) : NULL
842 MEMBDUP(dispositionnotificationto);
843 MEMBDUP(returnreceiptto);
847 MEMBCOPY(threadscore);
852 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
854 MsgInfo *full_msginfo;
857 if (msginfo == NULL) return NULL;
859 file = procmsg_get_message_file(msginfo);
861 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
865 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
867 if (!full_msginfo) return NULL;
869 full_msginfo->msgnum = msginfo->msgnum;
870 full_msginfo->size = msginfo->size;
871 full_msginfo->mtime = msginfo->mtime;
872 full_msginfo->folder = msginfo->folder;
874 full_msginfo->plaintext_file = g_strdup(msginfo->plaintext_file);
875 full_msginfo->decryption_failed = msginfo->decryption_failed;
877 procmsg_msginfo_set_to_folder(full_msginfo, msginfo->to_folder);
882 void procmsg_msginfo_free(MsgInfo *msginfo)
884 if (msginfo == NULL) return;
887 if (msginfo->refcnt > 0)
890 debug_print("freeing msginfo %d in %s\n", msginfo->msgnum, msginfo->folder ? msginfo->folder->path : "(nil)");
892 if (msginfo->to_folder) {
893 msginfo->to_folder->op_count--;
894 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
897 g_free(msginfo->fromspace);
898 g_free(msginfo->references);
899 g_free(msginfo->returnreceiptto);
900 g_free(msginfo->dispositionnotificationto);
901 g_free(msginfo->xface);
903 g_free(msginfo->fromname);
905 g_free(msginfo->date);
906 g_free(msginfo->from);
909 g_free(msginfo->newsgroups);
910 g_free(msginfo->subject);
911 g_free(msginfo->msgid);
912 g_free(msginfo->inreplyto);
913 g_free(msginfo->xref);
918 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
922 memusage += sizeof(MsgInfo);
923 if (msginfo->fromname)
924 memusage += strlen(msginfo->fromname);
926 memusage += strlen(msginfo->date);
928 memusage += strlen(msginfo->from);
930 memusage += strlen(msginfo->to);
932 memusage += strlen(msginfo->cc);
933 if (msginfo->newsgroups)
934 memusage += strlen(msginfo->newsgroups);
935 if (msginfo->subject)
936 memusage += strlen(msginfo->subject);
938 memusage += strlen(msginfo->msgid);
939 if (msginfo->inreplyto)
940 memusage += strlen(msginfo->inreplyto);
942 memusage += strlen(msginfo->xface);
943 if (msginfo->dispositionnotificationto)
944 memusage += strlen(msginfo->dispositionnotificationto);
945 if (msginfo->returnreceiptto)
946 memusage += strlen(msginfo->returnreceiptto);
947 if (msginfo->references)
948 memusage += strlen(msginfo->references);
949 if (msginfo->fromspace)
950 memusage += strlen(msginfo->fromspace);
955 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
957 const MsgInfo *msginfo1 = a;
958 const MsgInfo *msginfo2 = b;
965 return msginfo1->msgnum - msginfo2->msgnum;
974 Q_MAIL_ACCOUNT_ID = 4,
975 Q_NEWS_ACCOUNT_ID = 5,
976 Q_SAVE_COPY_FOLDER = 6,
977 Q_REPLY_MESSAGE_ID = 7,
981 gint procmsg_send_message_queue(const gchar *file)
983 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
984 {"SSV:", NULL, FALSE},
986 {"NG:", NULL, FALSE},
987 {"MAID:", NULL, FALSE},
988 {"NAID:", NULL, FALSE},
989 {"SCF:", NULL, FALSE},
990 {"RMID:", NULL, FALSE},
991 {"FMID:", NULL, FALSE},
992 {NULL, NULL, FALSE}};
995 gint mailval = 0, newsval = 0;
997 gchar *smtpserver = NULL;
998 GSList *to_list = NULL;
999 GSList *newsgroup_list = NULL;
1000 gchar *savecopyfolder = NULL;
1001 gchar *replymessageid = NULL;
1002 gchar *fwdmessageid = NULL;
1003 gchar buf[BUFFSIZE];
1005 PrefsAccount *mailac = NULL, *newsac = NULL;
1008 g_return_val_if_fail(file != NULL, -1);
1010 if ((fp = fopen(file, "rb")) == NULL) {
1011 FILE_OP_ERROR(file, "fopen");
1015 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1017 gchar *p = buf + strlen(qentry[hnum].name);
1021 if (!from) from = g_strdup(p);
1024 if (!smtpserver) smtpserver = g_strdup(p);
1027 to_list = address_list_append(to_list, p);
1030 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1032 case Q_MAIL_ACCOUNT_ID:
1033 mailac = account_find_from_id(atoi(p));
1035 case Q_NEWS_ACCOUNT_ID:
1036 newsac = account_find_from_id(atoi(p));
1038 case Q_SAVE_COPY_FOLDER:
1039 if (!savecopyfolder) savecopyfolder = g_strdup(p);
1041 case Q_REPLY_MESSAGE_ID:
1042 if (!replymessageid) replymessageid = g_strdup(p);
1044 case Q_FWD_MESSAGE_ID:
1045 if (!fwdmessageid) fwdmessageid = g_strdup(p);
1049 filepos = ftell(fp);
1052 debug_print("Sending message by mail\n");
1054 g_warning("Queued message header is broken.\n");
1056 } else if (mailac && mailac->use_mail_command &&
1057 mailac->mail_command && (* mailac->mail_command)) {
1058 mailval = send_message_local(mailac->mail_command, fp);
1060 } else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
1061 mailval = send_message_local(prefs_common.extsend_cmd, fp);
1065 mailac = account_find_from_smtp_server(from, smtpserver);
1067 g_warning("Account not found. "
1068 "Using current account...\n");
1069 mailac = cur_account;
1074 mailval = send_message_smtp(mailac, to_list, fp);
1076 PrefsAccount tmp_ac;
1078 g_warning("Account not found.\n");
1080 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1081 tmp_ac.address = from;
1082 tmp_ac.smtp_server = smtpserver;
1083 tmp_ac.smtpport = SMTP_PORT;
1084 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1089 fseek(fp, filepos, SEEK_SET);
1090 if (newsgroup_list && (newsval == 0)) {
1095 /* write to temporary file */
1096 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1097 G_DIR_SEPARATOR, (gint)file);
1098 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1099 FILE_OP_ERROR(tmp, "fopen");
1101 alertpanel_error(_("Could not create temporary file for news sending."));
1103 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1104 FILE_OP_ERROR(tmp, "chmod");
1105 g_warning("can't change file mode\n");
1108 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1109 if (fputs(buf, tmpfp) == EOF) {
1110 FILE_OP_ERROR(tmp, "fputs");
1112 alertpanel_error(_("Error when writing temporary file for news sending."));
1118 debug_print("Sending message by news\n");
1120 folder = FOLDER(newsac->folder);
1122 newsval = news_post(folder, tmp);
1124 alertpanel_error(_("Error occurred while posting the message to %s ."),
1125 newsac->nntp_server);
1133 slist_free_strings(to_list);
1134 g_slist_free(to_list);
1135 slist_free_strings(newsgroup_list);
1136 g_slist_free(newsgroup_list);
1141 /* save message to outbox */
1142 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1145 debug_print("saving sent message...\n");
1147 outbox = folder_find_item_from_identifier(savecopyfolder);
1149 outbox = folder_get_default_outbox();
1151 procmsg_save_to_outbox(outbox, file, TRUE);
1154 if (replymessageid != NULL || fwdmessageid != NULL) {
1158 if (replymessageid != NULL)
1159 tokens = g_strsplit(replymessageid, "\x7f", 0);
1161 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1162 item = folder_find_item_from_identifier(tokens[0]);
1164 /* check if queued message has valid folder and message id */
1165 if (item != NULL && tokens[2] != NULL) {
1168 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1170 /* check if referring message exists and has a message id */
1171 if ((msginfo != NULL) &&
1172 (msginfo->msgid != NULL) &&
1173 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1174 procmsg_msginfo_free(msginfo);
1178 if (msginfo == NULL) {
1179 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1182 if (msginfo != NULL) {
1183 if (replymessageid != NULL) {
1184 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1185 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1188 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1189 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1191 procmsg_msginfo_free(msginfo);
1197 g_free(savecopyfolder);
1198 g_free(replymessageid);
1199 g_free(fwdmessageid);
1201 return (newsval != 0 ? newsval : mailval);
1204 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1206 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1209 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1213 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1218 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1219 item->unread_msgs++;
1220 if (procmsg_msg_has_marked_parent(msginfo))
1221 item->unreadmarked_msgs++;
1224 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1225 item->unread_msgs--;
1226 if (procmsg_msg_has_marked_parent(msginfo))
1227 item->unreadmarked_msgs--;
1231 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1232 procmsg_update_unread_children(msginfo, TRUE);
1235 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1236 procmsg_update_unread_children(msginfo, FALSE);
1240 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1243 MsgInfoUpdate msginfo_update;
1244 MsgPermFlags perm_flags_new, perm_flags_old;
1246 g_return_if_fail(msginfo != NULL);
1247 item = msginfo->folder;
1248 g_return_if_fail(item != NULL);
1250 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1252 /* Perm Flags handling */
1253 perm_flags_old = msginfo->flags.perm_flags;
1254 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1255 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1256 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1259 if (perm_flags_old != perm_flags_new) {
1260 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1262 update_folder_msg_counts(item, msginfo, perm_flags_old);
1264 msginfo_update.msginfo = msginfo;
1265 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1266 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1269 /* Tmp flags hanlding */
1270 msginfo->flags.tmp_flags |= tmp_flags;
1273 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1276 MsgInfoUpdate msginfo_update;
1277 MsgPermFlags perm_flags_new, perm_flags_old;
1279 g_return_if_fail(msginfo != NULL);
1280 item = msginfo->folder;
1281 g_return_if_fail(item != NULL);
1283 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1285 /* Perm Flags handling */
1286 perm_flags_old = msginfo->flags.perm_flags;
1287 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1289 if (perm_flags_old != perm_flags_new) {
1290 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1292 update_folder_msg_counts(item, msginfo, perm_flags_old);
1294 msginfo_update.msginfo = msginfo;
1295 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1296 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1299 /* Tmp flags hanlding */
1300 msginfo->flags.tmp_flags &= ~tmp_flags;
1304 *\brief check for flags (e.g. mark) in prior msgs of current thread
1306 *\param info Current message
1307 *\param perm_flags Flags to be checked
1308 *\param parentmsgs Hash of prior msgs to avoid loops
1310 *\return gboolean TRUE if perm_flags are found
1312 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1313 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1317 g_return_val_if_fail(info != NULL, FALSE);
1319 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1320 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1322 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1323 procmsg_msginfo_free(tmp);
1325 } else if (tmp != NULL) {
1328 if (g_hash_table_lookup(parentmsgs, info)) {
1329 debug_print("loop detected: %s%c%d\n",
1330 folder_item_get_path(info->folder),
1331 G_DIR_SEPARATOR, info->msgnum);
1334 g_hash_table_insert(parentmsgs, info, "1");
1335 result = procmsg_msg_has_flagged_parent_real(
1336 tmp, perm_flags, parentmsgs);
1338 procmsg_msginfo_free(tmp);
1348 *\brief Callback for cleaning up hash of parentmsgs
1350 gboolean parentmsgs_hash_remove(gpointer key,
1358 *\brief Set up list of parentmsgs
1359 * See procmsg_msg_has_flagged_parent_real()
1361 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1364 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1366 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1367 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1368 g_hash_table_destroy(parentmsgs);
1373 *\brief Check if msgs prior in thread are marked
1374 * See procmsg_msg_has_flagged_parent_real()
1376 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1378 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1382 GSList *procmsg_find_children_func(MsgInfo *info,
1383 GSList *children, GSList *all)
1387 g_return_val_if_fail(info!=NULL, children);
1388 if (info->msgid == NULL)
1391 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1392 MsgInfo *tmp = (MsgInfo *)cur->data;
1393 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1394 /* Check if message is already in the list */
1395 if ((children == NULL) ||
1396 (g_slist_index(children, tmp) == -1)) {
1397 children = g_slist_prepend(children,
1398 procmsg_msginfo_new_ref(tmp));
1399 children = procmsg_find_children_func(tmp,
1408 GSList *procmsg_find_children (MsgInfo *info)
1413 g_return_val_if_fail(info!=NULL, NULL);
1414 all = folder_item_get_msg_list(info->folder);
1415 children = procmsg_find_children_func(info, NULL, all);
1416 if (children != NULL) {
1417 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1418 /* this will not free the used pointers
1419 created with procmsg_msginfo_new_ref */
1420 procmsg_msginfo_free((MsgInfo *)cur->data);
1428 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1430 GSList *children = procmsg_find_children(info);
1432 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1433 MsgInfo *tmp = (MsgInfo *)cur->data;
1434 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1436 info->folder->unreadmarked_msgs++;
1438 info->folder->unreadmarked_msgs--;
1439 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1441 procmsg_msginfo_free(tmp);
1443 g_slist_free(children);
1447 * Set the destination folder for a copy or move operation
1449 * \param msginfo The message which's destination folder is changed
1450 * \param to_folder The destination folder for the operation
1452 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1454 if(msginfo->to_folder != NULL) {
1455 msginfo->to_folder->op_count--;
1456 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1458 msginfo->to_folder = to_folder;
1459 if(to_folder != NULL) {
1460 to_folder->op_count++;
1461 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1466 * Apply filtering actions to the msginfo
1468 * \param msginfo The MsgInfo describing the message that should be filtered
1469 * \return TRUE if the message was moved and MsgInfo is now invalid,
1472 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1474 MailFilteringData mail_filtering_data;
1476 mail_filtering_data.msginfo = msginfo;
1477 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1480 /* filter if enabled in prefs or move to inbox if not */
1481 if((global_processing != NULL) &&
1482 filter_message_by_msginfo(global_processing, msginfo))