2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 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.
30 #include "procheader.h"
33 #include "statusbar.h"
36 typedef struct _FlagInfo FlagInfo
;
44 static void mark_sum_func (gpointer key
,
48 static GHashTable
*procmsg_read_mark_file (const gchar
*folder
);
49 static gint
procmsg_cmp_msgnum (gconstpointer a
,
51 static gint
procmsg_cmp_flag_msgnum (gconstpointer a
,
55 GHashTable
*procmsg_msg_hash_table_create(GSList
*mlist
)
57 GHashTable
*msg_table
;
59 if (mlist
== NULL
) return NULL
;
61 msg_table
= g_hash_table_new(NULL
, g_direct_equal
);
62 procmsg_msg_hash_table_append(msg_table
, mlist
);
67 void procmsg_msg_hash_table_append(GHashTable
*msg_table
, GSList
*mlist
)
72 if (msg_table
== NULL
|| mlist
== NULL
) return;
74 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
75 msginfo
= (MsgInfo
*)cur
->data
;
77 g_hash_table_insert(msg_table
,
78 GUINT_TO_POINTER(msginfo
->msgnum
),
83 GHashTable
*procmsg_to_folder_hash_table_create(GSList
*mlist
)
85 GHashTable
*msg_table
;
89 if (mlist
== NULL
) return NULL
;
91 msg_table
= g_hash_table_new(NULL
, g_direct_equal
);
93 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
94 msginfo
= (MsgInfo
*)cur
->data
;
95 g_hash_table_insert(msg_table
, msginfo
->to_folder
, msginfo
);
101 static gint
procmsg_read_cache_data_str(FILE *fp
, gchar
**str
)
107 if (fread(&len
, sizeof(len
), 1, fp
) == 1) {
114 size_t size
= MIN(len
, BUFFSIZE
- 1);
116 if (fread(buf
, size
, 1, fp
) != 1) {
118 if (tmp
) g_free(tmp
);
125 *str
= g_strconcat(tmp
, buf
, NULL
);
129 tmp
= *str
= g_strdup(buf
);
138 g_warning(_("Cache data is corrupted\n"));
143 #define READ_CACHE_DATA(data, fp) \
145 if (procmsg_read_cache_data_str(fp, &data) < 0) { \
146 procmsg_msginfo_free(msginfo); \
151 #define READ_CACHE_DATA_INT(n, fp) \
153 if (fread(&n, sizeof(n), 1, fp) != 1) { \
154 g_warning(_("Cache data is corrupted\n")); \
155 procmsg_msginfo_free(msginfo); \
160 GSList
*procmsg_read_cache(FolderItem
*item
, gboolean scan_file
)
162 GSList
*mlist
= NULL
;
167 MsgFlags default_flags
;
168 gchar file_buf
[BUFFSIZE
];
173 g_return_val_if_fail(item
!= NULL
, NULL
);
174 g_return_val_if_fail(item
->folder
!= NULL
, NULL
);
175 type
= item
->folder
->type
;
177 default_flags
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
178 default_flags
.tmp_flags
= MSG_CACHED
;
180 if (item
->stype
== F_QUEUE
) {
181 MSG_SET_TMP_FLAGS(default_flags
, MSG_QUEUED
);
182 } else if (item
->stype
== F_DRAFT
) {
183 MSG_SET_TMP_FLAGS(default_flags
, MSG_DRAFT
);
185 } else if (type
== F_IMAP
) {
186 MSG_SET_TMP_FLAGS(default_flags
, MSG_IMAP
);
187 } else if (type
== F_NEWS
) {
188 MSG_SET_TMP_FLAGS(default_flags
, MSG_NEWS
);
194 path
= folder_item_get_path(item
);
195 if (change_dir(path
) < 0) {
201 cache_file
= folder_item_get_cache_file(item
);
202 if ((fp
= fopen(cache_file
, "r")) == NULL
) {
203 debug_print(_("\tNo cache file\n"));
207 setvbuf(fp
, file_buf
, _IOFBF
, sizeof(file_buf
));
210 debug_print(_("\tReading summary cache..."));
212 /* compare cache version */
213 if (fread(&ver
, sizeof(ver
), 1, fp
) != 1 ||
214 CACHE_VERSION
!= ver
) {
215 debug_print(_("Cache version is different. Discarding it.\n"));
220 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
221 msginfo
= g_new0(MsgInfo
, 1);
222 msginfo
->msgnum
= num
;
223 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
224 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
225 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
226 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
228 READ_CACHE_DATA(msginfo
->fromname
, fp
);
230 READ_CACHE_DATA(msginfo
->date
, fp
);
231 READ_CACHE_DATA(msginfo
->from
, fp
);
232 READ_CACHE_DATA(msginfo
->to
, fp
);
233 READ_CACHE_DATA(msginfo
->cc
, fp
);
234 READ_CACHE_DATA(msginfo
->newsgroups
, fp
);
235 READ_CACHE_DATA(msginfo
->subject
, fp
);
236 READ_CACHE_DATA(msginfo
->msgid
, fp
);
237 READ_CACHE_DATA(msginfo
->inreplyto
, fp
);
238 READ_CACHE_DATA(msginfo
->references
, fp
);
240 MSG_SET_PERM_FLAGS(msginfo
->flags
, default_flags
.perm_flags
);
241 MSG_SET_TMP_FLAGS(msginfo
->flags
, default_flags
.tmp_flags
);
243 /* if the message file doesn't exist or is changed,
244 don't add the data */
245 if (type
== F_MH
&& scan_file
&&
246 folder_item_is_msg_changed(item
, msginfo
))
247 procmsg_msginfo_free(msginfo
);
249 msginfo
->folder
= item
;
252 last
= mlist
= g_slist_append(NULL
, msginfo
);
254 last
= g_slist_append(last
, msginfo
);
261 debug_print(_("done.\n"));
266 #undef READ_CACHE_DATA
267 #undef READ_CACHE_DATA_INT
269 void procmsg_set_flags(GSList
*mlist
, FolderItem
*item
)
276 GHashTable
*mark_table
;
280 g_return_if_fail(item
!= NULL
);
281 g_return_if_fail(item
->folder
!= NULL
);
283 debug_print(_("\tMarking the messages..."));
285 markdir
= folder_item_get_path(item
);
286 if (!is_dir_exist(markdir
))
287 make_dir_hier(markdir
);
289 mark_table
= procmsg_read_mark_file(markdir
);
292 if (!mark_table
) return;
294 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
295 msginfo
= (MsgInfo
*)cur
->data
;
297 if (lastnum
< msginfo
->msgnum
)
298 lastnum
= msginfo
->msgnum
;
300 flags
= g_hash_table_lookup
301 (mark_table
, GUINT_TO_POINTER(msginfo
->msgnum
));
304 /* add the permanent flags only */
305 msginfo
->flags
.perm_flags
= flags
->perm_flags
;
306 if (item
->folder
->type
== F_IMAP
) {
307 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_IMAP
);
308 } else if (item
->folder
->type
== F_NEWS
) {
309 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_NEWS
);
312 /* not found (new message) */
314 for (tmp
= mlist
; tmp
!= cur
; tmp
= tmp
->next
)
316 (((MsgInfo
*)tmp
->data
)->flags
,
323 item
->last_num
= lastnum
;
325 debug_print(_("done.\n"));
327 debug_print(_("\t%d new message(s)\n"), newmsg
);
329 hash_free_value_mem(mark_table
);
330 g_hash_table_destroy(mark_table
);
333 gint
procmsg_get_last_num_in_cache(GSList
*mlist
)
339 if (mlist
== NULL
) return 0;
341 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
342 msginfo
= (MsgInfo
*)cur
->data
;
343 if (msginfo
&& msginfo
->msgnum
> last
)
344 last
= msginfo
->msgnum
;
350 void procmsg_msg_list_free(GSList
*mlist
)
355 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
356 msginfo
= (MsgInfo
*)cur
->data
;
357 procmsg_msginfo_free(msginfo
);
362 void procmsg_write_cache(MsgInfo
*msginfo
, FILE *fp
)
364 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
366 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
367 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
368 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
369 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
370 WRITE_CACHE_DATA_INT(flags
, fp
);
372 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
374 WRITE_CACHE_DATA(msginfo
->date
, fp
);
375 WRITE_CACHE_DATA(msginfo
->from
, fp
);
376 WRITE_CACHE_DATA(msginfo
->to
, fp
);
377 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
378 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
379 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
380 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
381 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
382 WRITE_CACHE_DATA(msginfo
->references
, fp
);
385 void procmsg_write_flags(MsgInfo
*msginfo
, FILE *fp
)
387 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
389 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
390 WRITE_CACHE_DATA_INT(flags
, fp
);
399 static void mark_sum_func(gpointer key
, gpointer value
, gpointer data
)
401 MsgFlags
*flags
= value
;
402 struct MarkSum
*marksum
= data
;
404 /*if (MSG_IS_NEW(*flags) && !MSG_IS_IGNORE_THREAD(*flags)) (*marksum->new)++;*/
405 if (MSG_IS_NEW(*flags
)) (*marksum
->new)++;
406 /*if (MSG_IS_UNREAD(*flags) && !MSG_IS_IGNORE_THREAD(*flags)) (*marksum->unread)++;*/
407 if (MSG_IS_UNREAD(*flags
)) (*marksum
->unread
)++;
411 void procmsg_get_mark_sum(const gchar
*folder
,
412 gint
*new, gint
*unread
, gint
*total
)
414 GHashTable
*mark_table
;
415 struct MarkSum marksum
;
417 *new = *unread
= *total
= 0;
419 marksum
.unread
= unread
;
420 marksum
.total
= total
;
422 mark_table
= procmsg_read_mark_file(folder
);
425 g_hash_table_foreach(mark_table
, mark_sum_func
, &marksum
);
426 g_hash_table_destroy(mark_table
);
430 static GHashTable
*procmsg_read_mark_file(const gchar
*folder
)
433 GHashTable
*mark_table
= NULL
;
436 MsgPermFlags perm_flags
;
438 if ((fp
= procmsg_open_mark_file(folder
, FALSE
)) == NULL
)
441 mark_table
= g_hash_table_new(NULL
, g_direct_equal
);
443 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
444 if (fread(&perm_flags
, sizeof(flags
), 1, fp
) != 1) break;
446 flags
= g_new0(MsgFlags
, 1);
447 flags
->perm_flags
= perm_flags
;
449 g_hash_table_insert(mark_table
, GUINT_TO_POINTER(num
), flags
);
456 FILE *procmsg_open_mark_file(const gchar
*folder
, gboolean append
)
462 markfile
= g_strconcat(folder
, G_DIR_SEPARATOR_S
, MARK_FILE
, NULL
);
464 if ((fp
= fopen(markfile
, "r")) == NULL
)
465 debug_print(_("Mark file not found.\n"));
466 else if (fread(&ver
, sizeof(ver
), 1, fp
) != 1 || MARK_VERSION
!= ver
) {
467 debug_print(_("Mark version is different (%d != %d). "
468 "Discarding it.\n"), ver
, MARK_VERSION
);
474 if (append
== FALSE
) {
480 /* reopen with append mode */
482 if ((fp
= fopen(markfile
, "a")) == NULL
)
483 g_warning(_("Can't open mark file with append mode.\n"));
485 /* open with overwrite mode if mark file doesn't exist or
486 version is different */
487 if ((fp
= fopen(markfile
, "w")) == NULL
)
488 g_warning(_("Can't open mark file with write mode.\n"));
491 WRITE_CACHE_DATA_INT(ver
, fp
);
499 void procmsg_move_messages(GSList
*mlist
)
501 GSList
*cur
, *movelist
= NULL
;
503 FolderItem
*dest
= NULL
;
508 hash
= procmsg_to_folder_hash_table_create(mlist
);
509 folder_item_scan_foreach(hash
);
510 g_hash_table_destroy(hash
);
512 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
513 msginfo
= (MsgInfo
*)cur
->data
;
515 dest
= msginfo
->to_folder
;
516 movelist
= g_slist_append(movelist
, msginfo
);
517 } else if (dest
== msginfo
->to_folder
) {
518 movelist
= g_slist_append(movelist
, msginfo
);
520 folder_item_move_msgs_with_dest(dest
, movelist
);
521 g_slist_free(movelist
);
523 dest
= msginfo
->to_folder
;
524 movelist
= g_slist_append(movelist
, msginfo
);
529 folder_item_move_msgs_with_dest(dest
, movelist
);
530 g_slist_free(movelist
);
534 void procmsg_copy_messages(GSList
*mlist
)
536 GSList
*cur
, *copylist
= NULL
;
538 FolderItem
*dest
= NULL
;
543 hash
= procmsg_to_folder_hash_table_create(mlist
);
544 folder_item_scan_foreach(hash
);
545 g_hash_table_destroy(hash
);
547 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
548 msginfo
= (MsgInfo
*)cur
->data
;
550 dest
= msginfo
->to_folder
;
551 copylist
= g_slist_append(copylist
, msginfo
);
552 } else if (dest
== msginfo
->to_folder
) {
553 copylist
= g_slist_append(copylist
, msginfo
);
555 folder_item_copy_msgs_with_dest(dest
, copylist
);
556 g_slist_free(copylist
);
558 dest
= msginfo
->to_folder
;
559 copylist
= g_slist_append(copylist
, msginfo
);
564 folder_item_copy_msgs_with_dest(dest
, copylist
);
565 g_slist_free(copylist
);
569 gchar
*procmsg_get_message_file_path(MsgInfo
*msginfo
)
573 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
575 if (msginfo
->plaintext_file
)
576 file
= g_strdup(msginfo
->plaintext_file
);
578 path
= folder_item_get_path(msginfo
->folder
);
579 file
= g_strconcat(path
, G_DIR_SEPARATOR_S
,
580 itos(msginfo
->msgnum
), NULL
);
587 gchar
*procmsg_get_message_file(MsgInfo
*msginfo
)
589 gchar
*filename
= NULL
;
591 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
593 filename
= folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
595 g_warning(_("can't fetch message %d\n"), msginfo
->msgnum
);
600 FILE *procmsg_open_message(MsgInfo
*msginfo
)
605 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
607 file
= procmsg_get_message_file_path(msginfo
);
608 g_return_val_if_fail(file
!= NULL
, NULL
);
610 if ((fp
= fopen(file
, "r")) == NULL
) {
611 FILE_OP_ERROR(file
, "fopen");
618 if (MSG_IS_QUEUED(msginfo
->flags
)) {
621 while (fgets(buf
, sizeof(buf
), fp
) != NULL
)
622 if (buf
[0] == '\r' || buf
[0] == '\n') break;
628 gboolean
procmsg_msg_exist(MsgInfo
*msginfo
)
633 if (!msginfo
) return FALSE
;
635 path
= procmsg_get_message_file_path(msginfo
);
637 ret
= !folder_item_is_msg_changed(msginfo
->folder
, msginfo
);
643 void procmsg_empty_trash(void)
648 for (cur
= folder_get_list(); cur
!= NULL
; cur
= cur
->next
) {
649 trash
= FOLDER(cur
->data
)->trash
;
650 if (trash
) folder_item_remove_all_msg(trash
);
654 gint
procmsg_send_queue(void)
659 queue
= folder_get_default_queue();
660 g_return_val_if_fail(queue
!= NULL
, -1);
661 folder_item_scan(queue
);
662 if (queue
->last_num
< 0) return -1;
663 else if (queue
->last_num
== 0) return 0;
665 for (i
= 1; i
<= queue
->last_num
; i
++) {
668 file
= folder_item_fetch_msg(queue
, i
);
670 if (send_message_queue(file
) < 0) {
671 g_warning(_("Sending queued message failed.\n"));
675 folder_item_remove_msg(queue
, i
);
683 void procmsg_print_message(MsgInfo
*msginfo
, const gchar
*cmdline
)
685 static const gchar
*def_cmd
= "lpr %s";
692 g_return_if_fail(msginfo
);
694 if ((tmpfp
= procmime_get_text_part(msginfo
)) == NULL
) {
695 g_warning(_("Can't get text part\n"));
699 prtmp
= g_strdup_printf("%s%cprinttmp.%08x",
700 get_mime_tmp_dir(), G_DIR_SEPARATOR
, id
++);
702 if ((prfp
= fopen(prtmp
, "w")) == NULL
) {
703 FILE_OP_ERROR(prtmp
, "fopen");
709 if (msginfo
->date
) fprintf(prfp
, "Date: %s\n", msginfo
->date
);
710 if (msginfo
->from
) fprintf(prfp
, "From: %s\n", msginfo
->from
);
711 if (msginfo
->to
) fprintf(prfp
, "To: %s\n", msginfo
->to
);
712 if (msginfo
->cc
) fprintf(prfp
, "Cc: %s\n", msginfo
->cc
);
713 if (msginfo
->newsgroups
)
714 fprintf(prfp
, "Newsgroups: %s\n", msginfo
->newsgroups
);
715 if (msginfo
->subject
) fprintf(prfp
, "Subject: %s\n", msginfo
->subject
);
718 while (fgets(buf
, sizeof(buf
), tmpfp
) != NULL
)
724 if (cmdline
&& (p
= strchr(cmdline
, '%')) && *(p
+ 1) == 's' &&
726 g_snprintf(buf
, sizeof(buf
) - 1, cmdline
, prtmp
);
729 g_warning(_("Print command line is invalid: `%s'\n"),
731 g_snprintf(buf
, sizeof(buf
) - 1, def_cmd
, prtmp
);
737 if (buf
[strlen(buf
) - 1] != '&') strcat(buf
, "&");
741 MsgInfo
*procmsg_msginfo_copy(MsgInfo
*msginfo
)
745 if (msginfo
== NULL
) return NULL
;
747 newmsginfo
= g_new0(MsgInfo
, 1);
749 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
750 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
751 g_strdup(msginfo->mmb) : NULL
774 MEMBDUP(dispositionnotificationto
);
775 MEMBDUP(returnreceiptto
);
779 MEMBCOPY(threadscore
);
784 void procmsg_msginfo_free(MsgInfo
*msginfo
)
786 if (msginfo
== NULL
) return;
788 g_free(msginfo
->fromspace
);
789 g_free(msginfo
->references
);
790 g_free(msginfo
->returnreceiptto
);
791 g_free(msginfo
->dispositionnotificationto
);
792 g_free(msginfo
->xface
);
794 g_free(msginfo
->fromname
);
796 g_free(msginfo
->date
);
797 g_free(msginfo
->from
);
800 g_free(msginfo
->newsgroups
);
801 g_free(msginfo
->subject
);
802 g_free(msginfo
->msgid
);
803 g_free(msginfo
->inreplyto
);
808 static gint
procmsg_cmp_msgnum(gconstpointer a
, gconstpointer b
)
810 const MsgInfo
*msginfo
= a
;
811 const guint msgnum
= GPOINTER_TO_UINT(b
);
816 return msginfo
->msgnum
- msgnum
;
819 gint
procmsg_cmp_msgnum_for_sort(gconstpointer a
, gconstpointer b
)
821 const MsgInfo
*msginfo1
= a
;
822 const MsgInfo
*msginfo2
= b
;
829 return msginfo1
->msgnum
- msginfo2
->msgnum
;
832 static gint
procmsg_cmp_flag_msgnum(gconstpointer a
, gconstpointer b
)
834 const FlagInfo
*finfo
= a
;
835 const guint msgnum
= GPOINTER_TO_UINT(b
);
840 return finfo
->msgnum
- msgnum
;