2007-08-10 [wwp] 2.10.0cvs112
[claws.git] / src / msgcache.c
index 24556d03d561e012cd23f367f0d27bb72d3a1f07..5313f6d6ed87b4a2d28cf7cb87e01365e8ad6681 100644 (file)
@@ -4,7 +4,7 @@
  *
  * 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
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -13,8 +13,8 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <sys/mman.h>
+#ifdef _WIN32
+# include <w32lib.h>
+# define MAP_FAILED    ((char *) -1)
+#else
+# include <sys/mman.h>
+#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -39,6 +44,7 @@
 #include "procmsg.h"
 #include "codeconv.h"
 #include "timing.h"
+#include "tags.h"
 
 #ifdef HAVE_FWRITE_UNLOCKED
 #define SC_FWRITE fwrite_unlocked
         ((x[2]&0xff) << 16) |          \
         ((x[3]&0xff) << 24))
 
+#ifdef G_OS_WIN32
+static gboolean msgcache_use_mmap_read = FALSE;
+#else
 static gboolean msgcache_use_mmap_read = TRUE;
+#endif
 static gboolean msgcache_use_mmap_write = FALSE;
 
 #else
@@ -81,7 +91,11 @@ static gboolean msgcache_use_mmap_write = FALSE;
         ((x[2]&0xff) << 16) |          \
         ((x[3]&0xff) << 24))
 
+#ifdef G_OS_WIN32
+static gboolean msgcache_use_mmap_read = FALSE;
+#else
 static gboolean msgcache_use_mmap_read = TRUE;
+#endif
 static gboolean msgcache_use_mmap_write = FALSE;
 #endif
 
@@ -389,7 +403,7 @@ static FILE *msgcache_open_data_file(const gchar *file, guint version,
 
        if (mode == DATA_WRITE) {
                int w_err = 0, wrote = 0;
-               if ((fp = g_fopen(file, "w+")) == NULL) {
+               if ((fp = g_fopen(file, "wb")) == NULL) {
                        FILE_OP_ERROR(file, "fopen");
                        return NULL;
                }
@@ -529,19 +543,14 @@ static gint msgcache_get_cache_data_str(gchar *src, gchar **str, gint len,
        return len;
 }
 
-gchar *strconv_strdup_convert(StringConverter *conv, gchar *srcstr)
-{
-       return g_strdup(srcstr);
-}
-
-gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr)
+static gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr)
 {
        CharsetConverter *charsetconv = (CharsetConverter *) conv;
 
        return conv_codeset_strdup(srcstr, charsetconv->srccharset, charsetconv->dstcharset);
 }
 
-void strconv_charset_free(StringConverter *conv)
+static void strconv_charset_free(StringConverter *conv)
 {
        CharsetConverter *charsetconv = (CharsetConverter *) conv;
 
@@ -595,8 +604,10 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
                tmp_flags |= MSG_DRAFT;
        }
 
-       if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0)
+       if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0) {
+               fclose(fp);
                return NULL;
+       }
        dstcharset = CS_UTF_8;
        if (srccharset == NULL || dstcharset == NULL) {
                conv = NULL;
@@ -626,8 +637,23 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
                        map_len = st.st_size;
                else
                        map_len = -1;
-               if (map_len > 0)
+               if (map_len > 0) {
+#ifdef G_OS_WIN32
+                       cache_data = NULL;
+                       HANDLE hFile, hMapping;
+                       hFile = (HANDLE) _get_osfhandle (fileno(fp));
+                       if (hFile == (HANDLE) -1)
+                               goto w32_fail;
+                       hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
+                       if (!hMapping)
+                               goto w32_fail;
+                       cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+               w32_fail:
+                       ;
+#else
                        cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
+#endif
+               }
        } else {
                cache_data = NULL;
        }
@@ -683,8 +709,12 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
                        if(msginfo->msgid)
                                g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
                }
-               
+
+#ifdef G_OS_WIN32
+               UnmapViewOfFile((void*) cache_data);
+#else
                munmap(cache_data, map_len);
