2005-10-10 [colin] 1.9.15cvs28
[claws.git] / src / msgcache.c
index 6c51394352546aa1443578f877054af0ee84a384..8e8273effb8137492840aec32c9f9412e00ac2f2 100644 (file)
@@ -14,7 +14,7 @@
  *
  * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #include "defs.h"
 #include "procmsg.h"
 #include "codeconv.h"
 
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define bswap_32(x) \
+     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
+      (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
+#else
+#define bswap_32(x) (x)
+#endif
+
+static gboolean swapping = TRUE;
+
 typedef enum
 {
        DATA_READ,
@@ -225,13 +235,14 @@ gint msgcache_get_memory_usage(MsgCache *cache)
  *  Cache saving functions
  */
 
-#define READ_CACHE_DATA(data, fp) \
+#define READ_CACHE_DATA(data, fp, total_len) \
 { \
-       if (msgcache_read_cache_data_str(fp, &data, conv) < 0) { \
+       if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
                procmsg_msginfo_free(msginfo); \
                error = TRUE; \
                break; \
        } \
+       total_len += tmp_len; \
 }
 
 #define READ_CACHE_DATA_INT(n, fp) \
@@ -239,21 +250,21 @@ gint msgcache_get_memory_usage(MsgCache *cache)
        guint32 idata; \
        size_t ni; \
  \
-       if ((ni = fread(&idata, 1, sizeof(idata), fp)) != sizeof(idata)) { \
+       if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
                g_warning("read_int: Cache data corrupted, read %d of %d at " \
-                         "offset %d\n", ni, sizeof(idata), ftell(fp)); \
+                         "offset %ld\n", ni, sizeof(idata), ftell(fp)); \
                procmsg_msginfo_free(msginfo); \
                error = TRUE; \
                break; \
        } else \
-               n = idata;\
+               n = swapping ? bswap_32(idata) : (idata);\
 }
 
 #define WRITE_CACHE_DATA_INT(n, fp)            \
 {                                              \
        guint32 idata;                          \
                                                \
-       idata = (guint32)n;                     \
+       idata = (guint32)bswap_32(n);                   \
        fwrite(&idata, sizeof(idata), 1, fp);   \
 }
 
@@ -270,7 +281,7 @@ gint msgcache_get_memory_usage(MsgCache *cache)
        } \
 }
 
