Make procmsg_msginfo_free() zero out pointers to freed memory.
[claws.git] / src / mh.c
index b7a3b4673a09fc2f32dd95968eb12ac29b851b13..dbf250c37132ffac6275f5def3bba9ecdc8e0abb 100644 (file)
--- a/src/mh.c
+++ b/src/mh.c
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
+#include "claws-features.h"
 #endif
 
 #include "defs.h"
@@ -33,6 +34,7 @@
 #include <time.h>
 
 #include "folder.h"
+#include "folder_item_prefs.h"
 #include "mh.h"
 #include "procmsg.h"
 #include "procheader.h"
@@ -41,6 +43,7 @@
 #include "statusbar.h"
 #include "gtkutils.h"
 #include "timing.h"
+#include "msgcache.h"
 
 /* Define possible missing constants for Windows. */
 #ifdef G_OS_WIN32
@@ -146,6 +149,7 @@ FolderClass *mh_get_class(void)
                mh_class.type = F_MH;
                mh_class.idstr = "mh";
                mh_class.uistr = "MH";
+               mh_class.supports_server_search = FALSE;
                
                /* Folder functions */
                mh_class.new_folder = mh_folder_new;
@@ -173,6 +177,7 @@ FolderClass *mh_get_class(void)
                mh_class.add_msgs = mh_add_msgs;
                mh_class.copy_msg = mh_copy_msg;
                mh_class.copy_msgs = mh_copy_msgs;
+               mh_class.search_msgs = folder_item_search_msgs_local;
                mh_class.remove_msg = mh_remove_msg;
                mh_class.remove_msgs = mh_remove_msgs;
                mh_class.remove_all_msg = mh_remove_all_msg;