+#endif
        } else {
                while (fread(&num, sizeof(num), 1, fp) == 1) {
                        if (swapping)
@@ -789,8 +819,23 @@ void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
                        map_len = st.st_size;
                else
                        map_len = -1;
-               if (map_len > 0)
+               if (map_len > 0) {
+#ifdef G_OS_WIN32
+                       cache_data = NULL;
+                       HANDLE hFile, hMapping;
+                       hFile = (HANDLE) _get_osfhandle (fileno(fp));
+                       if (hFile == (HANDLE) -1)
+                               goto w32_fail2;
+                       hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
+                       if (!hMapping)
+                               goto w32_fail2;
+                       cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+               w32_fail2:
+                       ;
+#else
                        cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
+#endif
+               }
        } else {
                cache_data = NULL;
        }
@@ -806,7 +851,11 @@ void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
                                msginfo->flags.perm_flags = perm_flags;
                        }
                }
+#ifdef G_OS_WIN32
+               UnmapViewOfFile((void*) cache_data);
+#else
                munmap(cache_data, map_len);
+#endif
        } else {
                while (fread(&num, sizeof(num), 1, fp) == 1) {
                        if (swapping)
@@ -823,6 +872,110 @@ void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
        fclose(fp);
 }
 
+void msgcache_read_tags(MsgCache *cache, const gchar *tags_file)
+{
+       FILE *fp;
+       MsgInfo *msginfo;
+       guint32 num;
+       gint map_len = -1;
+       char *cache_data = NULL;
+       struct stat st;
+       
+       swapping = TRUE;
+
+       /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
+        * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
+        * it means it's the old version (not little-endian) on a big-endian machine. The code has
+        * no effect on x86 as their file doesn't change. */
+
+       if ((fp = msgcache_open_data_file(tags_file, TAGS_VERSION, DATA_READ, NULL, 0)) == NULL) {
+               /* see if it isn't swapped ? */
+               if ((fp = msgcache_open_data_file(tags_file, bswap_32(TAGS_VERSION), DATA_READ, NULL, 0)) == NULL)
+                       return;
+               else
+                       swapping = FALSE; /* yay */
+       }
+       debug_print("reading %sswapped tags file.\n", swapping?"":"un");
+       
+       if (msgcache_use_mmap_read) {
+               if (fstat(fileno(fp), &st) >= 0)
+                       map_len = st.st_size;
+               else
+                       map_len = -1;
+               if (map_len > 0) {
+#ifdef G_OS_WIN32
+                       cache_data = NULL;
+                       HANDLE hFile, hMapping;
+                       hFile = (HANDLE) _get_osfhandle (fileno(fp));
+                       if (hFile == (HANDLE) -1)
+                               goto w32_fail6;
+                       hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
+                       if (!hMapping)
+                               goto w32_fail6;
+                       cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+               w32_fail6:
+                       ;
+#else
+                       cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
+#endif
+               }
+       } else {
+               cache_data = NULL;
+       }
+       if (cache_data != NULL && cache_data != MAP_FAILED) {
+               int rem_len = map_len-ftell(fp);
+               char *walk_data = cache_data+ftell(fp);
+
+               while(rem_len > 0) {
+                       gint id = -1;
+                       GET_CACHE_DATA_INT(num);
+                       msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
+                       if(msginfo) {
+                               g_slist_free(msginfo->tags);
+                               msginfo->tags = NULL;
+                               do {
+                                       GET_CACHE_DATA_INT(id);
+                                       if (id > 0) {
+                                               msginfo->tags = g_slist_prepend(
+                                                       msginfo->tags, 
+                                                       GINT_TO_POINTER(id));
+                                       }
+                               } while (id > 0);
+                               msginfo->tags = g_slist_reverse(msginfo->tags);
+                       }
+               }
+#ifdef G_OS_WIN32
+               UnmapViewOfFile((void*) cache_data);
+#else
+               munmap(cache_data, map_len);
+#endif
+       } else {
+               while (fread(&num, sizeof(num), 1, fp) == 1) {
+                       gint id = -1;
+                       if (swapping)
+                               num = bswap_32(num);
+                       msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
+                       if(msginfo) {
+                               g_slist_free(msginfo->tags);
+                               msginfo->tags = NULL;
+                               do {
+                                       if (fread(&id, sizeof(id), 1, fp) != 1) 
+                                               id = -1;
+                                       if (swapping)
+                                               id = bswap_32(id);
+                                       if (id > 0) {
+                                               msginfo->tags = g_slist_prepend(
+                                                       msginfo->tags, 
+                                                       GINT_TO_POINTER(id));
+                                       }
+                               } while (id > 0);
+                               msginfo->tags = g_slist_reverse(msginfo->tags);
+                       }
+               }
+       }
+       fclose(fp);
+}
+
 static int msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
 {
        MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
@@ -899,6 +1052,23 @@ static int msgcache_write_flags(MsgInfo *msginfo, FILE *fp)
        return w_err ? -1 : wrote;
 }
 
+static int msgcache_write_tags(MsgInfo *msginfo, FILE *fp)
+{
+       GSList *cur = msginfo->tags;
+       int w_err = 0, wrote = 0;
+
+       WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
+       for (; cur; cur = cur->next) {
+               gint id = GPOINTER_TO_INT(cur->data);
+               if (tags_get_tag(id) != NULL) {
+                       WRITE_CACHE_DATA_INT(id, fp);
+               }
+       }
+       WRITE_CACHE_DATA_INT(-1, fp);
+
+       return w_err ? -1 : wrote;
+}
+
 static int msgcache_write_mmap_flags(MsgInfo *msginfo, char *walk_data)
 {
        MsgPermFlags flags = msginfo->flags.perm_flags;
@@ -909,15 +1079,34 @@ static int msgcache_write_mmap_flags(MsgInfo *msginfo, char *walk_data)
        return wrote;
 }
 
+static int msgcache_write_mmap_tags(MsgInfo *msginfo, char *walk_data)
+{
+       GSList *cur = msginfo->tags;
+       int wrote = 0;
+       
+       PUT_CACHE_DATA_INT(msginfo->msgnum);
+       for (; cur; cur = cur->next) {
+               gint id = GPOINTER_TO_INT(cur->data);
+               if (tags_get_tag(id) != NULL) {
+                       PUT_CACHE_DATA_INT(id);
+               }
+       }
+       PUT_CACHE_DATA_INT(-1);
+       return wrote;
+}
+
 struct write_fps
 {
        FILE *cache_fp;
        FILE *mark_fp;
+       FILE *tags_fp;
        char *cache_data;
        char *mark_data;
+       char *tags_data;
        int error;
        guint cache_size;
        guint mark_size;
+       guint tags_size;
 };
 
 static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data)