-static FILE *msgcache_open_data_file(const gchar *file, gint version,
+static FILE *msgcache_open_data_file(const gchar *file, guint version,
                                     DataOpenMode mode,
                                     gchar *buf, size_t buf_size)
 {
@@ -280,7 +291,7 @@ static FILE *msgcache_open_data_file(const gchar *file, gint version,
        g_return_val_if_fail(file != NULL, NULL);
 
        if (mode == DATA_WRITE) {
-               if ((fp = fopen(file, "wb")) == NULL) {
+               if ((fp = g_fopen(file, "wb")) == NULL) {
                        FILE_OP_ERROR(file, "fopen");
                        return NULL;
                }
@@ -292,27 +303,28 @@ static FILE *msgcache_open_data_file(const gchar *file, gint version,
        }
 
        /* check version */
-       if ((fp = fopen(file, "rb")) == NULL)
-               debug_print("Mark/Cache file not found\n");
+       if ((fp = g_fopen(file, "rb")) == NULL)
+               debug_print("Mark/Cache file '%s' not found\n", file);
        else {
                if (buf && buf_size > 0)
                        setvbuf(fp, buf, _IOFBF, buf_size);
                if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1 ||
-                        version != data_ver) {
-                       debug_print("Mark/Cache version is different (%d != %d). "
-                                   "Discarding it.\n", data_ver, version);
+                        version != bswap_32(data_ver)) {
+                       g_message("%s: Mark/Cache version is different (%u != %u).\n",
+                                 file, bswap_32(data_ver), version);
                        fclose(fp);
                        fp = NULL;
                }
+               data_ver = bswap_32(data_ver);
        }
-
+       
        if (mode == DATA_READ)
                return fp;
 
        if (fp) {
                /* reopen with append mode */
                fclose(fp);
-               if ((fp = fopen(file, "ab")) == NULL)
+               if ((fp = g_fopen(file, "ab")) == NULL)
                        FILE_OP_ERROR(file, "fopen");
        } else {
                /* open with overwrite mode if mark file doesn't exist or
@@ -332,12 +344,23 @@ static gint msgcache_read_cache_data_str(FILE *fp, gchar **str,
        guint32 len;
 
        *str = NULL;
-       if ((ni = fread(&len, 1, sizeof(len), fp) != sizeof(len)) ||
-           len > G_MAXINT) {
-               g_warning("read_data_str: Cache data (len) corrupted, read %d "
-                         "of %d bytes at offset %d\n", ni, sizeof(len), 
-                         ftell(fp));
-               return -1;
+       if (!swapping) {
+               if ((ni = fread(&len, sizeof(len), 1, fp) != 1) ||
+                   len > G_MAXINT) {
+                       g_warning("read_data_str: Cache data (len) corrupted, read %d "
+                                 "of %d bytes at offset %ld\n", ni, sizeof(len), 
+                                 ftell(fp));
+                       return -1;
+               }
+       } else {
+               if ((ni = fread(&len, sizeof(len), 1, fp) != 1) ||
+                   bswap_32(len) > G_MAXINT) {
+                       g_warning("read_data_str: Cache data (len) corrupted, read %d "
+                                 "of %d bytes at offset %ld\n", ni, sizeof(len), 
+                                 ftell(fp));
+                       return -1;
+               }
+               len = bswap_32(len);
        }
 
        if (len == 0)
@@ -347,7 +370,7 @@ static gint msgcache_read_cache_data_str(FILE *fp, gchar **str,
 
        if ((ni = fread(tmpstr, 1, len, fp)) != len) {
                g_warning("read_data_str: Cache data corrupted, read %d of %d "
-                         "bytes at offset %d\n", 
+                         "bytes at offset %ld\n", 
                          ni, len, ftell(fp));
                g_free(tmpstr);
                return -1;
@@ -360,7 +383,7 @@ static gint msgcache_read_cache_data_str(FILE *fp, gchar **str,
        } else 
                *str = tmpstr;
 
-       return 0;
+       return len;
 }
 
 gchar *strconv_strdup_convert(StringConverter *conv, gchar *srcstr)
@@ -391,23 +414,44 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
        MsgTmpFlags tmp_flags = 0;
        gchar file_buf[BUFFSIZE];
        guint32 num;
+        guint refnum;
        gboolean error = FALSE;
        StringConverter *conv = NULL;
        gchar *srccharset = NULL;
        const gchar *dstcharset = NULL;
-
+       gchar *ref = NULL;
+       guint memusage = 0;
+       guint tmp_len = 0;
+#if 0
+       struct timeval start;
+       struct timeval end;
+       struct timeval diff;
+       gettimeofday(&start, NULL);
+#endif
        g_return_val_if_fail(cache_file != NULL, NULL);
        g_return_val_if_fail(item != NULL, NULL);
 
+       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
-               (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL)
-               return NULL;
+               (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
+               if ((fp = msgcache_open_data_file
+               (cache_file, bswap_32(CACHE_VERSION), DATA_READ, file_buf, sizeof(file_buf))) == NULL)
+                       return NULL;
+               else
+                       swapping = FALSE;
+       }
 
-       debug_print("\tReading message cache from %s...\n", cache_file);
+       debug_print("\tReading %sswapped message cache from %s...\n", swapping?"":"un", cache_file);
 
-       if (item->stype == F_QUEUE) {
+       if (folder_has_parent_of_type(item, F_QUEUE)) {
                tmp_flags |= MSG_QUEUED;
-       } else if (item->stype == F_DRAFT) {
+       } else if (folder_has_parent_of_type(item, F_DRAFT)) {
                tmp_flags |= MSG_DRAFT;
        }
 
@@ -417,15 +461,9 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
        if (srccharset == NULL || dstcharset == NULL) {
                conv = NULL;
        } else if (strcmp(srccharset, dstcharset) == 0) {
-               StrdupConverter *strdupconv;
-
-               debug_print("using StrdupConverter\n");
-
-               strdupconv = g_new0(StrdupConverter, 1);
-               strdupconv->converter.convert = strconv_strdup_convert;
-               strdupconv->converter.free = NULL;
+               debug_print("using Noop Converter\n");
 
-               conv = (StringConverter *) strdupconv;
+               conv = NULL;
        } else {
                CharsetConverter *charsetconv;
 
@@ -442,30 +480,48 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
        g_free(srccharset);
 
        cache = msgcache_new();
-       g_hash_table_freeze(cache->msgnum_table);
 
        while (fread(&num, sizeof(num), 1, fp) == 1) {
+               if (swapping)
+                       num = bswap_32(num);
+
                msginfo = procmsg_msginfo_new();
                msginfo->msgnum = num;
+               memusage += sizeof(MsgInfo);
+               
                READ_CACHE_DATA_INT(msginfo->size, fp);
                READ_CACHE_DATA_INT(msginfo->mtime, fp);
                READ_CACHE_DATA_INT(msginfo->date_t, fp);
                READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
-
-               READ_CACHE_DATA(msginfo->fromname, fp);
-
-               READ_CACHE_DATA(msginfo->date, fp);
-               READ_CACHE_DATA(msginfo->from, fp);
-               READ_CACHE_DATA(msginfo->to, fp);
-               READ_CACHE_DATA(msginfo->cc, fp);
-               READ_CACHE_DATA(msginfo->newsgroups, fp);
-               READ_CACHE_DATA(msginfo->subject, fp);
-               READ_CACHE_DATA(msginfo->msgid, fp);
-               READ_CACHE_DATA(msginfo->inreplyto, fp);
-               READ_CACHE_DATA(msginfo->references, fp);
-               READ_CACHE_DATA(msginfo->xref, fp);
+                               
+               READ_CACHE_DATA(msginfo->fromname, fp, memusage);
+
+               READ_CACHE_DATA(msginfo->date, fp, memusage);
+               READ_CACHE_DATA(msginfo->from, fp, memusage);
+               READ_CACHE_DATA(msginfo->to, fp, memusage);
+               READ_CACHE_DATA(msginfo->cc, fp, memusage);
+               READ_CACHE_DATA(msginfo->newsgroups, fp, memusage);
+               READ_CACHE_DATA(msginfo->subject, fp, memusage);
+               READ_CACHE_DATA(msginfo->msgid, fp, memusage);
+               READ_CACHE_DATA(msginfo->inreplyto, fp, memusage);
+               READ_CACHE_DATA(msginfo->xref, fp, memusage);
+               
                READ_CACHE_DATA_INT(msginfo->planned_download, fp);
                READ_CACHE_DATA_INT(msginfo->total_size, fp);
+               READ_CACHE_DATA_INT(refnum, fp);
+               
+               for (; refnum != 0; refnum--) {
+                       ref = NULL;
+
+                       READ_CACHE_DATA(ref, fp, memusage);
+
+                       if (ref && strlen(ref))
+                               msginfo->references =
+                                       g_slist_prepend(msginfo->references, ref);
+               }
+               if (msginfo->references)
+                       msginfo->references =
+                               g_slist_reverse(msginfo->references);
 
                msginfo->folder = item;
                msginfo->flags.tmp_flags |= tmp_flags;
@@ -473,10 +529,8 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
                g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
                if(msginfo->msgid)
                        g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
-               cache->memusage += procmsg_msginfo_memusage(msginfo);
        }
        fclose(fp);
-       g_hash_table_thaw(cache->msgnum_table);
 
        if (conv != NULL) {
                if (conv->free != NULL)
@@ -490,10 +544,15 @@ MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
        }
 
        cache->last_access = time(NULL);
+       cache->memusage = memusage;
 
        debug_print("done. (%d items read)\n", g_hash_table_size(cache->msgnum_table));
        debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
-
+#if 0
+       gettimeofday(&end, NULL);
+       timersub(&end, &start, &diff);
+       printf("spent %d seconds %d usages %d;%d\n", diff.tv_sec, diff.tv_usec, cache->memusage, memusage);
+#endif
        return cache;
 }
 
@@ -503,26 +562,40 @@ void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
        MsgInfo *msginfo;
        MsgPermFlags perm_flags;
        guint32 num;
-
-       if ((fp = msgcache_open_data_file(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL)
-               return;
-
-       debug_print("\tReading message marks from %s...\n", mark_file);
-
+       
+       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(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL) {
+               /* see if it isn't swapped ? */
+               if ((fp = msgcache_open_data_file(mark_file, bswap_32(MARK_VERSION), DATA_READ, NULL, 0)) == NULL)
+                       return;
+               else
+                       swapping = FALSE; /* yay */
+       }
+       debug_print("reading %sswapped mark file.\n", swapping?"":"un");
        while (fread(&num, sizeof(num), 1, fp) == 1) {
+               if (swapping)
+                       num = bswap_32(num);
                if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
-
+               if (swapping)
+                       perm_flags = bswap_32(perm_flags);
                msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
                if(msginfo) {
                        msginfo->flags.perm_flags = perm_flags;
                }
-       }
+       }       
        fclose(fp);
 }
 
 void msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
 {
        MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
+       GSList *cur;
 
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
        WRITE_CACHE_DATA_INT(msginfo->size, fp);
@@ -540,10 +613,15 @@ void msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
        WRITE_CACHE_DATA(msginfo->subject, fp);
        WRITE_CACHE_DATA(msginfo->msgid, fp);
        WRITE_CACHE_DATA(msginfo->inreplyto, fp);
-       WRITE_CACHE_DATA(msginfo->references, fp);
        WRITE_CACHE_DATA(msginfo->xref, fp);
        WRITE_CACHE_DATA_INT(msginfo->planned_download, fp);
        WRITE_CACHE_DATA_INT(msginfo->total_size, fp);
+        
+       WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
+
+       for (cur = msginfo->references; cur != NULL; cur = cur->next) {
+               WRITE_CACHE_DATA((gchar *)cur->data, fp);
+       }
 }
 
 static void msgcache_write_flags(MsgInfo *msginfo, FILE *fp)