2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
37 # include <sys/time.h>
43 #include "procheader.h"
47 /* Define possible missing constants for Windows. */
60 static void mh_folder_init (Folder
*folder
,
64 static Folder
*mh_folder_new (const gchar
*name
,
66 static void mh_folder_destroy (Folder
*folder
);
67 static gchar
*mh_fetch_msg (Folder
*folder
,
70 static MsgInfo
*mh_get_msginfo (Folder
*folder
,
73 static gint
mh_add_msg (Folder
*folder
,
77 static gint
mh_add_msgs (Folder
*folder
,
81 static gint
mh_copy_msg (Folder
*folder
,
84 static gint
mh_copy_msgs (Folder
*folder
,
88 static gint
mh_remove_msg (Folder
*folder
,
91 static gint
mh_remove_all_msg (Folder
*folder
,
93 static gboolean
mh_is_msg_changed (Folder
*folder
,
97 static gint
mh_get_num_list (Folder
*folder
,
100 gboolean
*old_uids_valid
);
101 static gint
mh_scan_tree (Folder
*folder
);
103 static gint
mh_create_tree (Folder
*folder
);
104 static FolderItem
*mh_create_folder (Folder
*folder
,
107 static gint
mh_rename_folder (Folder
*folder
,
110 static gint
mh_remove_folder (Folder
*folder
,
113 static gchar
*mh_get_new_msg_filename (FolderItem
*dest
);
115 static MsgInfo
*mh_parse_msg (const gchar
*file
,
117 static void mh_remove_missing_folder_items (Folder
*folder
);
118 static gchar
*mh_filename_from_utf8 (const gchar
*path
);
119 static gchar
*mh_filename_to_utf8 (const gchar
*path
);
120 static void mh_scan_tree_recursive (FolderItem
*item
);
122 static gboolean
mh_rename_folder_func (GNode
*node
,
124 static gchar
*mh_item_get_path (Folder
*folder
,
127 static gboolean
mh_scan_required (Folder
*folder
,
129 static int mh_item_close (Folder
*folder
,
131 static gint
mh_get_flags (Folder
*folder
, FolderItem
*item
,
132 MsgInfoList
*msginfo_list
, GRelation
*msgflags
);
133 static void mh_write_sequences (FolderItem
*item
);
135 static FolderClass mh_class
;
137 FolderClass
*mh_get_class(void)
139 if (mh_class
.idstr
== NULL
) {
140 mh_class
.type
= F_MH
;
141 mh_class
.idstr
= "mh";
142 mh_class
.uistr
= "MH";
144 /* Folder functions */
145 mh_class
.new_folder
= mh_folder_new
;
146 mh_class
.destroy_folder
= mh_folder_destroy
;
147 mh_class
.set_xml
= folder_local_set_xml
;
148 mh_class
.get_xml
= folder_local_get_xml
;
149 mh_class
.scan_tree
= mh_scan_tree
;
150 mh_class
.create_tree
= mh_create_tree
;
152 /* FolderItem functions */
153 mh_class
.item_get_path
= mh_item_get_path
;
154 mh_class
.create_folder
= mh_create_folder
;
155 mh_class
.rename_folder
= mh_rename_folder
;
156 mh_class
.remove_folder
= mh_remove_folder
;
157 mh_class
.get_num_list
= mh_get_num_list
;
158 mh_class
.scan_required
= mh_scan_required
;
159 mh_class
.close
= mh_item_close
;
160 mh_class
.get_flags
= mh_get_flags
;
162 /* Message functions */
163 mh_class
.get_msginfo
= mh_get_msginfo
;
164 mh_class
.fetch_msg
= mh_fetch_msg
;
165 mh_class
.add_msg
= mh_add_msg
;
166 mh_class
.add_msgs
= mh_add_msgs
;
167 mh_class
.copy_msg
= mh_copy_msg
;
168 mh_class
.copy_msgs
= mh_copy_msgs
;
169 mh_class
.remove_msg
= mh_remove_msg
;
170 mh_class
.remove_all_msg
= mh_remove_all_msg
;
171 mh_class
.is_msg_changed
= mh_is_msg_changed
;
177 static Folder
*mh_folder_new(const gchar
*name
, const gchar
*path
)
181 folder
= (Folder
*)g_new0(MHFolder
, 1);
182 folder
->klass
= &mh_class
;
183 mh_folder_init(folder
, name
, path
);
188 static void mh_folder_destroy(Folder
*folder
)
190 folder_local_folder_destroy(LOCAL_FOLDER(folder
));
193 static void mh_folder_init(Folder
*folder
, const gchar
*name
, const gchar
*path
)
195 folder_local_folder_init(folder
, name
, path
);
199 gboolean
mh_scan_required(Folder
*folder
, FolderItem
*item
)
204 path
= folder_item_get_path(item
);
205 g_return_val_if_fail(path
!= NULL
, FALSE
);
207 if (stat(path
, &s
) < 0) {
208 FILE_OP_ERROR(path
, "stat");
213 if ((s
.st_mtime
> item
->mtime
) &&
214 (s
.st_mtime
- 3600 != item
->mtime
)) {
215 debug_print("MH scan required, folder updated: %s (%ld > %ld)\n",
217 (long int) s
.st_mtime
,
218 (long int) item
->mtime
);
223 debug_print("MH scan not required: %s (%ld <= %ld)\n",
225 (long int) s
.st_mtime
,
226 (long int) item
->mtime
);
231 void mh_get_last_num(Folder
*folder
, FolderItem
*item
)
239 g_return_if_fail(item
!= NULL
);
241 debug_print("mh_get_last_num(): Scanning %s ...\n", item
->path
);
243 path
= folder_item_get_path(item
);
244 g_return_if_fail(path
!= NULL
);
245 if (change_dir(path
) < 0) {
251 if ((dp
= opendir(".")) == NULL
) {
252 FILE_OP_ERROR(item
->path
, "opendir");
256 while ((d
= readdir(dp
)) != NULL
) {
257 if ((num
= to_number(d
->d_name
)) > 0 &&
258 dirent_is_regular_file(d
)) {
265 debug_print("Last number in dir %s = %d\n", item
->path
, max
);
266 item
->last_num
= max
;
269 gint
mh_get_num_list(Folder
*folder
, FolderItem
*item
, GSList
**list
, gboolean
*old_uids_valid
)
275 gint num
, nummsgs
= 0;
277 g_return_val_if_fail(item
!= NULL
, -1);
279 debug_print("mh_get_num_list(): Scanning %s ...\n", item
->path
);
281 *old_uids_valid
= TRUE
;
283 path
= folder_item_get_path(item
);
284 g_return_val_if_fail(path
!= NULL
, -1);
285 if (change_dir(path
) < 0) {
291 if ((dp
= opendir(".")) == NULL
) {
292 FILE_OP_ERROR(item
->path
, "opendir");
296 while ((d
= readdir(dp
)) != NULL
) {
297 if ((num
= to_number(d
->d_name
)) > 0) {
298 *list
= g_slist_prepend(*list
, GINT_TO_POINTER(num
));
304 item
->mtime
= time(NULL
);
308 static gchar
*mh_fetch_msg(Folder
*folder
, FolderItem
*item
, gint num
)
313 g_return_val_if_fail(item
!= NULL
, NULL
);
314 g_return_val_if_fail(num
> 0, NULL
);
316 path
= folder_item_get_path(item
);
317 file
= g_strconcat(path
, G_DIR_SEPARATOR_S
, itos(num
), NULL
);
319 if (!is_file_exist(file
)) {
328 static MsgInfo
*mh_get_msginfo(Folder
*folder
, FolderItem
*item
, gint num
)
333 g_return_val_if_fail(item
!= NULL
, NULL
);
337 file
= mh_fetch_msg(folder
, item
, num
);
338 if (!file
) return NULL
;
340 msginfo
= mh_parse_msg(file
, item
);
342 msginfo
->msgnum
= num
;
349 static gchar
*mh_get_new_msg_filename(FolderItem
*dest
)
354 destpath
= folder_item_get_path(dest
);
355 g_return_val_if_fail(destpath
!= NULL
, NULL
);
357 if (!is_dir_exist(destpath
))
358 make_dir_hier(destpath
);
361 destfile
= g_strdup_printf("%s%c%d", destpath
, G_DIR_SEPARATOR
,
363 if (is_file_entry_exist(destfile
)) {
375 static gint
mh_add_msg(Folder
*folder
, FolderItem
*dest
, const gchar
*file
, MsgFlags
*flags
)
379 MsgFileInfo fileinfo
;
381 g_return_val_if_fail(file
!= NULL
, -1);
383 fileinfo
.msginfo
= NULL
;
384 fileinfo
.file
= (gchar
*)file
;
385 fileinfo
.flags
= flags
;
386 file_list
.data
= &fileinfo
;
387 file_list
.next
= NULL
;
389 ret
= mh_add_msgs(folder
, dest
, &file_list
, NULL
);
393 static gint
mh_add_msgs(Folder
*folder
, FolderItem
*dest
, GSList
*file_list
,
398 MsgFileInfo
*fileinfo
;
400 g_return_val_if_fail(dest
!= NULL
, -1);
401 g_return_val_if_fail(file_list
!= NULL
, -1);
403 if (dest
->last_num
< 0) {
404 mh_get_last_num(folder
, dest
);
405 if (dest
->last_num
< 0) return -1;
408 for (cur
= file_list
; cur
!= NULL
; cur
= cur
->next
) {
409 fileinfo
= (MsgFileInfo
*)cur
->data
;
411 destfile
= mh_get_new_msg_filename(dest
);
412 if (destfile
== NULL
) return -1;
415 if (link(fileinfo
->file
, destfile
) < 0) {
417 if (copy_file(fileinfo
->file
, destfile
, TRUE
) < 0) {
418 g_warning(_("can't copy message %s to %s\n"),
419 fileinfo
->file
, destfile
);
427 if (relation
!= NULL
)
428 g_relation_insert(relation
, fileinfo
, GINT_TO_POINTER(dest
->last_num
+ 1));
432 mh_write_sequences(dest
);
433 return dest
->last_num
;
436 static gint
mh_copy_msg(Folder
*folder
, FolderItem
*dest
, MsgInfo
*msginfo
)
440 g_return_val_if_fail(msginfo
!= NULL
, -1);
442 msglist
.data
= msginfo
;
445 return mh_copy_msgs(folder
, dest
, &msglist
, NULL
);
448 static gint
mh_copy_msgs(Folder
*folder
, FolderItem
*dest
, MsgInfoList
*msglist
,
451 gboolean dest_need_scan
= FALSE
;
455 FolderItemPrefs
*prefs
;
456 MsgInfo
*msginfo
= NULL
;
457 gboolean remove_special_headers
= FALSE
;
458 MsgInfoList
*cur
= NULL
;
459 g_return_val_if_fail(dest
!= NULL
, -1);
460 g_return_val_if_fail(msglist
!= NULL
, -1);
462 msginfo
= (MsgInfo
*)msglist
->data
;
464 g_return_val_if_fail(msginfo
!= NULL
, -1);
466 if (msginfo
->folder
== dest
) {
467 g_warning("the src folder is identical to the dest.\n");
471 if (dest
->last_num
< 0) {
472 mh_get_last_num(folder
, dest
);
473 if (dest
->last_num
< 0) return -1;
476 if ((MSG_IS_QUEUED(msginfo
->flags
) || MSG_IS_DRAFT(msginfo
->flags
))
477 && !folder_has_parent_of_type(dest
, F_QUEUE
)
478 && !folder_has_parent_of_type(dest
, F_DRAFT
)) {
479 /* as every msginfo comes from the same folder, it is supposed they
480 * will either match the preceding condition either all or none.
482 remove_special_headers
= TRUE
;
483 } else if (!(MSG_IS_QUEUED(msginfo
->flags
) || MSG_IS_DRAFT(msginfo
->flags
))
484 && (folder_has_parent_of_type(dest
, F_QUEUE
)
485 || folder_has_parent_of_type(dest
, F_DRAFT
))) {
491 for (cur
= msglist
; cur
; cur
= cur
->next
) {
492 msginfo
= (MsgInfo
*)cur
->data
;
495 srcfile
= procmsg_get_message_file(msginfo
);
496 destfile
= mh_get_new_msg_filename(dest
);
502 debug_print("Copying message %s%c%d to %s ...\n",
503 msginfo
->folder
->path
, G_DIR_SEPARATOR
,
504 msginfo
->msgnum
, dest
->path
);
507 if (remove_special_headers
) {
508 if (procmsg_remove_special_headers(srcfile
, destfile
) !=0) {
513 } else if (copy_file(srcfile
, destfile
, TRUE
) < 0) {
514 FILE_OP_ERROR(srcfile
, "copy");
519 if (prefs
&& prefs
->enable_folder_chmod
&& prefs
->folder_chmod
) {
520 if (chmod(destfile
, prefs
->folder_chmod
) < 0)
521 FILE_OP_ERROR(destfile
, "chmod");
524 filemode
= prefs
->folder_chmod
;
525 if (filemode
& S_IRGRP
) filemode
|= S_IWGRP
;
526 if (filemode
& S_IROTH
) filemode
|= S_IWOTH
;
529 g_relation_insert(relation
, msginfo
, GINT_TO_POINTER(dest
->last_num
+1));
535 dest_need_scan
= mh_scan_required(dest
->folder
, dest
);
537 dest
->mtime
= time(NULL
);
539 mh_write_sequences(dest
);
541 return dest
->last_num
;
544 static gint
mh_remove_msg(Folder
*folder
, FolderItem
*item
, gint num
)
546 gboolean need_scan
= FALSE
;
549 g_return_val_if_fail(item
!= NULL
, -1);
551 file
= mh_fetch_msg(folder
, item
, num
);
552 g_return_val_if_fail(file
!= NULL
, -1);
554 need_scan
= mh_scan_required(folder
, item
);
556 if (g_unlink(file
) < 0) {
557 FILE_OP_ERROR(file
, "unlink");
563 item
->mtime
= time(NULL
);
569 static gint
mh_remove_all_msg(Folder
*folder
, FolderItem
*item
)
574 g_return_val_if_fail(item
!= NULL
, -1);
576 path
= folder_item_get_path(item
);
577 g_return_val_if_fail(path
!= NULL
, -1);
578 val
= remove_all_numbered_files(path
);
581 mh_write_sequences(item
);
586 static gboolean
mh_is_msg_changed(Folder
*folder
, FolderItem
*item
,
591 if (g_stat(itos(msginfo
->msgnum
), &s
) < 0 ||
592 msginfo
->size
!= s
.st_size
|| (
593 (msginfo
->mtime
- s
.st_mtime
!= 0) &&
594 (msginfo
->mtime
- s
.st_mtime
!= 3600) &&
595 (msginfo
->mtime
- s
.st_mtime
!= -3600)))
601 static gint
mh_scan_tree(Folder
*folder
)
606 g_return_val_if_fail(folder
!= NULL
, -1);
609 item
= folder_item_new(folder
, folder
->name
, NULL
);
610 item
->folder
= folder
;
611 folder
->node
= item
->node
= g_node_new(item
);
613 item
= FOLDER_ITEM(folder
->node
->data
);
615 rootpath
= folder_item_get_path(item
);
616 if (change_dir(rootpath
) < 0) {
622 mh_create_tree(folder
);
623 mh_remove_missing_folder_items(folder
);
624 mh_scan_tree_recursive(item
);
629 #define MAKE_DIR_IF_NOT_EXIST(dir) \
631 if (!is_dir_exist(dir)) { \
632 if (is_file_exist(dir)) { \
633 g_warning("File `%s' already exists.\n" \
634 "Can't create folder.", dir); \
637 if (make_dir(dir) < 0) \
642 static gint
mh_create_tree(Folder
*folder
)
646 g_return_val_if_fail(folder
!= NULL
, -1);
648 CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1);
649 rootpath
= LOCAL_FOLDER(folder
)->rootpath
;
650 MAKE_DIR_IF_NOT_EXIST(rootpath
);
651 CHDIR_RETURN_VAL_IF_FAIL(rootpath
, -1);
652 MAKE_DIR_IF_NOT_EXIST(INBOX_DIR
);
653 MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR
);
654 MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR
);
655 MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR
);
656 MAKE_DIR_IF_NOT_EXIST(TRASH_DIR
);
661 #undef MAKE_DIR_IF_NOT_EXIST
663 static gchar
*mh_item_get_path(Folder
*folder
, FolderItem
*item
)
665 gchar
*folder_path
, *path
;
667 g_return_val_if_fail(folder
!= NULL
, NULL
);
668 g_return_val_if_fail(item
!= NULL
, NULL
);
670 folder_path
= g_strdup(LOCAL_FOLDER(folder
)->rootpath
);
671 g_return_val_if_fail(folder_path
!= NULL
, NULL
);
673 /* FIXME: [W32] The code below does not correctly merge
674 relative filenames; there should be a function to handle
676 if ( !is_relative_filename (folder_path
) ) {
678 path
= g_strconcat(folder_path
, G_DIR_SEPARATOR_S
,
681 path
= g_strdup(folder_path
);
684 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
685 folder_path
, G_DIR_SEPARATOR_S
,
688 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
692 real_path
= mh_filename_from_utf8(path
);
693 if (!is_dir_exist(real_path
) && is_dir_exist(path
)) {
694 /* mmh, older version did put utf8 filenames instead of
695 * the correct encoding */
696 rename(path
, real_path
);
697 folder_item_scan(item
);
704 static FolderItem
*mh_create_folder(Folder
*folder
, FolderItem
*parent
,
707 gchar
*path
, *real_name
;
709 FolderItem
*new_item
;
710 gchar
*mh_sequences_filename
;
711 FILE *mh_sequences_file
;
713 g_return_val_if_fail(folder
!= NULL
, NULL
);
714 g_return_val_if_fail(parent
!= NULL
, NULL
);
715 g_return_val_if_fail(name
!= NULL
, NULL
);
717 path
= folder_item_get_path(parent
);
718 if (!is_dir_exist(path
))
719 if (make_dir_hier(path
) != 0)
722 real_name
= mh_filename_from_utf8(name
);
723 fullpath
= g_strconcat(path
, G_DIR_SEPARATOR_S
, real_name
, NULL
);
727 if (make_dir(fullpath
) < 0) {
735 path
= g_strconcat(parent
->path
, G_DIR_SEPARATOR_S
, name
,
738 path
= g_strdup(name
);
739 new_item
= folder_item_new(folder
, name
, path
);
740 folder_item_append(parent
, new_item
);
744 path
= folder_item_get_path(new_item
);
745 mh_sequences_filename
= g_strconcat(path
, G_DIR_SEPARATOR_S
,
746 ".mh_sequences", NULL
);
747 if ((mh_sequences_file
= g_fopen(mh_sequences_filename
, "a+b")) != NULL
) {
748 fclose(mh_sequences_file
);
750 g_free(mh_sequences_filename
);
756 static gint
mh_rename_folder(Folder
*folder
, FolderItem
*item
,
762 gchar
*newpath
, *utf8newpath
;
765 g_return_val_if_fail(folder
!= NULL
, -1);
766 g_return_val_if_fail(item
!= NULL
, -1);
767 g_return_val_if_fail(item
->path
!= NULL
, -1);
768 g_return_val_if_fail(name
!= NULL
, -1);
770 oldpath
= folder_item_get_path(item
);
771 if (!is_dir_exist(oldpath
))
772 make_dir_hier(oldpath
);
774 dirname
= g_path_get_dirname(oldpath
);
775 real_name
= mh_filename_from_utf8(name
);
776 newpath
= g_strconcat(dirname
, G_DIR_SEPARATOR_S
, real_name
, NULL
);
779 if (g_rename(oldpath
, newpath
) < 0) {
780 FILE_OP_ERROR(oldpath
, "rename");
789 if (strchr(item
->path
, G_DIR_SEPARATOR
) != NULL
) {
790 dirname
= g_path_get_dirname(item
->path
);
791 utf8newpath
= g_strconcat(dirname
, G_DIR_SEPARATOR_S
,
795 utf8newpath
= g_strdup(name
);
798 item
->name
= g_strdup(name
);
800 paths
[0] = g_strdup(item
->path
);
801 paths
[1] = utf8newpath
;
802 g_node_traverse(item
->node
, G_PRE_ORDER
, G_TRAVERSE_ALL
, -1,
803 mh_rename_folder_func
, paths
);
810 static gint
mh_remove_folder(Folder
*folder
, FolderItem
*item
)
814 g_return_val_if_fail(folder
!= NULL
, -1);
815 g_return_val_if_fail(item
!= NULL
, -1);
816 g_return_val_if_fail(item
->path
!= NULL
, -1);
818 path
= folder_item_get_path(item
);
819 if (remove_dir_recursive(path
) < 0) {
820 g_warning("can't remove directory `%s'\n", path
);
826 folder_item_remove(item
);
830 static MsgInfo
*mh_parse_msg(const gchar
*file
, FolderItem
*item
)
835 g_return_val_if_fail(item
!= NULL
, NULL
);
836 g_return_val_if_fail(file
!= NULL
, NULL
);
838 flags
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
841 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
842 MSG_SET_TMP_FLAGS(flags
, MSG_QUEUED
);
843 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
844 MSG_SET_TMP_FLAGS(flags
, MSG_DRAFT
);
847 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
848 if (!msginfo
) return NULL
;
850 msginfo
->msgnum
= atoi(file
);
851 msginfo
->folder
= item
;
856 static gboolean
mh_remove_missing_folder_items_func(GNode
*node
, gpointer data
)
861 g_return_val_if_fail(node
->data
!= NULL
, FALSE
);
863 if (G_NODE_IS_ROOT(node
))
866 item
= FOLDER_ITEM(node
->data
);
868 path
= folder_item_get_path(item
);
869 if (!is_dir_exist(path
)) {
870 debug_print("folder '%s' not found. removing...\n", path
);
871 folder_item_remove(item
);
878 static void mh_remove_missing_folder_items(Folder
*folder
)
880 g_return_if_fail(folder
!= NULL
);
882 debug_print("searching missing folders...\n");
884 g_node_traverse(folder
->node
, G_POST_ORDER
, G_TRAVERSE_ALL
, -1,
885 mh_remove_missing_folder_items_func
, folder
);
888 static void mh_scan_tree_recursive(FolderItem
*item
)
897 const gchar
*dir_name
;
899 gchar
*real_path
, *entry
, *utf8entry
, *utf8name
;
902 g_return_if_fail(item
!= NULL
);
903 g_return_if_fail(item
->folder
!= NULL
);
905 folder
= item
->folder
;
907 real_path
= item
->path
? mh_filename_from_utf8(item
->path
) : g_strdup(".");
909 dir
= g_dir_open(real_path
, 0, NULL
);
911 g_warning("failed to open directory: %s\n", real_path
);
916 dp
= opendir(real_path
);
918 FILE_OP_ERROR(real_path
, "opendir");
924 debug_print("scanning %s ...\n",
925 item
->path
? item
->path
926 : LOCAL_FOLDER(item
->folder
)->rootpath
);
928 folder
->ui_func(folder
, item
, folder
->ui_func_data
);
931 while ((dir_name
= g_dir_read_name(dir
)) != NULL
) {
933 while ((d
= readdir(dp
)) != NULL
) {
934 dir_name
= d
->d_name
;
936 if (dir_name
[0] == '.') continue;
938 utf8name
= mh_filename_to_utf8(dir_name
);
940 utf8entry
= g_strconcat(item
->path
, G_DIR_SEPARATOR_S
,
943 utf8entry
= g_strdup(utf8name
);
944 entry
= mh_filename_from_utf8(utf8entry
);
947 #if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
948 d
->d_type
== DT_DIR
||
949 (d
->d_type
== DT_UNKNOWN
&&
951 g_stat(entry
, &s
) == 0 && S_ISDIR(s
.st_mode
)
952 #if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
956 FolderItem
*new_item
= NULL
;
960 for (node
= node
->children
; node
!= NULL
; node
= node
->next
) {
961 FolderItem
*cur_item
= FOLDER_ITEM(node
->data
);
962 if (!strcmp2(cur_item
->path
, entry
)) {
968 debug_print("new folder '%s' found.\n", entry
);
969 new_item
= folder_item_new(folder
, utf8name
, utf8entry
);
970 folder_item_append(item
, new_item
);
974 if (!folder
->inbox
&&
975 !strcmp(dir_name
, INBOX_DIR
)) {
976 new_item
->stype
= F_INBOX
;
977 folder
->inbox
= new_item
;
978 } else if (!folder
->outbox
&&
979 !strcmp(dir_name
, OUTBOX_DIR
)) {
980 new_item
->stype
= F_OUTBOX
;
981 folder
->outbox
= new_item
;
982 } else if (!folder
->draft
&&
983 !strcmp(dir_name
, DRAFT_DIR
)) {
984 new_item
->stype
= F_DRAFT
;
985 folder
->draft
= new_item
;
986 } else if (!folder
->queue
&&
987 !strcmp(dir_name
, QUEUE_DIR
)) {
988 new_item
->stype
= F_QUEUE
;
989 folder
->queue
= new_item
;
990 } else if (!folder
->trash
&&
991 !strcmp(dir_name
, TRASH_DIR
)) {
992 new_item
->stype
= F_TRASH
;
993 folder
->trash
= new_item
;
997 mh_scan_tree_recursive(new_item
);
998 } else if (to_number(dir_name
) > 0) n_msg
++;
1011 item
->mtime
= time(NULL
);
1014 static gboolean
mh_rename_folder_func(GNode
*node
, gpointer data
)
1016 FolderItem
*item
= node
->data
;
1017 gchar
**paths
= data
;
1018 const gchar
*oldpath
= paths
[0];
1019 const gchar
*newpath
= paths
[1];
1021 gchar
*new_itempath
;
1024 oldpathlen
= strlen(oldpath
);
1025 if (strncmp(oldpath
, item
->path
, oldpathlen
) != 0) {
1026 g_warning("path doesn't match: %s, %s\n", oldpath
, item
->path
);
1030 base
= item
->path
+ oldpathlen
;
1031 while (*base
== G_DIR_SEPARATOR
) base
++;
1033 new_itempath
= g_strdup(newpath
);
1035 new_itempath
= g_strconcat(newpath
, G_DIR_SEPARATOR_S
, base
,
1038 item
->path
= new_itempath
;
1043 static gchar
*mh_filename_from_utf8(const gchar
*path
)
1045 gchar
*real_path
= g_filename_from_utf8(path
, -1, NULL
, NULL
, NULL
);
1048 g_warning("mh_filename_from_utf8: faild to convert character set\n");
1049 real_path
= g_strdup(path
);
1055 static gchar
*mh_filename_to_utf8(const gchar
*path
)
1057 gchar
*utf8path
= g_filename_to_utf8(path
, -1, NULL
, NULL
, NULL
);
1059 g_warning("mh_filename_to_utf8: faild to convert character set\n");
1060 utf8path
= g_strdup(path
);
1066 static gint
sort_cache_list_by_msgnum(gconstpointer a
, gconstpointer b
)
1068 MsgInfo
*msginfo_a
= (MsgInfo
*) a
;
1069 MsgInfo
*msginfo_b
= (MsgInfo
*) b
;
1071 return (msginfo_a
->msgnum
- msginfo_b
->msgnum
);
1074 static gchar
*get_unseen_seq_name(void)
1076 static gchar
*seq_name
= NULL
;
1078 gchar buf
[BUFFSIZE
];
1080 gchar
*profile_path
= g_strconcat(
1081 g_get_home_dir(), G_DIR_SEPARATOR_S
,
1082 ".mh_profile", NULL
);
1083 FILE *fp
= g_fopen(profile_path
, "r");
1085 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1086 if (!strncmp(buf
, "Unseen-Sequence:", strlen("Unseen-Sequence:"))) {
1087 gchar
*seq_tmp
= buf
+strlen("Unseen-Sequence:");
1088 while (*seq_tmp
== ' ')
1090 seq_name
= g_strdup(seq_tmp
);
1091 seq_name
= strretchomp(seq_name
);
1098 seq_name
= g_strdup("unseen");
1099 tmp
= g_strdup_printf("%s:", seq_name
);
1106 static gint
mh_get_flags(Folder
*folder
, FolderItem
*item
,
1107 MsgInfoList
*msginfo_list
, GRelation
*msgflags
)
1109 gchar
*mh_sequences_filename
;
1110 FILE *mh_sequences_file
;
1111 gchar buf
[BUFFSIZE
];
1112 gchar
*unseen_list
= NULL
;
1114 MsgInfoList
*mcur
= NULL
;
1116 GTimer *timer = g_timer_new();
1117 g_timer_start(timer);
1122 /* don't update from .mh_sequences if the item's opened: mails may have
1123 * been marked read/unread and it's not yet written in the file. */
1127 path
= folder_item_get_path(item
);
1129 mh_sequences_filename
= g_strconcat(path
, G_DIR_SEPARATOR_S
,
1130 ".mh_sequences", NULL
);
1132 if ((mh_sequences_file
= g_fopen(mh_sequences_filename
, "r+b")) != NULL
) {
1133 while (fgets(buf
, sizeof(buf
), mh_sequences_file
) != NULL
) {
1134 if (!strncmp(buf
, get_unseen_seq_name(), strlen(get_unseen_seq_name()))) {
1135 unseen_list
= g_strdup(buf
+strlen(get_unseen_seq_name()));
1139 fclose(mh_sequences_file
);
1142 g_free(mh_sequences_filename
);
1146 gchar
*token
= NULL
, *next
= NULL
, *boundary
= NULL
;
1148 GHashTable
*unseen_table
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
1150 cur
= unseen_list
= strretchomp(unseen_list
);
1151 debug_print("found unseen list in .mh_sequences: %s\n", unseen_list
);
1153 while (*cur
&& *cur
== ' ')
1156 if ((next
= strchr(cur
, ' ')) != NULL
) {
1165 if ((boundary
= strchr(token
, '-')) != NULL
) {
1171 for (i
= atoi(start
); i
<= atoi(end
); i
++) {
1172 g_hash_table_insert(unseen_table
, GINT_TO_POINTER(i
), GINT_TO_POINTER(1));
1174 } else if ((num
= atoi(token
)) > 0) {
1175 g_hash_table_insert(unseen_table
, GINT_TO_POINTER(num
), GINT_TO_POINTER(1));
1180 for (mcur
= msginfo_list
; mcur
; mcur
= mcur
->next
) {
1181 MsgInfo
*msginfo
= (MsgInfo
*)mcur
->data
;
1182 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1183 if (g_hash_table_lookup(unseen_table
, GINT_TO_POINTER(msginfo
->msgnum
))) {
1184 flags
|= MSG_UNREAD
;
1185 } else if (!(flags
& MSG_NEW
)) { /* don't mark new msgs as read */
1186 flags
&= ~(MSG_UNREAD
);
1188 if (flags
!= msginfo
->flags
.perm_flags
)
1189 g_relation_insert(msgflags
, msginfo
, GINT_TO_POINTER(flags
));
1191 g_hash_table_destroy(unseen_table
);
1192 g_free(unseen_list
);
1195 g_timer_stop(timer);
1196 printf("mh_get_flags: %f secs\n", g_timer_elapsed(timer, NULL));
1197 g_timer_destroy(timer);
1202 static void mh_write_sequences(FolderItem
*item
)
1204 gchar
*mh_sequences_old
, *mh_sequences_new
;
1205 FILE *mh_sequences_old_fp
, *mh_sequences_new_fp
;
1206 gchar buf
[BUFFSIZE
];
1209 GTimer *timer = g_timer_new();
1210 g_timer_start(timer);
1215 path
= folder_item_get_path(item
);
1217 mh_sequences_old
= g_strconcat(path
, G_DIR_SEPARATOR_S
,
1218 ".mh_sequences", NULL
);
1219 mh_sequences_new
= g_strconcat(path
, G_DIR_SEPARATOR_S
,
1220 ".mh_sequences.new", NULL
);
1221 if ((mh_sequences_new_fp
= g_fopen(mh_sequences_new
, "w+b")) != NULL
) {
1222 GSList
*msglist
= folder_item_get_msg_list(item
);
1224 MsgInfo
*info
= NULL
;
1225 gint start
= -1, end
= -1;
1226 gchar
*sequence
= g_strdup("");
1227 msglist
= g_slist_sort(msglist
, sort_cache_list_by_msgnum
);
1230 /* write the unseen sequence */
1232 info
= (MsgInfo
*)(cur
? cur
->data
:NULL
);
1233 if (info
&& (MSG_IS_UNREAD(info
->flags
) || MSG_IS_NEW(info
->flags
))) {
1235 start
= end
= info
->msgnum
;
1239 if (start
> 0 && end
> 0) {
1240 gchar
*tmp
= sequence
;
1242 sequence
= g_strdup_printf("%s %d-%d ", tmp
, start
, end
);
1244 sequence
= g_strdup_printf("%s %d ", tmp
, start
);
1249 cur
= cur
? cur
->next
:NULL
;
1250 } while (cur
|| (start
> 0 && end
> 0));
1251 if (sequence
&& strlen(sequence
)) {
1252 fprintf(mh_sequences_new_fp
, "%s%s\n",
1253 get_unseen_seq_name(), sequence
);
1254 debug_print("wrote unseen sequence: '%s%s'\n",
1255 get_unseen_seq_name(), sequence
);
1257 /* rewrite the rest of the file */
1258 if ((mh_sequences_old_fp
= g_fopen(mh_sequences_old
, "r+b")) != NULL
) {
1259 while (fgets(buf
, sizeof(buf
), mh_sequences_old_fp
) != NULL
) {
1260 if (strncmp(buf
, get_unseen_seq_name(), strlen(get_unseen_seq_name())))
1261 fprintf(mh_sequences_new_fp
, "%s", buf
);
1263 fclose(mh_sequences_old_fp
);
1266 fclose(mh_sequences_new_fp
);
1267 g_rename(mh_sequences_new
, mh_sequences_old
);
1269 procmsg_msg_list_free(msglist
);
1271 g_free(mh_sequences_old
);
1272 g_free(mh_sequences_new
);
1275 g_timer_stop(timer);
1276 printf("mh_get_flags: %f secs\n", g_timer_elapsed(timer, NULL));
1277 g_timer_destroy(timer);
1281 static int mh_item_close(Folder
*folder
, FolderItem
*item
)
1283 mh_write_sequences(item
);