@@ -939,6 +1128,11 @@ static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data
                write_fps->error = 1;
        else
                write_fps->mark_size += tmp;
+       tmp = msgcache_write_tags(msginfo, write_fps->tags_fp);
+       if (tmp < 0)
+               write_fps->error = 1;
+       else
+               write_fps->tags_size += tmp;
 }
 
 static void msgcache_write_mmap_func(gpointer key, gpointer value, gpointer user_data)
@@ -956,32 +1150,42 @@ static void msgcache_write_mmap_func(gpointer key, gpointer value, gpointer user
        tmp = msgcache_write_mmap_flags(msginfo, write_fps->mark_data);
        write_fps->mark_size += tmp;
        write_fps->mark_data += tmp;
+       tmp = msgcache_write_mmap_tags(msginfo, write_fps->tags_data);
+       write_fps->tags_size += tmp;
+       write_fps->tags_data += tmp;
 }
 
-gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *cache)
+gint msgcache_write(const gchar *cache_file, const gchar *mark_file, const gchar *tags_file, MsgCache *cache)
 {
        struct write_fps write_fps;
-       gchar *new_cache, *new_mark;
+       gchar *new_cache, *new_mark, *new_tags;
        int w_err = 0, wrote = 0;
        gint map_len = -1;
        char *cache_data = NULL;
        char *mark_data = NULL;
+       char *tags_data = NULL;
+
        START_TIMING("");
        g_return_val_if_fail(cache_file != NULL, -1);
        g_return_val_if_fail(mark_file != NULL, -1);
+       g_return_val_if_fail(tags_file != NULL, -1);
        g_return_val_if_fail(cache != NULL, -1);
 
        new_cache = g_strconcat(cache_file, ".new", NULL);
        new_mark  = g_strconcat(mark_file, ".new", NULL);
+       new_tags  = g_strconcat(tags_file, ".new", NULL);
 
        write_fps.error = 0;
        write_fps.cache_size = 0;
        write_fps.mark_size = 0;
+       write_fps.tags_size = 0;
+
        write_fps.cache_fp = msgcache_open_data_file(new_cache, CACHE_VERSION,
                DATA_WRITE, NULL, 0);
        if (write_fps.cache_fp == NULL) {
                g_free(new_cache);
                g_free(new_mark);
+               g_free(new_tags);
                return -1;
        }
 
@@ -993,6 +1197,7 @@ gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *c
                g_unlink(new_cache);
                g_free(new_cache);
                g_free(new_mark);
+               g_free(new_tags);
                return -1;
        }
 