@@ -207,7 +212,7 @@ static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
 gboolean mh_scan_required(Folder *folder, FolderItem *item)
 {
        gchar *path;
-       struct stat s;
+       GStatBuf s;
 
        path = folder_item_get_path(item);
        cm_return_val_if_fail(path != NULL, FALSE);
@@ -239,8 +244,9 @@ gboolean mh_scan_required(Folder *folder, FolderItem *item)
 static void mh_get_last_num(Folder *folder, FolderItem *item)
 {
        gchar *path;
-       DIR *dp;
-       struct dirent *d;
+       GDir *dp;
+       const gchar *d;
+       GError *error = NULL;
        gint max = 0;
        gint num;
 
@@ -256,21 +262,23 @@ static void mh_get_last_num(Folder *folder, FolderItem *item)
        }
        g_free(path);
 
-       if ((dp = opendir(".")) == NULL) {
-               FILE_OP_ERROR(item->path, "opendir");
+       if ((dp = g_dir_open(".", 0, &error)) == NULL) {
+               g_message("Couldn't open current directory: %s (%d).\n",
+                               error->message, error->code);
+               g_error_free(error);
                return;
        }
 
-       while ((d = readdir(dp)) != NULL) {
-               if ((num = to_number(d->d_name)) > 0 &&
-                   dirent_is_regular_file(d)) {
+       while ((d = g_dir_read_name(dp)) != NULL) {
+               if ((num = to_number(d)) > 0 &&
+                   g_file_test(d, G_FILE_TEST_IS_REGULAR)) {
                        if (max < num)
                                max = num;
                }
                if (num % 2000 == 0)
                        GTK_EVENTS_FLUSH();
        }
-       closedir(dp);
+       g_dir_close(dp);
 
        debug_print("Last number in dir %s = %d\n", item->path?item->path:"(null)", max);
        item->last_num = max;
@@ -280,8 +288,9 @@ gint mh_get_num_list(Folder *folder, FolderItem *item, GSList **list, gboolean *
 {
 
        gchar *path;
-       DIR *dp;
-       struct dirent *d;
+       GDir *dp;
+       const gchar *d;
+       GError *error = NULL;
        gint num, nummsgs = 0;
 
        cm_return_val_if_fail(item != NULL, -1);
@@ -298,18 +307,20 @@ gint mh_get_num_list(Folder *folder, FolderItem *item, GSList **list, gboolean *
        }
        g_free(path);
 
-       if ((dp = opendir(".")) == NULL) {
-               FILE_OP_ERROR(item->path, "opendir");
+       if ((dp = g_dir_open(".", 0, &error)) == NULL) {
+               g_message("Couldn't open current directory: %s (%d).\n",
+                               error->message, error->code);
+               g_error_free(error);
                return -1;
        }
 
-       while ((d = readdir(dp)) != NULL) {
-               if ((num = to_number(d->d_name)) > 0) {
+       while ((d = g_dir_read_name(dp)) != NULL) {
+               if ((num = to_number(d)) > 0) {
                        *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
                        nummsgs++;
                }
        }
-       closedir(dp);
+       g_dir_close(dp);
 
        mh_set_mtime(folder, item);
        return nummsgs;
@@ -364,6 +375,11 @@ static gchar *mh_get_new_msg_filename(FolderItem *dest)
        destpath = folder_item_get_path(dest);
        cm_return_val_if_fail(destpath != NULL, NULL);
 
+       if (dest->last_num < 0) {
+               mh_get_last_num(dest->folder, dest);
+               if (dest->last_num < 0) return NULL;
+       }
+
        if (!is_dir_exist(destpath))
                make_dir_hier(destpath);
 
@@ -425,7 +441,7 @@ static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
                if (link(fileinfo->file, destfile) < 0) {
 #endif
                        if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
-                               g_warning(_("can't copy message %s to %s\n"),
+                               g_warning("can't copy message %s to %s",
                                          fileinfo->file, destfile);
                                g_free(destfile);
                                return -1;
@@ -463,7 +479,6 @@ static gint mh_copy_msgs(Folder *folder, FolderItem *dest, MsgInfoList *msglist,
        FolderItem *src = NULL;
        gchar *srcfile;
        gchar *destfile;
-       gint filemode = 0;
        FolderItemPrefs *prefs;
        MsgInfo *msginfo = NULL;
        MsgInfoList *cur = NULL;
@@ -481,7 +496,7 @@ static gint mh_copy_msgs(Folder *folder, FolderItem *dest, MsgInfoList *msglist,
        cm_return_val_if_fail(msginfo != NULL, -1);
 
        if (msginfo->folder == dest) {
-               g_warning("the src folder is identical to the dest.\n");
+               g_warning("the src folder is identical to the dest.");
                return -1;
        }
 
@@ -572,15 +587,10 @@ static gint mh_copy_msgs(Folder *folder, FolderItem *dest, MsgInfoList *msglist,
                if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
                        if (chmod(destfile, prefs->folder_chmod) < 0)
                                FILE_OP_ERROR(destfile, "chmod");
-
-                       /* for mark file */
-                       filemode = prefs->folder_chmod;
-                       if (filemode & S_IRGRP) filemode |= S_IWGRP;
-                       if (filemode & S_IROTH) filemode |= S_IWOTH;
                }
                if (relation) {
                        if (g_hash_table_lookup(relation, msginfo) != NULL)
-                               g_warning("already in : %p", msginfo);
+                               g_warning("already in: %p", msginfo);
                        
                        g_hash_table_insert(relation, msginfo, GINT_TO_POINTER(dest->last_num+1));
                }
@@ -723,7 +733,7 @@ static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
 static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
                                  MsgInfo *msginfo)
 {
-       struct stat s;
+       GStatBuf s;
 
        if (g_stat(itos(msginfo->msgnum), &s) < 0 ||
            msginfo->size  != s.st_size || (
@@ -767,7 +777,7 @@ static gint mh_scan_tree(Folder *folder)
 { \
        if (!is_dir_exist(dir)) { \
                if (is_file_exist(dir)) { \
-                       g_warning("File `%s' already exists.\n" \
+                       g_warning("File '%s' already exists. " \
                                    "Can't create folder.", dir); \
                        return -1; \
                } \
@@ -778,7 +788,7 @@ static gint mh_scan_tree(Folder *folder)
 
 static gint mh_create_tree(Folder *folder)
 {
-       gchar *rootpath;
+       gchar *rootpath, *f;
 
        cm_return_val_if_fail(folder != NULL, -1);
 
@@ -786,11 +796,42 @@ static gint mh_create_tree(Folder *folder)
        rootpath = LOCAL_FOLDER(folder)->rootpath;
        MAKE_DIR_IF_NOT_EXIST(rootpath);
        CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
-       MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
-       MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
-       MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
-       MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
-       MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
+
+       /* Create special directories as needed */
+       if (folder->inbox != NULL &&
+                       folder->inbox->path != NULL)
+               f = folder->inbox->path;
+       else
+               f = INBOX_DIR;
+       MAKE_DIR_IF_NOT_EXIST(f);
+
+       if (folder->outbox != NULL &&
+                       folder->outbox->path != NULL)
+               f = folder->outbox->path;
+       else
+               f = OUTBOX_DIR;
+       MAKE_DIR_IF_NOT_EXIST(f);
+
+       if (folder->draft != NULL &&
+                       folder->draft->path != NULL)
+               f = folder->draft->path;
+       else
+               f = DRAFT_DIR;
+       MAKE_DIR_IF_NOT_EXIST(f);
+
+       if (folder->queue != NULL &&
+                       folder->queue->path != NULL)
+               f = folder->queue->path;
+       else
+               f = QUEUE_DIR;
+       MAKE_DIR_IF_NOT_EXIST(f);
+
+       if (folder->trash != NULL &&
+                       folder->trash->path != NULL)
+               f = folder->trash->path;
+       else
+               f = TRASH_DIR;
+       MAKE_DIR_IF_NOT_EXIST(f);
 
        return 0;
 }
@@ -830,14 +871,38 @@ static gchar *mh_item_get_path(Folder *folder, FolderItem *item)
        if (!is_dir_exist(real_path) && is_dir_exist(path)) {
                /* mmh, older version did put utf8 filenames instead of
                 * the correct encoding */
-               g_rename(path, real_path);
-               folder_item_scan(item);
+               if (g_rename(path, real_path) == 0)
+                       folder_item_scan(item);
        }
 
        g_free(path);
        return real_path;
 }
 
+static gboolean mh_renumber_msg(MsgInfo *info)
+{
+       gchar *src, *dest;
+       gboolean result = FALSE;
+       guint num;
+       cm_return_val_if_fail(info != NULL, FALSE);
+
+       src = folder_item_fetch_msg(info->folder, info->msgnum);
+       dest = mh_get_new_msg_filename(info->folder);
+       num = info->folder->last_num + 1;
+
+       if (move_file(src, dest, FALSE) == 0) {
+               msgcache_remove_msg(info->folder->cache, info->msgnum);
+               info->msgnum = num;
+               msgcache_add_msg(info->folder->cache, info);
+               result = TRUE;
+       }
+
+       g_free(src);
+       g_free(dest);
+
+       return result;
+}
+
 static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
                                    const gchar *name)
 {
@@ -855,12 +920,24 @@ static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
        if (!is_dir_exist(path)) 
                if (make_dir_hier(path) != 0)
                        return NULL;
-               
+
        real_name = mh_filename_from_utf8(name);
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, real_name, NULL);
        g_free(real_name);
        g_free(path);
 
+       if (to_number(name) > 0) {
+               MsgInfo *info = folder_item_get_msginfo(parent, to_number(name));
+               if (info != NULL) {
+                       gboolean ok = mh_renumber_msg(info);
+                       procmsg_msginfo_free(&info);
+                       if (!ok) {
+                               g_free(fullpath);
+                               return NULL;
+                       }
+               }
+       }
+
        if (make_dir(fullpath) < 0) {
                g_free(fullpath);
                return NULL;
@@ -947,16 +1024,17 @@ static gint mh_rename_folder(Folder *folder, FolderItem *item,
 static gint mh_remove_folder(Folder *folder, FolderItem *item)
 {
        gchar *path;
+       gint ret;
 
        cm_return_val_if_fail(folder != NULL, -1);
        cm_return_val_if_fail(item != NULL, -1);
        cm_return_val_if_fail(item->path != NULL, -1);
 
        path = folder_item_get_path(item);
-       if (remove_dir_recursive(path) < 0) {
-               g_warning("can't remove directory `%s'\n", path);
+       if ((ret = remove_dir_recursive(path)) < 0) {
+               g_warning("can't remove directory '%s'", path);
                g_free(path);
-               return -1;
+               return ret;
        }
 
        g_free(path);
@@ -1025,16 +1103,11 @@ static void mh_remove_missing_folder_items(Folder *folder)
 static void mh_scan_tree_recursive(FolderItem *item)
 {
        Folder *folder;
-#ifdef G_OS_WIN32
        GDir *dir;
-#else
-       DIR *dp;
-       struct dirent *d;
-#endif
        const gchar *dir_name;
-       struct stat s;
        gchar *real_path, *entry, *utf8entry, *utf8name;
        gint n_msg = 0;
+       GError *error = NULL;
 
        cm_return_if_fail(item != NULL);
        cm_return_if_fail(item->folder != NULL);
@@ -1042,20 +1115,14 @@ static void mh_scan_tree_recursive(FolderItem *item)
        folder = item->folder;
 
        real_path = item->path ? mh_filename_from_utf8(item->path) : g_strdup(".");
-#ifdef G_OS_WIN32
-       dir = g_dir_open(real_path, 0, NULL);
+       dir = g_dir_open(real_path, 0, &error);
        if (!dir) {
-               g_warning("failed to open directory: %s\n", real_path);
+               g_warning("failed to open directory '%s': %s (%d)",
+                               real_path, error->message, error->code);
+               g_error_free(error);
                g_free(real_path);
                return;
        }
-#else
-       dp = opendir(real_path);
-       if (!dp) {
-               FILE_OP_ERROR(real_path, "opendir");
-               return;
-       }
-#endif
        g_free(real_path);
 
        debug_print("scanning %s ...\n",
@@ -1064,12 +1131,7 @@ static void mh_scan_tree_recursive(FolderItem *item)
        if (folder->ui_func)
                folder->ui_func(folder, item, folder->ui_func_data);
 
-#ifdef G_OS_WIN32
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
-#else
-       while ((d = readdir(dp)) != NULL) {
-               dir_name = d->d_name;
-#endif
                if (dir_name[0] == '.') continue;
 
                utf8name = mh_filename_to_utf8(dir_name);
@@ -1080,16 +1142,7 @@ static void mh_scan_tree_recursive(FolderItem *item)
                        utf8entry = g_strdup(utf8name);
                entry = mh_filename_from_utf8(utf8entry);
 
-               if (
-#if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
-                       d->d_type == DT_DIR ||
-                       (d->d_type == DT_UNKNOWN &&
-#endif
-                       g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
-#if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
-                       )
-#endif
-                  ) {
+               if (g_file_test(entry, G_FILE_TEST_IS_DIR)) {
                        FolderItem *new_item = NULL;
                        GNode *node;
 
@@ -1142,11 +1195,7 @@ static void mh_scan_tree_recursive(FolderItem *item)
                g_free(utf8name);
        }
 
-#ifdef G_OS_WIN32
        g_dir_close(dir);
-#else
-       closedir(dp);
-#endif
 
        mh_set_mtime(folder, item);
 }
@@ -1163,7 +1212,7 @@ static gboolean mh_rename_folder_func(GNode *node, gpointer data)
 
        oldpathlen = strlen(oldpath);
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
-               g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
+               g_warning("path doesn't match: %s, %s", oldpath, item->path);
                return TRUE;
        }
 
@@ -1185,7 +1234,7 @@ static gchar *mh_filename_from_utf8(const gchar *path)
        gchar *real_path = g_filename_from_utf8(path, -1, NULL, NULL, NULL);
 
        if (!real_path) {
-               g_warning("mh_filename_from_utf8: failed to convert character set\n");
+               g_warning("mh_filename_from_utf8: failed to convert character set");
                real_path = g_strdup(path);
        }
 
@@ -1196,7 +1245,7 @@ static gchar *mh_filename_to_utf8(const gchar *path)
 {
        gchar *utf8path = g_filename_to_utf8(path, -1, NULL, NULL, NULL);
        if (!utf8path) {
-               g_warning("mh_filename_to_utf8: failed to convert character set\n");
+               g_warning("mh_filename_to_utf8: failed to convert character set");
                utf8path = g_strdup(path);
        }
 
@@ -1325,8 +1374,10 @@ static void mh_write_sequences(FolderItem *item, gboolean remove_unseen)
                if (fclose(mh_sequences_new_fp) == EOF)
                        err = TRUE;
 
-               if (!err)
-                       g_rename(mh_sequences_new, mh_sequences_old);
+               if (!err) {
+                       if (g_rename(mh_sequences_new, mh_sequences_old) < 0)
+                               FILE_OP_ERROR(mh_sequences_new, "rename");
+               }
                g_free(sequence);
                procmsg_msg_list_free(msglist);
        }
@@ -1354,7 +1405,7 @@ static int mh_item_close(Folder *folder, FolderItem *item)
 
 static void mh_set_mtime(Folder *folder, FolderItem *item)
 {
-       struct stat s;
+       GStatBuf s;
        gchar *path = folder_item_get_path(item);
 
        cm_return_if_fail(path != NULL);