@@ -1003,6 +1208,20 @@ gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *c
                g_unlink(new_cache);
                g_free(new_cache);
                g_free(new_mark);
+               g_free(new_tags);
+               return -1;
+       }
+
+       write_fps.tags_fp = msgcache_open_data_file(new_tags, TAGS_VERSION,
+               DATA_WRITE, NULL, 0);
+       if (write_fps.tags_fp == NULL) {
+               fclose(write_fps.cache_fp);
+               fclose(write_fps.mark_fp);
+               g_unlink(new_cache);
+               g_unlink(new_mark);
+               g_free(new_cache);
+               g_free(new_mark);
+               g_free(new_tags);
                return -1;
        }
 
@@ -1013,20 +1232,85 @@ gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *c
 
        write_fps.cache_size = ftell(write_fps.cache_fp);
        write_fps.mark_size = ftell(write_fps.mark_fp);
+       write_fps.tags_size = ftell(write_fps.tags_fp);
+
        if (msgcache_use_mmap_write && cache->memusage > 0) {
                map_len = cache->memusage;
                if (ftruncate(fileno(write_fps.cache_fp), (off_t)map_len) == 0) {
+
+#ifdef G_OS_WIN32
+                       cache_data = NULL;
+                       HANDLE hFile, hMapping;
+                       hFile = (HANDLE) _get_osfhandle (fileno(write_fps.cache_fp));
+                       if (hFile == (HANDLE) -1)
+                               goto w32_fail3;
+                       hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
+                       if (!hMapping)
+                               goto w32_fail3;
+                       cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+               w32_fail3:
+                       ;
+#else
                        cache_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
                                fileno(write_fps.cache_fp), 0);
+#endif
                }
                if (cache_data != NULL && cache_data != MAP_FAILED) {
                        if (ftruncate(fileno(write_fps.mark_fp), (off_t)map_len) == 0) {
+#ifdef G_OS_WIN32
+                               mark_data = NULL;
+                               HANDLE hFile, hMapping;
+                               hFile = (HANDLE) _get_osfhandle (fileno(write_fps.mark_fp));
+                               if (hFile == (HANDLE) -1)
+                                       goto w32_fail4;
+                               hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
+                               if (!hMapping)
+                                       goto w32_fail4;
+                               mark_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+                       w32_fail4:
+                               ;
+#else
                                mark_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
                                        fileno(write_fps.mark_fp), 0);
+#endif
                        } 
                        if (mark_data == NULL || mark_data == MAP_FAILED) {
+#ifdef G_OS_WIN32
+                               UnmapViewOfFile((void*) cache_data);
+#else
                                munmap(cache_data, map_len);
+#endif
                                cache_data = NULL;
+                       } else {
+                               if (ftruncate(fileno(write_fps.tags_fp), (off_t)map_len) == 0) {
+#ifdef G_OS_WIN32
+                                               tags_data = NULL;
+                                               HANDLE hFile, hMapping;
+                                               hFile = (HANDLE) _get_osfhandle (fileno(write_fps.tags_fp));
+                                               if (hFile == (HANDLE) -1)
+                                                       goto w32_fail5;
+                                               hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
+                                               if (!hMapping)
+                                                       goto w32_fail5;
+                                               tags_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
+                                       w32_fail5:
+                                               ;
+#else
+                                       tags_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
+                                               fileno(write_fps.tags_fp), 0);
+#endif
+                               } 
+                               if (tags_data == NULL || tags_data == MAP_FAILED) {
+#ifdef G_OS_WIN32
+                                       UnmapViewOfFile((void*) cache_data);
+                                       UnmapViewOfFile((void*) mark_data);
+#else
+                                       munmap(cache_data, map_len);
+                                       munmap(mark_data, map_len);
+#endif
+                                       cache_data = NULL;
+                                       mark_data = NULL;
+                               } 
                        }
                }
        }
@@ -1034,49 +1318,67 @@ gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *c
        if (cache_data != NULL && cache_data != MAP_FAILED) {
                write_fps.cache_data = cache_data + ftell(write_fps.cache_fp);
                write_fps.mark_data = mark_data + ftell(write_fps.mark_fp);
+               write_fps.tags_data = mark_data + ftell(write_fps.tags_fp);
                g_hash_table_foreach(cache->msgnum_table, msgcache_write_mmap_func, (gpointer)&write_fps);
+#ifdef G_OS_WIN32
+               UnmapViewOfFile((void*) cache_data);
+               UnmapViewOfFile((void*) mark_data);
+               UnmapViewOfFile((void*) tags_data);
+#else
                munmap(cache_data, map_len);
                munmap(mark_data, map_len);
+               munmap(tags_data, map_len);
+#endif
                ftruncate(fileno(write_fps.cache_fp), write_fps.cache_size);
                ftruncate(fileno(write_fps.mark_fp), write_fps.mark_size);
+               ftruncate(fileno(write_fps.tags_fp), write_fps.tags_size);
        } else {
 #ifdef HAVE_FWRITE_UNLOCKED
                flockfile(write_fps.cache_fp);
                flockfile(write_fps.mark_fp);
+               flockfile(write_fps.tags_fp);
 #endif
                g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps);
 #ifdef HAVE_FWRITE_UNLOCKED
                funlockfile(write_fps.mark_fp);
                funlockfile(write_fps.cache_fp);
+               funlockfile(write_fps.tags_fp);
 #endif
        }
        
        fflush(write_fps.cache_fp);
        fflush(write_fps.mark_fp);
+       fflush(write_fps.tags_fp);
 
 #if 0
        fsync(fileno(write_fps.cache_fp));
        fsync(fileno(write_fps.mark_fp));
+       fsync(fileno(write_fps.tags_fp));
 #endif
 
        fclose(write_fps.cache_fp);
        fclose(write_fps.mark_fp);
+       fclose(write_fps.tags_fp);
 
 
        if (write_fps.error != 0) {
                g_unlink(new_cache);
                g_unlink(new_mark);
+               g_unlink(new_tags);
                g_free(new_cache);
                g_free(new_mark);
+               g_free(new_tags);
                return -1;
        } else {
                move_file(new_cache, cache_file, TRUE);
                move_file(new_mark, mark_file, TRUE);
+               move_file(new_tags, tags_file, TRUE);
                cache->last_access = time(NULL);
        }
 
        g_free(new_cache);
        g_free(new_mark);
+       g_free(new_tags);
        debug_print("done.\n");
        END_TIMING();
        return 0;