/*
 * LibSylph -- E-Mail client library
 * Copyright (C) 1999-2013 Hiroyuki Yamamoto
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "defs.h"

#include <glib.h>
#include <glib/gi18n.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include<pthread.h>

#include "utils.h"
#include "procmsg.h"
#include "procheader.h"
#include "account.h"
#include "procmime.h"
#include "prefs_common.h"
#include "folder.h"
#include "codeconv.h"
#include "imap.h"

static pthread_mutex_t g_mutex_lock_cache_file = PTHREAD_MUTEX_INITIALIZER;

static pthread_mutex_t g_mutex_lock_mark_file = PTHREAD_MUTEX_INITIALIZER;

typedef struct _MsgFlagInfo {
    guint msgnum;
    MsgFlags flags;
} MsgFlagInfo;

static GSList *procmsg_read_cache_queue		(FolderItem	*item,
                         gboolean	 scan_file);

static void mark_sum_func			(gpointer	 key,
                         gpointer	 value,
                         gpointer	 data);

static GHashTable *procmsg_read_mark_file	(FolderItem	*item);
static void procmsg_write_mark_file		(FolderItem	*item,
                         GHashTable	*mark_table);

static GMappedFile *procmsg_open_cache_file_mmap(FolderItem	*item,
                         DataOpenMode	 mode);

static gint procmsg_cmp_by_mark			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_unread		(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_mime			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_label		(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_number		(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_size			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_date			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_from			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_to			(gconstpointer	 a,
                         gconstpointer	 b);
static gint procmsg_cmp_by_subject		(gconstpointer	 a,
                         gconstpointer	 b);

void cache_file_thread_mutext_trylock()
{
    pthread_mutex_trylock(&g_mutex_lock_cache_file);
}

void cache_file_thread_mutext_unlock()
{
    pthread_mutex_unlock(&g_mutex_lock_cache_file);
}

void cache_file_thread_mutext_destroy()
{
    pthread_mutex_destroy(&g_mutex_lock_cache_file);
}

GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
{
    GHashTable *msg_table;

    if (mlist == NULL) return NULL;

    msg_table = g_hash_table_new(NULL, g_direct_equal);
    procmsg_msg_hash_table_append(msg_table, mlist);

    return msg_table;
}

void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
{
    GSList *cur;
    MsgInfo *msginfo;

    if (msg_table == NULL || mlist == NULL) return;

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;

        g_hash_table_insert(msg_table,
                    GUINT_TO_POINTER(msginfo->msgnum),
                    msginfo);
    }
}

GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
{
    GHashTable *msg_table;
    GSList *cur;
    MsgInfo *msginfo;

    if (mlist == NULL) return NULL;

    msg_table = g_hash_table_new(NULL, g_direct_equal);

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
    }

    return msg_table;
}

gint procmsg_read_cache_data_str(FILE *fp, gchar **str)
{
    guint32 len;
    gchar *tmp = NULL;

    g_return_val_if_fail(fp != NULL, -1);
    g_return_val_if_fail(str != NULL, -1);

    if (fread(&len, sizeof(len), 1, fp) != 1)
        return -1;

    if (len > G_MAXINT)
        return -1;

    gchar *buf = g_malloc0(BUFFSIZE * sizeof (gchar));
    while (len > 0) {
        size_t size = MIN(len, BUFFSIZE * sizeof (gchar));

        if (fread(buf, size, 1, fp) != 1) {
            if (tmp)
                g_free(tmp);
            *str = NULL;
            g_free(buf);
            return -1;
        }

        buf[size] = '\0';
        if (tmp) {
            *str = g_strconcat(tmp, buf, NULL);
            g_free(tmp);
            tmp = *str;
        } else
            tmp = *str = g_strdup(buf);

        len -= size;
    }
    g_free(buf);
    return 0;
}

static gint procmsg_read_cache_data_str_mem(const gchar **p, const gchar *endp, gchar **str)
{
    guint32 len;

    g_return_val_if_fail(p != NULL, -1);
    g_return_val_if_fail(*p != NULL, -1);
    g_return_val_if_fail(endp != NULL, -1);
    g_return_val_if_fail(str != NULL, -1);

    if (endp - *p < sizeof(len))
        return -1;

    memcpy(&len, *p, sizeof(len));
    *p += sizeof(len);
    if (len > G_MAXINT || len > endp - *p)
        return -1;

    if (len > 0) {
        if(NULL == p || (NULL == *p))
            return -1;
        *str = g_strndup(*p, len);
        *p += len;
    }

    return 0;
}

#define READ_CACHE_DATA(data)						\
{									\
    if (procmsg_read_cache_data_str_mem(&p, endp, &data) < 0) {	\
        g_warning("Cache data is corrupted\n");			\
        procmsg_msginfo_free(msginfo);				\
        procmsg_msg_list_free(mlist);				\
        g_mapped_file_free(mapfile);				\
        return NULL;					\
    }								\
}

#define READ_CACHE_DATA_INT(n)					\
{								\
    if (endp - p < sizeof(guint32)) {			\
        g_warning("Cache data is corrupted\n");		\
        procmsg_msginfo_free(msginfo);			\
        procmsg_msg_list_free(mlist);			\
        g_mapped_file_free(mapfile);			\
        return NULL;					\
    } else {						\
        guint32 idata;					\
        memcpy(&idata, p, sizeof(idata));		\
        n = idata;					\
        p += sizeof(guint32);				\
    }							\
}

GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file)
{
    GSList *mlist = NULL;
    GSList *last = NULL;
    GMappedFile *mapfile;
    const gchar *filep;
    gsize file_len;
    const gchar *p, *endp;
    MsgInfo *msginfo;
    MsgFlags default_flags;
    guint32 num;
    guint refnum;
    FolderType type;

    g_return_val_if_fail(item != NULL, NULL);
    g_return_val_if_fail(item->folder != NULL, NULL);
    type = FOLDER_TYPE(item->folder);

    cache_file_thread_mutext_trylock();

    default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
    default_flags.tmp_flags = 0;
    if (type == F_MH ||type == F_POP || type == F_IMAP) {
        if (item->stype == F_QUEUE) {
            MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
        } else if (item->stype == F_DRAFT) {
            MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
        }
    }
    if (type == F_IMAP) {
        MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
    } else if (type == F_NEWS) {
        MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
    }

    if (type == F_MH||type == F_POP) {
        gchar *path;

        path = folder_item_get_path(item);
        if (change_dir(path) < 0) {
            g_free(path);
            cache_file_thread_mutext_unlock();
            return NULL;
        }
        g_free(path);
    }

    mapfile = procmsg_open_cache_file_mmap(item, DATA_READ);
    if (!mapfile) {
        item->cache_dirty = TRUE;
        cache_file_thread_mutext_unlock();
        return NULL;
    }

    debug_print("Reading summary cache...\n");

    filep = g_mapped_file_get_contents(mapfile);
//    g_print("filep:%s\n",filep);
    file_len = g_mapped_file_get_length(mapfile);
    endp = filep + file_len;
    p = filep + sizeof(guint32); /* version */

    while (endp - p >= sizeof(num)) {
        msginfo = g_new0(MsgInfo, 1);
//        msginfo->references = NULL;
//        msginfo->receivedtime = 0;
//        msginfo->encinfo = NULL;
//        msginfo->xface = NULL;
//        msginfo->fromname = NULL;
//        msginfo->date = NULL;
//        msginfo->from = NULL;
//        msginfo->to = NULL;
//        msginfo->cc = NULL;
//        msginfo->newsgroups = NULL;
//        msginfo->subject = NULL;
//        msginfo->msgid = NULL;
//        msginfo->inreplyto = NULL;
//        msginfo->xpriority = NULL;
//        msginfo->disposition_notification_to = NULL;
//        msginfo->separatesend = NULL;
//        msginfo->datetimesend = NULL;
//        msginfo->sendresultflags = NULL;
//        msginfo->references = NULL;
//        msginfo->file_path = NULL;

        READ_CACHE_DATA_INT(msginfo->msgnum);
//        g_print("%d\n",msginfo->msgnum);
        READ_CACHE_DATA_INT(msginfo->size);
        READ_CACHE_DATA_INT(msginfo->mtime);
        READ_CACHE_DATA_INT(msginfo->date_t);
        READ_CACHE_DATA_INT(msginfo->receivedtime);

        READ_CACHE_DATA_INT(msginfo->flags.tmp_flags);

        READ_CACHE_DATA_INT(msginfo->attachmentnum);//add shui

        gint reserveint=0;
        READ_CACHE_DATA_INT(msginfo->calendarflag);//add shang 会议标识
        READ_CACHE_DATA_INT(reserveint);//add shui 备用
        READ_CACHE_DATA_INT(reserveint);//add shui 备用

//        if(msginfo->attachmentnum>0){
//          g_print("read attachmentnum:%d\n",msginfo->attachmentnum);
//        }

        READ_CACHE_DATA(msginfo->fromname);

        READ_CACHE_DATA(msginfo->date);
        READ_CACHE_DATA(msginfo->from);
        READ_CACHE_DATA(msginfo->to);
        READ_CACHE_DATA(msginfo->newsgroups);
        READ_CACHE_DATA(msginfo->subject);
        READ_CACHE_DATA(msginfo->msgid);
        READ_CACHE_DATA(msginfo->inreplyto);
        READ_CACHE_DATA(msginfo->xpriority);
        READ_CACHE_DATA(msginfo->sendresultflags);
        READ_CACHE_DATA(msginfo->cc);
        READ_CACHE_DATA(msginfo->uos_msgid);
        READ_CACHE_DATA(msginfo->disposition_notification_to);
        gchar *reservechar=NULL;
        READ_CACHE_DATA(msginfo->datetimesend);//add shang 定时发送字段
        if(reservechar) {
            g_free(reservechar);
            reservechar = NULL;
        }
        READ_CACHE_DATA(reservechar);//add shui 备用
        if(reservechar) {
            g_free(reservechar);
            reservechar = NULL;
        }
        READ_CACHE_DATA(reservechar);//add shui 备用
        if(reservechar) {
            g_free(reservechar);
            reservechar = NULL;
        }
        READ_CACHE_DATA_INT(refnum);
        for (; refnum != 0; refnum--) {
            gchar *ref;

            READ_CACHE_DATA(ref);
            msginfo->references =
                g_slist_prepend(msginfo->references, ref);
        }
        if (msginfo->references)
            msginfo->references =
                g_slist_reverse(msginfo->references);

        MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
        MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);

        /* if the message file doesn't exist or is changed,
           don't add the data */
        if (((type == F_MH||type == F_POP)&& scan_file &&
             folder_item_is_msg_changed(item, msginfo)) ||
             msginfo->msgnum == 0) {
            procmsg_msginfo_free(msginfo);
            item->cache_dirty = TRUE;
        } else {
            msginfo->folder = item;

            if (!mlist)
                last = mlist = g_slist_append(NULL, msginfo);
            else {
                last = g_slist_append(last, msginfo);
                last = last->next;
            }
        }
    }

    g_mapped_file_free(mapfile);

    if (item->cache_queue) {
        GSList *qlist;
        qlist = procmsg_read_cache_queue(item, scan_file);
        mlist = g_slist_concat(mlist, qlist);
    }

    cache_file_thread_mutext_unlock();

    return mlist;
}

static GSList *procmsg_read_cache_queue(FolderItem *item, gboolean scan_file)
{
    FolderType type;
    MsgInfo *msginfo;
    MsgFlags default_flags;
    GSList *cur;
    GSList *qlist = NULL;

    g_return_val_if_fail(item != NULL, NULL);
    g_return_val_if_fail(item->folder != NULL, NULL);

    if (!item->cache_queue)
        return NULL;

    debug_print("Reading cache queue...\n");

    type = FOLDER_TYPE(item->folder);
    default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
    default_flags.tmp_flags = 0;

    for (cur = item->cache_queue; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;

        debug_print("read cache queue: %s/%d\n",
                item->path, msginfo->msgnum);

        MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
        MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);

        if (((type == F_MH||type == F_POP)&& scan_file &&
             folder_item_is_msg_changed(item, msginfo))) {
            procmsg_msginfo_free(msginfo);
            item->cache_dirty = TRUE;
        } else {
            msginfo->folder = item;
            qlist = g_slist_prepend(qlist, msginfo);
        }
    }

    g_slist_free(item->cache_queue);
    item->cache_queue = NULL;
    item->cache_dirty = TRUE;

    return qlist;
}

static void mark_unset_new_func(gpointer key, gpointer value, gpointer data)
{
    MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW);
}

void procmsg_set_flags(GSList *mlist, FolderItem *item)
{
    GSList *cur;
    gint new = 0, unread = 0, total = 0;
    gint lastnum = 0;
    gint unflagged = 0;
    gboolean mark_queue_exist;
    MsgInfo *msginfo;
    GHashTable *mark_table;
    MsgFlags *flags;

    g_return_if_fail(item != NULL);
    g_return_if_fail(item->folder != NULL);

//	debug_print("Marking the messages...\n");

    mark_queue_exist = (item->mark_queue != NULL);
    mark_table = procmsg_read_mark_file(item);
    if (!mark_table) {
        item->new1 = item->unread = item->total = g_slist_length(mlist);
        item->updated = TRUE;
        item->mark_dirty = TRUE;
        return;
    }

    /* unset new flags if new (unflagged) messages exist */
    if (!mark_queue_exist) {
        for (cur = mlist; cur != NULL; cur = cur->next) {
            msginfo = (MsgInfo *)cur->data;
            flags = g_hash_table_lookup
                (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
            if (!flags) {
                g_hash_table_foreach(mark_table,
                             mark_unset_new_func, NULL);
                item->mark_dirty = TRUE;
                break;
            }
        }
    }

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;

        if (lastnum < msginfo->msgnum)
            lastnum = msginfo->msgnum;

        flags = g_hash_table_lookup
            (mark_table, GUINT_TO_POINTER(msginfo->msgnum));

        if (flags != NULL) {
            /* add the permanent flags only */
            msginfo->flags.perm_flags = flags->perm_flags;
            if (MSG_IS_NEW(*flags))
                ++new;
            if (MSG_IS_UNREAD(*flags))
                ++unread;
            if (FOLDER_TYPE(item->folder) == F_IMAP) {
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
            } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
            }
        } else {
            ++unflagged;
            ++new;
            ++unread;
        }

        ++total;
    }

    item->new1 = new;
    item->unread = unread;
    item->total = total;
    item->unmarked_num = unflagged;
    item->last_num = lastnum;
    item->updated = TRUE;

    if (unflagged > 0)
        item->mark_dirty = TRUE;

    debug_print("new: %d unread: %d unflagged: %d total: %d\n",
            new, unread, unflagged, total);

    hash_free_value_mem(mark_table);
    g_hash_table_destroy(mark_table);
}

static void mark_all_read_func(gpointer key, gpointer value, gpointer data)
{
    MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW|MSG_UNREAD);
}

void procmsg_mark_all_read(FolderItem *item)
{
    GHashTable *mark_table;

    g_return_if_fail(item != NULL);

    debug_print("Marking all messages as read\n");

    mark_table = procmsg_read_mark_file(item);
    if (mark_table) {
        g_hash_table_foreach(mark_table, mark_all_read_func, NULL);
        procmsg_write_mark_file(item, mark_table);
        hash_free_value_mem(mark_table);
        g_hash_table_destroy(mark_table);
    }

    if (item->mark_queue) {
        GSList *cur;
        MsgFlagInfo *flaginfo;

        for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
            flaginfo = (MsgFlagInfo *)cur->data;
            MSG_UNSET_PERM_FLAGS
                (flaginfo->flags, MSG_NEW|MSG_UNREAD);
        }
        item->mark_dirty = TRUE;
    }

    item->new1 = item->unread = 0;
}

static FolderSortType cmp_func_sort_type;

GSList *procmsg_sort_msg_list(GSList *mlist, FolderSortKey sort_key,
                  FolderSortType sort_type)
{
    GCompareFunc cmp_func;

    switch (sort_key) {
    case SORT_BY_MARK:
        cmp_func = procmsg_cmp_by_mark; break;
    case SORT_BY_UNREAD:
        cmp_func = procmsg_cmp_by_unread; break;
    case SORT_BY_MIME:
        cmp_func = procmsg_cmp_by_mime; break;
    case SORT_BY_LABEL:
        cmp_func = procmsg_cmp_by_label; break;
    case SORT_BY_NUMBER:
        cmp_func = procmsg_cmp_by_number; break;
    case SORT_BY_SIZE:
        cmp_func = procmsg_cmp_by_size; break;
    case SORT_BY_DATE:
        cmp_func = procmsg_cmp_by_date; break;
    case SORT_BY_FROM:
        cmp_func = procmsg_cmp_by_from; break;
    case SORT_BY_SUBJECT:
        cmp_func = procmsg_cmp_by_subject; break;
    case SORT_BY_TO:
        cmp_func = procmsg_cmp_by_to; break;
    default:
        return mlist;
    }

    cmp_func_sort_type = sort_type;

    mlist = g_slist_sort(mlist, cmp_func);

    return mlist;
}

gint procmsg_get_last_num_in_msg_list(GSList *mlist)
{
    GSList *cur;
    MsgInfo *msginfo;
    gint last = 0;

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        if (msginfo && msginfo->msgnum > last)
            last = msginfo->msgnum;
    }

    return last;
}

void procmsg_msg_list_free(GSList *mlist)
{
    GSList *cur;
    MsgInfo *msginfo;

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        procmsg_msginfo_free(msginfo);
    }
    g_slist_free(mlist);
}

void procmsg_write_cache(MsgInfo *msginfo, FILE *fp)
{
    g_return_if_fail(msginfo != NULL);
    g_return_if_fail(fp != NULL);

    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);
    WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
    WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
    WRITE_CACHE_DATA_INT(msginfo->receivedtime, fp);
    WRITE_CACHE_DATA_INT(flags, fp);
    WRITE_CACHE_DATA_INT(msginfo->attachmentnum, fp);//add shui
    WRITE_CACHE_DATA_INT(msginfo->calendarflag, fp);//add shang 会议标记
    WRITE_CACHE_DATA_INT(0, fp);//add shui 备用
    WRITE_CACHE_DATA_INT(0, fp);//add shui 备用

//    if(msginfo->attachmentnum>0){
//        g_print("write attachmentnum:%d\n",msginfo->attachmentnum);
//    }

    WRITE_CACHE_DATA(msginfo->fromname, fp);

    WRITE_CACHE_DATA(msginfo->date, fp);
    WRITE_CACHE_DATA(msginfo->from, fp);
    WRITE_CACHE_DATA(msginfo->to, fp);
    WRITE_CACHE_DATA(msginfo->newsgroups, fp);
    WRITE_CACHE_DATA(msginfo->subject, fp);
    WRITE_CACHE_DATA(msginfo->msgid, fp);
    WRITE_CACHE_DATA(msginfo->inreplyto, fp);
    WRITE_CACHE_DATA(msginfo->xpriority, fp);
    if(!msginfo->sendresultflags)
        msginfo->sendresultflags=g_strdup("1");
    WRITE_CACHE_DATA(msginfo->sendresultflags, fp);
    WRITE_CACHE_DATA(msginfo->cc, fp);
//    if(!msginfo->uos_msgid)
//        msginfo->uos_msgid=g_strdup("0");
    WRITE_CACHE_DATA(msginfo->uos_msgid, fp);
    WRITE_CACHE_DATA(msginfo->disposition_notification_to, fp);
    WRITE_CACHE_DATA(msginfo->datetimesend, fp);//add shang 定时发送字段
    WRITE_CACHE_DATA(NULL, fp);//add shui 备用
    WRITE_CACHE_DATA(NULL, fp);//add shui 备用

    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);
    }
}

void procmsg_write_flags(MsgInfo *msginfo, FILE *fp)
{
    g_return_if_fail(msginfo != NULL);
    g_return_if_fail(fp != NULL);

    MsgPermFlags flags = msginfo->flags.perm_flags;

    WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
    WRITE_CACHE_DATA_INT(flags, fp);
}

void procmsg_write_cache_list(FolderItem *item, GSList *mlist)
{
    FILE *fp;
    GSList *cur;

    g_return_if_fail(item != NULL);

    debug_print("Writing summary cache (%s)\n", item->path);

    cache_file_thread_mutext_trylock();

    fp = procmsg_open_cache_file(item, DATA_WRITE);
    if (fp == NULL){
        cache_file_thread_mutext_unlock();
        return;
    }

    for (cur = mlist; cur != NULL; cur = cur->next) {
        MsgInfo *msginfo = (MsgInfo *)cur->data;
        procmsg_write_cache(msginfo, fp);
    }

    if (item->cache_queue)
        procmsg_flush_cache_queue(item, fp);

    fclose(fp);
    item->cache_dirty = FALSE;

    cache_file_thread_mutext_unlock();
}

void procmsg_write_flags_list(FolderItem *item, GSList *mlist)
{
    FILE *fp;
    GSList *cur;

    g_return_if_fail(item != NULL);

    mark_file_thread_mutext_trylock();

    debug_print("Writing summary flags (%s)\n", item->path);

    fp = procmsg_open_mark_file(item, DATA_WRITE);
    if (fp == NULL){
        mark_file_thread_mutext_unlock();
        return;
    }

    for (cur = mlist; cur != NULL; cur = cur->next) {
        MsgInfo *msginfo = (MsgInfo *)cur->data;
        procmsg_write_flags(msginfo, fp);
    }

    if (item->mark_queue)
        procmsg_flush_mark_queue(item, fp);

    fclose(fp);
    item->mark_dirty = FALSE;

    mark_file_thread_mutext_unlock();
}

static gint cmp_by_item(gconstpointer a, gconstpointer b)
{
    const MsgInfo *msginfo1 = a;
    const MsgInfo *msginfo2 = b;

    if (msginfo1->folder == msginfo2->folder)
        return msginfo1->msgnum - msginfo2->msgnum;

    return msginfo1->folder - msginfo2->folder;
}

void procmsg_write_flags_for_multiple_folders(GSList *mlist)
{
    GSList *tmp_list, *cur;
    FolderItem *prev_item = NULL;
    FILE *fp = NULL;

    if (!mlist)
        return;

    tmp_list = g_slist_copy(mlist);
    tmp_list = g_slist_sort(tmp_list, cmp_by_item);

    mark_file_thread_mutext_trylock();

    for (cur = tmp_list; cur != NULL; cur = cur->next) {
        MsgInfo *msginfo = (MsgInfo *)cur->data;
        FolderItem *item = msginfo->folder;

        if (prev_item != item) {
            if (fp)
                fclose(fp);
            fp = procmsg_open_mark_file(item, DATA_APPEND);
            if (!fp) {
                g_warning("can't open mark file\n");
                break;
            }
            item->updated = TRUE;
        }
        procmsg_write_flags(msginfo, fp);
        prev_item = item;
    }

    if (fp)
        fclose(fp);

    mark_file_thread_mutext_unlock();

    g_slist_free(tmp_list);
}

void procmsg_flush_mark_queue(FolderItem *item, FILE *fp)
{
    MsgFlagInfo *flaginfo;
    MsgInfo msginfo = {0};
    gboolean append = FALSE;
    GSList *qlist, *cur;

    g_return_if_fail(item != NULL);

    if (!item->mark_queue)
        return;

    debug_print("flushing mark_queue: %s ...\n", item->path);

    mark_file_thread_mutext_trylock();

    if (!fp) {
        append =  TRUE;
        fp = procmsg_open_mark_file(item, DATA_APPEND);
        if(!fp)
            mark_file_thread_mutext_unlock();
        g_return_if_fail(fp != NULL);
    }

    qlist = g_slist_reverse(item->mark_queue);
    item->mark_queue = NULL;

    for (cur = qlist; cur != NULL; cur = cur->next) {
        flaginfo = (MsgFlagInfo *)cur->data;

        msginfo.msgnum = flaginfo->msgnum;
        msginfo.flags = flaginfo->flags;
        procmsg_write_flags(&msginfo, fp);
        g_free(flaginfo);
    }

    g_slist_free(qlist);

    if (append)
        fclose(fp);

    mark_file_thread_mutext_unlock();
}

void procmsg_add_mark_queue(FolderItem *item, gint num, MsgFlags flags)
{
    MsgFlagInfo *flaginfo;

    g_return_if_fail(item != NULL);

    flaginfo = g_new(MsgFlagInfo, 1);
    flaginfo->msgnum = (unsigned int)num;
    flaginfo->flags = flags;
    item->mark_queue = g_slist_prepend(item->mark_queue, flaginfo);
}

void procmsg_flaginfo_list_free(GSList *flaglist)
{
    GSList *cur;
    MsgFlagInfo *flaginfo;

    for (cur = flaglist; cur != NULL; cur = cur->next) {
        flaginfo = (MsgFlagInfo *)cur->data;
        g_free(flaginfo);
    }
    g_slist_free(flaglist);
}

void procmsg_flush_cache_queue(FolderItem *item, FILE *fp)
{
    MsgInfo *msginfo;
    gboolean append = FALSE;
    GSList *qlist, *cur;

    g_return_if_fail(item != NULL);

    if (!item->cache_queue)
        return;

    debug_print("flushing cache_queue: %s ...\n", item->path);

    cache_file_thread_mutext_trylock();

    if (!fp) {
        append =  TRUE;
        fp = procmsg_open_cache_file(item, DATA_APPEND);
        if(!fp)
            cache_file_thread_mutext_unlock();
        g_return_if_fail(fp != NULL);
    }

    qlist = g_slist_reverse(item->cache_queue);
    item->cache_queue = NULL;

    for (cur = qlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;

        debug_print("flush cache queue: %s/%d\n",
                item->path, msginfo->msgnum);
        procmsg_write_cache(msginfo, fp);
        procmsg_msginfo_free(msginfo);
    }

    g_slist_free(qlist);

    if (append)
        fclose(fp);

    cache_file_thread_mutext_unlock();
}

void procmsg_add_cache_queue(FolderItem *item, gint num, MsgInfo *msginfo)
{
    MsgInfo *queue_msginfo;

    g_return_if_fail(item != NULL);
    g_return_if_fail(msginfo != NULL);

    queue_msginfo = procmsg_msginfo_copy(msginfo);
    queue_msginfo->msgnum = num;
    queue_msginfo->folder = item;
    if (queue_msginfo->file_path) {
        g_free(queue_msginfo->file_path);
        queue_msginfo->file_path = NULL;
    }

//	debug_print("procmsg_add_cache_queue: add msg cache: %s/%d\n",
//		    item->path, num);
    item->cache_queue = g_slist_prepend(item->cache_queue, queue_msginfo);
}

gboolean procmsg_flush_folder(FolderItem *item)
{
    gboolean flushed = FALSE;
    gint n_new, n_unread, n_total, n_min, n_max;

    g_return_val_if_fail(item != NULL, FALSE);
    g_return_val_if_fail(item->folder != NULL, FALSE);

    if (FOLDER_TYPE(item->folder) != F_MH || item->last_num < 0) {
        folder_item_scan(item);
        return TRUE;
    }

    if (item->mark_queue && !item->opened)
        flushed = TRUE;
    procmsg_get_mark_sum(item, &n_new, &n_unread, &n_total, &n_min, &n_max,
                 0);
    item->unmarked_num = 0;
    item->new1 = n_new;
    item->unread = n_unread;
    item->total = n_total;

    if (item->cache_queue && !item->opened) {
        procmsg_flush_cache_queue(item, NULL);
        flushed = TRUE;
    }

    if (flushed)
        debug_print("procmsg_flush_folder: flushed %s\n", item->path);

    return flushed;
}

static void procmsg_flush_folder_foreach_func(gpointer key, gpointer val,
                          gpointer data)
{
    procmsg_flush_folder(FOLDER_ITEM(key));
}

void procmsg_flush_folder_foreach(GHashTable *folder_table)
{
    g_hash_table_foreach(folder_table, procmsg_flush_folder_foreach_func,
                 NULL);
}

void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags)
{
    FILE *fp;
    MsgInfo msginfo;

    g_return_if_fail(item != NULL);

    if (item->opened) {
        procmsg_add_mark_queue(item, num, flags);
        return;
    }

    if ((fp = procmsg_open_mark_file(item, DATA_APPEND)) == NULL) {
        g_warning(_("can't open mark file\n"));
        return;
    }

    msginfo.msgnum = num;
    msginfo.flags = flags;

    procmsg_write_flags(&msginfo, fp);
    fclose(fp);
}

struct MarkSum {
    gint *new;
    gint *unread;
    gint *total;
    gint *min;
    gint *max;
    gint first;
};

static void mark_sum_func(gpointer key, gpointer value, gpointer data)
{
    MsgFlags *flags = value;
    gint num = GPOINTER_TO_INT(key);
    struct MarkSum *marksum = data;

    if (marksum->first <= num) {
        if (MSG_IS_NEW(*flags)) (*marksum->new)++;
        if (MSG_IS_UNREAD(*flags)) (*marksum->unread)++;
        if (num > *marksum->max) *marksum->max = num;
        if (num < *marksum->min || *marksum->min == 0) *marksum->min = num;
        (*marksum->total)++;
    }

    g_free(flags);
}

void procmsg_get_mark_sum(FolderItem *item,
              gint *new, gint *unread, gint *total,
              gint *min, gint *max,
              gint first)
{
    GHashTable *mark_table;
    struct MarkSum marksum;

    *new = *unread = *total = *min = *max = 0;
    marksum.new    = new;
    marksum.unread = unread;
    marksum.total  = total;
    marksum.min    = min;
    marksum.max    = max;
    marksum.first  = first;

    mark_table = procmsg_read_mark_file(item);

    if (mark_table) {
        g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
        g_hash_table_destroy(mark_table);
    }
}

static GHashTable *procmsg_read_mark_file(FolderItem *item)
{
    FILE *fp;
    GHashTable *mark_table = NULL;
    guint32 idata;
    guint num;
    MsgFlags *flags;
    MsgPermFlags perm_flags;
    GSList *cur;

    g_return_val_if_fail(item != NULL, NULL);

    mark_file_thread_mutext_trylock();

    if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL){
        mark_file_thread_mutext_unlock();
        return NULL;
    }

    mark_table = g_hash_table_new(NULL, g_direct_equal);

    while (fread(&idata, sizeof(idata), 1, fp) == 1) {
        num = idata;
        if (fread(&idata, sizeof(idata), 1, fp) != 1) break;
        perm_flags = idata;

        flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num));
        if (flags != NULL)
            g_free(flags);

        flags = g_new0(MsgFlags, 1);
        flags->perm_flags = perm_flags;

        g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
    }

    fclose(fp);

    mark_file_thread_mutext_unlock();

    if (item->mark_queue) {
        g_hash_table_foreach(mark_table, mark_unset_new_func, NULL);
        item->mark_dirty = TRUE;
    }

    for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
        MsgFlagInfo *flaginfo = (MsgFlagInfo *)cur->data;

        flags = g_hash_table_lookup(mark_table,
                        GUINT_TO_POINTER(flaginfo->msgnum));
        if (flags != NULL)
            g_free(flags);

        flags = g_new0(MsgFlags, 1);
        flags->perm_flags = flaginfo->flags.perm_flags;

        g_hash_table_insert(mark_table,
                    GUINT_TO_POINTER(flaginfo->msgnum), flags);

    }

    if (item->mark_queue && !item->opened) {
        procmsg_write_mark_file(item, mark_table);
        procmsg_flaginfo_list_free(item->mark_queue);
        item->mark_queue = NULL;
        item->mark_dirty = FALSE;
    }

    return mark_table;
}

static void write_mark_func(gpointer key, gpointer value, gpointer data)
{
    MsgInfo msginfo;

    g_return_if_fail(value != NULL);

    msginfo.msgnum = GPOINTER_TO_UINT(key);
    msginfo.flags.perm_flags = ((MsgFlags *)value)->perm_flags;
    procmsg_write_flags(&msginfo, (FILE *)data);
}

static void procmsg_write_mark_file(FolderItem *item, GHashTable *mark_table)
{
    mark_file_thread_mutext_trylock();

    FILE *fp;

    if ((fp = procmsg_open_mark_file(item, DATA_WRITE)) == NULL) {
        g_warning("procmsg_write_mark_file: cannot open mark file.");
        mark_file_thread_mutext_unlock();
        return;
    }
    g_hash_table_foreach(mark_table, write_mark_func, fp);
    fclose(fp);

    mark_file_thread_mutext_unlock();
}

FILE *procmsg_open_data_file(const gchar *file, guint version,
                 DataOpenMode mode, gchar *buf, size_t buf_size)
{
    FILE *fp;
    guint32 data_ver = 0;

    g_return_val_if_fail(file != NULL, NULL);

    if (mode == DATA_WRITE) {
        if ((fp = g_fopen(file, "wb")) == NULL) {
            if (errno == EACCES) {
                change_file_mode_rw(NULL, file);
                if ((fp = g_fopen(file, "wb")) == NULL) {
                    FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
                    return NULL;
                }
            } else {
                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
                return NULL;
            }
        }
        if (change_file_mode_rw(fp, file) < 0)
            FILE_OP_ERROR(file, "chmod");

        WRITE_CACHE_DATA_INT(version, fp);
        return fp;
    }

    /* check version */
    if ((fp = g_fopen(file, "rb")) == NULL) {
        if (errno == EACCES) {
            change_file_mode_rw(NULL, file);
            if ((fp = g_fopen(file, "rb")) == NULL) {
                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
            }
        } else {
            debug_print("Mark/Cache file '%s' not found\n", file);
        }
    }

    if (fp) {
        if (buf && buf_size > 0)
            setvbuf(fp, buf, _IOFBF, buf_size);
        if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1) {
            g_warning("%s: cannot read mark/cache file (truncated?)\n", file);
            fclose(fp);
            fp = NULL;
        } else if (version != data_ver) {
            g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
                  file, data_ver, version);
            fclose(fp);
            fp = NULL;
        }
    }

    if (mode == DATA_READ)
        return fp;

    if (fp) {
        /* reopen with append mode */
        fclose(fp);
        if ((fp = g_fopen(file, "ab")) == NULL) {
            if (errno == EACCES) {
                change_file_mode_rw(NULL, file);
                if ((fp = g_fopen(file, "ab")) == NULL) {
                    FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
                }
            } else {
                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
            }
        }
    } else {
        /* open with overwrite mode if mark file doesn't exist or
           version is different */
        fp = procmsg_open_data_file(file, version, DATA_WRITE, buf,
                        buf_size);
    }

    return fp;
}

static GMappedFile *procmsg_open_cache_file_mmap(FolderItem *item,
                         DataOpenMode mode)
{
    gchar *cachefile;
    GMappedFile *map = NULL;
    GError *error = NULL;
    gsize size;
    guint32 data_ver = 0;
    gchar *p;

    if (mode != DATA_READ)
        return NULL;

    cache_file_thread_mutext_trylock();

    cachefile = folder_item_get_cache_file(item);
    if (cachefile) {
        map = g_mapped_file_new(cachefile, FALSE, &error);
        if (!map) {
            if (error && error->code == G_FILE_ERROR_NOENT)
                debug_print("%s: mark/cache file not found\n", cachefile);
            else if (error)
                g_warning("%s: cannot open mark/cache file: %s", cachefile, error->message);
            else
                g_warning("%s: cannot open mark/cache file", cachefile);
            if (error)
                g_error_free(error);
            g_free(cachefile);
            cache_file_thread_mutext_unlock();
            return NULL;
        }
        size = g_mapped_file_get_length(map);
        if (size < sizeof(data_ver)) {
            g_warning("%s: cannot read mark/cache file (truncated?)", cachefile);
            g_mapped_file_free(map);
            g_free(cachefile);
            cache_file_thread_mutext_unlock();
            return NULL;
        }
        p = g_mapped_file_get_contents(map);
        data_ver = *(guint32 *)p;
        if (CACHE_VERSION != data_ver) {
            g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
                  cachefile, data_ver, CACHE_VERSION);
            g_mapped_file_free(map);
            g_free(cachefile);
            cache_file_thread_mutext_unlock();
            return NULL;
        }
        g_free(cachefile);
    }

    cache_file_thread_mutext_unlock();

    return map;
}

FILE *procmsg_open_cache_file(FolderItem *item, DataOpenMode mode)
{
    gchar *cachefile;
    FILE *fp;

    cachefile = folder_item_get_cache_file(item);
//    g_print("--------procmsg_open_cache_file:%s\n",cachefile);
    fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, NULL, 0);
    g_free(cachefile);

    return fp;
}

FILE *procmsg_open_mark_file(FolderItem *item, DataOpenMode mode)
{
    gchar *markfile = NULL;
    FILE *fp = NULL;

    markfile = folder_item_get_mark_file(item);
    if (markfile != NULL) {
        fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0);
        g_free(markfile);
    }

    return fp;
}

void procmsg_clear_cache(FolderItem *item)
{
    cache_file_thread_mutext_trylock();

    FILE *fp;

    fp = procmsg_open_cache_file(item, DATA_WRITE);
    if (fp)
        fclose(fp);

    cache_file_thread_mutext_unlock();
}

void procmsg_clear_mark(FolderItem *item)
{
    mark_file_thread_mutext_trylock();

    FILE *fp;

    fp = procmsg_open_mark_file(item, DATA_WRITE);
    if (fp)
        fclose(fp);

    mark_file_thread_mutext_unlock();
}

/* return the reversed thread tree */
GNode *procmsg_get_thread_tree(GSList *mlist)
{
    GNode *root, *parent, *node, *next;
    GHashTable *table;
    MsgInfo *msginfo;
    const gchar *msgid;
    GSList *reflist;

    root = g_node_new(NULL);
    table = g_hash_table_new(g_str_hash, g_str_equal);

    for (; mlist != NULL; mlist = mlist->next) {
        msginfo = (MsgInfo *)mlist->data;
        parent = root;

        /* only look for the real parent first */
        if (msginfo->inreplyto) {
            parent = g_hash_table_lookup(table, msginfo->inreplyto);
            if (parent == NULL)
                parent = root;
        }

        node = g_node_insert_data_before
            (parent, parent == root ? parent->children : NULL,
             msginfo);
        if ((msgid = msginfo->msgid) &&
            g_hash_table_lookup(table, msgid) == NULL)
            g_hash_table_insert(table, (gchar *)msgid, node);
    }

    /* complete the unfinished threads */
    for (node = root->children; node != NULL; ) {
        next = node->next;
        msginfo = (MsgInfo *)node->data;
        parent = NULL;

        if (msginfo->inreplyto)
            parent = g_hash_table_lookup(table, msginfo->inreplyto);

        /* try looking for the indirect parent */
        if (!parent && msginfo->references) {
            for (reflist = msginfo->references;
                 reflist != NULL; reflist = reflist->next)
                if ((parent = g_hash_table_lookup
                    (table, reflist->data)) != NULL)
                    break;
        }

        /* node should not be the parent, and node should not
           be an ancestor of parent (circular reference) */
        if (parent && parent != node &&
            !g_node_is_ancestor(node, parent)) {
            g_node_unlink(node);
            g_node_insert_before
                (parent, parent->children, node);
        }
        node = next;
    }

    g_hash_table_destroy(table);

    return root;
}

static gboolean procmsg_thread_date_func(GNode *node, gpointer data)
{
    guint *tdate = (guint *)data;
    MsgInfo *msginfo = (MsgInfo *)node->data;

    if (*tdate < msginfo->date_t)
        *tdate = msginfo->date_t;

    return FALSE;
}

guint procmsg_get_thread_date(GNode *node)
{
    guint tdate = 0;

    g_return_val_if_fail(node != NULL && node->parent != NULL &&
                 node->parent->parent == NULL, 0);

    g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
            procmsg_thread_date_func, &tdate);

    return tdate;
}

gint procmsg_move_messages(GSList *mlist, gboolean bcachepool) //modify by shui
{
    GSList *cur, *movelist = NULL;
    MsgInfo *msginfo;
    FolderItem *dest = NULL;
    GHashTable *hash;
    gint val = 0;

    g_return_val_if_fail(mlist != NULL, -1);

    hash = procmsg_to_folder_hash_table_create(mlist);
    if(!bcachepool)
        folder_item_scan_foreach(hash);
    else
        folder_item_scan_foreach_cache(hash);
    g_hash_table_destroy(hash);

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        if (!dest) {
            dest = msginfo->to_folder;
            movelist = g_slist_append(movelist, msginfo);
        } else if (dest == msginfo->to_folder) {
            movelist = g_slist_append(movelist, msginfo);
        } else {
            val = folder_item_move_msgs(dest, movelist);
            g_slist_free(movelist);
            movelist = NULL;
            if (val == -1)
                return val;
            dest = msginfo->to_folder;
            movelist = g_slist_append(movelist, msginfo);
        }
    }

    if (movelist) {
        val = folder_item_move_msgs(dest, movelist);
        g_slist_free(movelist);
    }

    return val == -1 ? -1 : 0;
}

gint procmsg_copy_messages(GSList *mlist)
{
    GSList *cur, *copylist = NULL;
    MsgInfo *msginfo;
    FolderItem *dest = NULL;
    GHashTable *hash;
    gint val = 0;

    g_return_val_if_fail(mlist != NULL, -1);

    hash = procmsg_to_folder_hash_table_create(mlist);
    folder_item_scan_foreach(hash);
    g_hash_table_destroy(hash);

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        if (!dest) {
            dest = msginfo->to_folder;
            copylist = g_slist_append(copylist, msginfo);
        } else if (dest == msginfo->to_folder) {
            copylist = g_slist_append(copylist, msginfo);
        } else {
            val = folder_item_copy_msgs(dest, copylist);
            g_slist_free(copylist);
            copylist = NULL;
            if (val == -1)
                return val;
            dest = msginfo->to_folder;
            copylist = g_slist_append(copylist, msginfo);
        }
    }

    if (copylist) {
        val = folder_item_copy_msgs(dest, copylist);
        g_slist_free(copylist);
    }

    return val == -1 ? -1 : 0;
}

gint procmsg_add_messages_from_queue(FolderItem *dest, GSList *mlist,
                     gboolean is_move)
{
    MsgInfo *msginfo;
    GSList *cur;
    gchar *file;
    FILE *fp;
    gchar *dest_file;
    gboolean is_error = FALSE;
    FolderItem *src;
    MsgFlags flags;

    g_return_val_if_fail(dest != NULL, -1);
    g_return_val_if_fail(mlist != NULL, -1);

    msginfo = (MsgInfo *)mlist->data;
    if (!msginfo || !msginfo->folder || msginfo->folder->stype != F_QUEUE ||
        !MSG_IS_QUEUED(msginfo->flags) || dest->stype == F_QUEUE)
        return -1;

    debug_print("procmsg_add_messages_from_queue: adding messages from queue folder\n");

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;
        flags = msginfo->flags;
        if (!MSG_IS_QUEUED(flags))
            return -1;
        MSG_UNSET_TMP_FLAGS(flags, MSG_QUEUED);
        src = msginfo->folder;
        file = procmsg_get_message_file(msginfo);
        if (!file)
            return -1;
        if ((fp = g_fopen(file, "rb")) == NULL) {
            FILE_OP_ERROR(file, "folder_item_move_msgs: fopen");
            g_free(file);
            return -1;
        }
        gchar *buf = g_malloc0(BUFFSIZE * sizeof (gchar));
        while (fgets(buf, BUFFSIZE * sizeof (gchar), fp) != NULL) {
            if (buf[0] == '\r' || buf[0] == '\n')
                break;
        }
        g_free(buf);
        if (ferror(fp)) {
            fclose(fp);
            g_free(file);
            return -1;
        }

        dest_file = get_tmp_file();
        debug_print("copy queued msg: %s -> %s\n", file, dest_file);

        if (copy_file_part(fp, ftell(fp), G_MAXINT, dest_file) < 0) {
            fclose(fp);
            is_error = TRUE;
        } else {
            fclose(fp);
            if (folder_item_add_msg(dest, dest_file, &flags, TRUE) < 0) {
                g_unlink(dest_file);
                is_error = TRUE;
            } else if (is_move &&
                   folder_item_remove_msg(src, msginfo) < 0)
                is_error = TRUE;
        }

        g_free(dest_file);
        g_free(file);
        if (is_error)
            return -1;
    }

    return 0;
}

gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
{
    gchar *path, *file;

    g_return_val_if_fail(msginfo != NULL, NULL);

    if (msginfo->encinfo && msginfo->encinfo->plaintext_file)
        file = g_strdup(msginfo->encinfo->plaintext_file);
    else if (msginfo->file_path)
        return g_strdup(msginfo->file_path);
    else {
        g_return_val_if_fail(msginfo->folder != NULL, NULL);
        g_return_val_if_fail(msginfo->folder->folder != NULL, NULL);
        g_return_val_if_fail(msginfo->folder->folder->klass != NULL, NULL);
        gchar nstr[16];
        path = folder_item_get_path(msginfo->folder);
        file = g_strconcat(path, G_DIR_SEPARATOR_S,
                   utos_buf(nstr, msginfo->msgnum), NULL);
        g_free(path);
    }

    return file;
}

gchar *procmsg_get_message_file(MsgInfo *msginfo)
{
    gchar *filename = NULL;

    g_return_val_if_fail(msginfo != NULL, NULL);

    if (msginfo->file_path&&is_file_exist(msginfo->file_path))
        return g_strdup(msginfo->file_path);

    if(msginfo->folder&&msginfo->folder->folder&&msginfo->folder->folder->klass->type== F_IMAP&&TRUE==msginfo->fetch_large){
       filename = imap_fetch_large_msg(msginfo->folder->folder, msginfo->folder, msginfo->msgnum);
    }
    else{
       filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
    }
    if (!filename)
        log_print(_("can't fetch message %d\n"), msginfo->msgnum);

    return filename;
}

GSList *procmsg_get_message_file_list(GSList *mlist)
{
    GSList *file_list = NULL;
    MsgInfo *msginfo;
    MsgFileInfo *fileinfo;
    gchar *file;

    while (mlist != NULL) {
        msginfo = (MsgInfo *)mlist->data;
        file = procmsg_get_message_file(msginfo);
        if (!file) {
            procmsg_message_file_list_free(file_list);
            return NULL;
        }
        fileinfo = g_new(MsgFileInfo, 1);
        fileinfo->file = file;
        fileinfo->flags = g_new(MsgFlags, 1);
        *fileinfo->flags = msginfo->flags;
        file_list = g_slist_prepend(file_list, fileinfo);
        mlist = mlist->next;
    }

    file_list = g_slist_reverse(file_list);

    return file_list;
}

void procmsg_message_file_list_free(GSList *file_list)
{
    GSList *cur;
    MsgFileInfo *fileinfo;

    for (cur = file_list; cur != NULL; cur = cur->next) {
        fileinfo = (MsgFileInfo *)cur->data;
        g_free(fileinfo->file);
        g_free(fileinfo->flags);
        g_free(fileinfo);
    }

    g_slist_free(file_list);
}

FILE *procmsg_open_message(MsgInfo *msginfo)
{
    FILE *fp;
    gchar *file=NULL;

    g_return_val_if_fail(msginfo != NULL, NULL);

    file = procmsg_get_message_file_path(msginfo);
    g_return_val_if_fail(file != NULL, NULL);

    if (!is_file_exist(file)) {
        g_return_val_if_fail(msginfo->folder != NULL, NULL);
        //查看.cache 目录下是否有该邮件, 如果有则移动到当前目录下。
        gchar *folder_path = folder_get_path(msginfo->folder->folder);
        gchar *exchange_file = g_strdup_printf("%s%c.cache%c%d", folder_path, G_DIR_SEPARATOR, G_DIR_SEPARATOR, msginfo->msgnum);
        g_free(folder_path);
        if(is_file_exist(exchange_file)) {
            move_file(exchange_file, file, FALSE);
            goto folder_changed;
        }
        g_free(file);
        g_free(exchange_file);
        file = procmsg_get_message_file(msginfo);
        if (!file) {
            log_print("procmsg_get_message_file fail\n");
            return NULL;
        }
    }
    folder_changed:
    if ((fp = g_fopen(file, "rb")) == NULL) {
        FILE_OP_ERROR(file, "procmsg_open_message: fopen");
        g_free(file);
        return NULL;
    }

    g_free(file);

    if (MSG_IS_QUEUED(msginfo->flags)) {
        gchar buf[BUFFSIZE];

        while (fgets(buf, sizeof(buf), fp) != NULL)
            if (buf[0] == '\r' || buf[0] == '\n') break;
    }

    return fp;
}

static DecryptMessageFunc decrypt_message_func = NULL;
static gboolean auto_decrypt = TRUE;

void procmsg_set_decrypt_message_func(DecryptMessageFunc func)
{
    decrypt_message_func = func;
}

void procmsg_set_auto_decrypt_message(gboolean enabled)
{
    auto_decrypt = enabled;
}

FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
{
    FILE *fp;

    if (decrypt_message_func && auto_decrypt)
        return decrypt_message_func(msginfo, mimeinfo);

    *mimeinfo = NULL;
    if ((fp = procmsg_open_message(msginfo)) == NULL){
        log_print("procmsg_open_message fail\n");
        return NULL;
    }
    *mimeinfo = procmime_scan_mime_header(fp);

    return fp;
}

gboolean procmsg_msg_exist(MsgInfo *msginfo)
{
    gchar *path;
    gboolean ret;

    if (!msginfo) return FALSE;

    path = folder_item_get_path(msginfo->folder);
    change_dir(path);
    ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
    g_free(path);

    return ret;
}

gboolean procmsg_trash_messages_exist(void)
{
    FolderItem *trash;
    GList *cur;

    for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
        trash = FOLDER(cur->data)->trash;
        if (trash && trash->total > 0)
            return TRUE;
    }

    return FALSE;
}

void procmsg_empty_trash(FolderItem *trash)
{
    if (!trash)
        return;

    g_return_if_fail(trash->stype == F_TRASH || trash->stype == F_JUNK);

    if (trash->total > 0) {
        debug_print("Emptying messages in %s ...\n", trash->path);

        folder_item_remove_all_msg(trash);
        procmsg_clear_cache(trash);
        procmsg_clear_mark(trash);
        trash->cache_dirty = FALSE;
        trash->mark_dirty = FALSE;
    }
}

void procmsg_empty_all_trash(void)
{
    FolderItem *trash;
    GList *cur;

    for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
        trash = FOLDER(cur->data)->trash;
        procmsg_empty_trash(trash);
    }
}

static gboolean remove_all_cached_messages_func(GNode *node, gpointer data)
{
    FolderItem *item;
    gchar *dir;

    g_return_val_if_fail(node != NULL, FALSE);
    g_return_val_if_fail(node->data != NULL, FALSE);

    item = FOLDER_ITEM(node->data);
    if (!item->path || item->stype == F_VIRTUAL)
        return FALSE;

    dir = folder_item_get_path(item);
    if (is_dir_exist(dir)) {
        debug_print("removing all cached messages in '%s' ...\n",
                item->path);
        remove_all_numbered_files(dir);
    }
    g_free(dir);

    return FALSE;
}

void procmsg_remove_all_cached_messages(Folder *folder)
{
    g_return_if_fail(folder != NULL);
    g_return_if_fail(FOLDER_IS_REMOTE(folder));

    debug_print("Removing all caches in the mailbox '%s' ...\n",
            folder->name);

    g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
            remove_all_cached_messages_func, NULL);
}

gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file)
{
    gint num;
    MsgFlags flag = {0, 0};

    debug_print("saving sent message...\n");

    if (!outbox)
        outbox = folder_get_default_outbox();
    g_return_val_if_fail(outbox != NULL, -1);

    folder_item_scan(outbox);
    if ((num = folder_item_add_msg(outbox, file, &flag, FALSE)) < 0) {
        g_warning("can't save message\n");
        return -1;
    }
    procmsg_flush_folder(outbox);

    return num;
}

static guint print_id = 0;

static gint print_command_exec(const gchar *file, const gchar *cmdline)
{
    static const gchar *def_cmd = "lpr %s";
    gchar buf[1024];

#ifdef G_OS_WIN32
    if (canonicalize_file_replace(file) < 0)
        return -1;
#endif

    if (cmdline && str_find_format_times(cmdline, 's') == 1)
        g_snprintf(buf, sizeof(buf) - 1, cmdline, file);
    else {
        if (cmdline) {
            g_warning(_("Print command line is invalid: `%s'\n"),
                  cmdline);
            return -1;
        }

#ifdef G_OS_WIN32
        execute_print_file(file);
        return 0;
#else
        g_snprintf(buf, sizeof(buf) - 1, def_cmd, file);
#endif
    }

    g_strchomp(buf);
    if (buf[strlen(buf) - 1] != '&')
        strcat(buf, "&");

    if (system(buf) != 0)
        return -1;

    return 0;
}

static void procmsg_write_headers(MsgInfo *msginfo, MimeInfo *partinfo,
                  FILE *fp, FILE *dest_fp,
                  const gchar *encoding, gboolean all_headers)
{
    GPtrArray *headers;
    gint i;

    g_return_if_fail(msginfo != NULL);
    g_return_if_fail(dest_fp != NULL);

    if (all_headers)
        headers = procheader_get_header_array_asis(fp, NULL);
    else
        headers = procheader_get_header_array_for_display(fp, NULL);

    for (i = 0; i < headers->len; i++) {
        Header *hdr;
        gchar *file_str;
        const gchar *body;

        hdr = g_ptr_array_index(headers, i);

        if (partinfo) {
            if (!g_ascii_strcasecmp(hdr->name, "Subject") ||
                !g_ascii_strcasecmp(hdr->name, "From") ||
                !g_ascii_strcasecmp(hdr->name, "To") ||
                !g_ascii_strcasecmp(hdr->name, "Cc")) {
                unfold_line(hdr->body);
            }

            body = hdr->body;
            while (g_ascii_isspace(*body))
                body++;
        } else {
            if (!g_ascii_strcasecmp(hdr->name, "Subject"))
                body = msginfo->subject;
            else if (!g_ascii_strcasecmp(hdr->name, "From"))
                body = msginfo->from;
            else if (!g_ascii_strcasecmp(hdr->name, "To"))
                body = msginfo->to;
            else if (!g_ascii_strcasecmp(hdr->name, "Cc")) {
                unfold_line(hdr->body);
                body = hdr->body;
                while (g_ascii_isspace(*body))
                    body++;
            } else {
                body = hdr->body;
                while (g_ascii_isspace(*body))
                    body++;
            }
        }

        if (body && *body != '\0') {
            file_str = conv_codeset_strdup
                (body, CS_INTERNAL, encoding);
            fprintf(dest_fp, "%s: %s\n", hdr->name,
                file_str ? file_str : body);
            g_free(file_str);
        } else {
            fprintf(dest_fp, "%s: (none)\n", hdr->name);
        }
    }

    procheader_header_array_destroy(headers);
}

void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline,
               gboolean all_headers)
{
    gchar *prtmp;

    g_return_if_fail(msginfo != NULL);

    gchar *mimedir=get_mime_tmp_dir();
    prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
                mimedir, G_DIR_SEPARATOR,
                print_id++);
    g_free(mimedir);

    if (procmsg_save_message_as_text(msginfo, prtmp,
                     conv_get_locale_charset_str(),
                     all_headers) == 0)
        print_command_exec(prtmp, cmdline);

    g_free(prtmp);
}

void procmsg_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo,
                const gchar *cmdline, gboolean all_headers)
{
    FILE *msgfp, *tmpfp, *prfp;
    gchar *prtmp;

    if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
        return;
    }

    if ((tmpfp = procmime_get_text_content
        (partinfo, msgfp, conv_get_locale_charset_str())) == NULL) {
        fclose(msgfp);
        return;
    }
    fclose(msgfp);

    gchar *mimedir=get_mime_tmp_dir();
    prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
                mimedir, G_DIR_SEPARATOR,
                print_id++);
    g_free(mimedir);
    if ((prfp = g_fopen(prtmp, "w")) == NULL) {
        FILE_OP_ERROR(prtmp, "procmsg_print_message_part: fopen");
        g_free(prtmp);
        fclose(tmpfp);
        return;
    }

    gchar *buf = (gchar *)malloc(BUFFSIZE * sizeof (gchar));
    memset(buf, 0, BUFFSIZE * sizeof (gchar));
    while (fgets(buf, BUFFSIZE * sizeof (gchar), tmpfp) != NULL)
        fputs(buf, prfp);
    free(buf);
    fclose(prfp);
    fclose(tmpfp);

    print_command_exec(prtmp, cmdline);

    g_free(prtmp);
}

gint procmsg_save_message_as_text(MsgInfo *msginfo, const gchar *dest,
                  const gchar *encoding, gboolean all_headers)
{
    MimeInfo *mimeinfo, *partinfo;
    FILE *fp;
    FILE *tmpfp;
    FILE *destfp;
    gchar *part_str;
    gint ret = 0;

    g_return_val_if_fail(msginfo != NULL, -1);
    g_return_val_if_fail(dest != NULL, -1);

    mimeinfo = procmime_scan_message(msginfo);
    if (!mimeinfo)
        return -1;
    if ((fp = procmsg_open_message(msginfo)) == NULL) {
        procmime_mimeinfo_free_all(mimeinfo);
        return -1;
    }
    if ((destfp = g_fopen(dest, "w")) == NULL) {
        fclose(fp);
        procmime_mimeinfo_free_all(mimeinfo);
        return -1;
    }
    procmsg_write_headers(msginfo, mimeinfo, fp, destfp, encoding, all_headers);
    fputc('\n', destfp);

    partinfo = mimeinfo;

    gchar *buf = g_malloc0(BUFFSIZE * sizeof (gchar));
    while (partinfo != NULL) {
        if (fseek(fp, partinfo->fpos, SEEK_SET) < 0)
            break;

        if (partinfo->filename || partinfo->name)
            g_snprintf(buf, BUFFSIZE * sizeof (gchar), "\n[%s  %s (%s)]\n",
                   partinfo->filename ? partinfo->filename :
                   partinfo->name,
                   partinfo->content_type,
                   to_human_readable(partinfo->content_size));
        else
            g_snprintf(buf, BUFFSIZE * sizeof (gchar), "\n[%s (%s)]\n",
                   partinfo->content_type,
                   to_human_readable(partinfo->content_size));
        part_str = conv_codeset_strdup(buf, CS_INTERNAL, encoding);
        if (!part_str)
            part_str = g_strdup(buf);

        if (partinfo->mime_type == MIME_TEXT ||
            partinfo->mime_type == MIME_TEXT_HTML) {
            if (!partinfo->main &&
                partinfo->parent &&
                partinfo->parent->children != partinfo) {
                fputs(part_str, destfp);
            }

            if ((tmpfp = procmime_get_text_content(partinfo, fp, encoding)) == NULL) {
                g_free(part_str);
                break;
            }
            while (fgets(buf, BUFFSIZE * sizeof (gchar), tmpfp) != NULL)
                fputs(buf, destfp);

            fclose(tmpfp);
        } else if (partinfo->mime_type == MIME_MESSAGE_RFC822) {
            fputs(part_str, destfp);
            while (fgets(buf, BUFFSIZE * sizeof (gchar), fp) != NULL)
                if (buf[0] == '\r' || buf[0] == '\n') break;
            procmsg_write_headers(msginfo, partinfo, fp, destfp, encoding, all_headers);
            fputc('\n', destfp);
        } else if (partinfo->mime_type != MIME_MULTIPART) {
            fputs(part_str, destfp);
        }

        g_free(part_str);

        if (partinfo->parent && partinfo->parent->content_type &&
            !g_ascii_strcasecmp(partinfo->parent->content_type,
                    "multipart/alternative"))
            partinfo = partinfo->parent->next;
        else
            partinfo = procmime_mimeinfo_next(partinfo);
    }

    free(buf);
    if (fclose(destfp) == EOF) {
        FILE_OP_ERROR(dest, "fclose");
        g_unlink(dest);
        ret = -1;
    }

    fclose(fp);
    procmime_mimeinfo_free_all(mimeinfo);

    return ret;
}

/**
 * procmsg_concat_partial_messages:
 * @mlist: list of MsgInfo* including message/partial messages.
 * @file: output file name of concatenated message.
 *
 * Concatenate @mlist which consists of message/partial messages and
 * output to @file. If @mlist has different partial id, the first one
 * is used.
 *
 * Return value: 0 on success, or -1 if failed.
 **/
gint procmsg_concat_partial_messages(GSList *mlist, const gchar *file)
{
    static HeaderEntry hentry[] = {{"Content-Type:", NULL, FALSE},
                       {NULL, NULL, FALSE}};
    FILE *fp;
    FILE *tmp_fp;
    gchar *part_id = NULL;
    gint total = 0;
    MsgInfo *msg_array[100] = {NULL};
    MsgInfo *msginfo;
    MimeInfo *mimeinfo;
    GSList *cur;
    gint i;

    g_return_val_if_fail(mlist != NULL, -1);
    g_return_val_if_fail(file != NULL, -1);

    debug_print("procmsg_concat_partial_messages\n");
    gchar *buf = (gchar *)malloc(BUFFSIZE * sizeof (gchar));
    memset(buf, 0, BUFFSIZE * sizeof (gchar));

    for (cur = mlist; cur != NULL; cur = cur->next) {
        gint n = 0;
        gint t = 0;
        gchar *cur_id = NULL;

        msginfo = (MsgInfo *)cur->data;

        fp = procmsg_open_message_decrypted(msginfo, &mimeinfo);
        if (!fp)
            continue;
        if (!mimeinfo->content_type ||
            g_ascii_strcasecmp(mimeinfo->content_type, "message/partial") != 0)
            goto skip;

        rewind(fp);
        if (procheader_get_one_field(buf, BUFFSIZE * sizeof (gchar), fp, hentry) == -1)
            goto skip;

        procmime_scan_content_type_partial(buf + strlen(hentry[0].name),
                           &t, &cur_id, &n);
        if (n == 0 || n > 100 || t > 100 || (t > 0 && n > t)) {
            debug_print("bad partial number (%d/%d), skip\n", n, t);
            g_free(cur_id);
            goto skip;
        }

        debug_print("partial: %d/%d id=%s\n", n, t, cur_id);
        if (!part_id)
            part_id = g_strdup(cur_id);
        if (total == 0)
            total = t;

        if ((t > 0 && total != t) || (total > 0 && n > total) ||
            strcmp(part_id, cur_id) != 0) {
            debug_print("skip\n");
            g_free(cur_id);
            goto skip;
        }

        msg_array[n - 1] = msginfo;

        g_free(cur_id);
skip:
        procmime_mimeinfo_free_all(mimeinfo);
        fclose(fp);
    }

    if (!part_id) {
        debug_print("piece not found\n");
        free(buf);
        return -1;
    }

    debug_print("part_id = %s , total = %d\n", part_id, total);
    g_free(part_id);

    if (total == 0) {
        debug_print("total number not found\n");
        free(buf);
        return -1;
    }

    /* check if all pieces exist */
    for (i = 0; i < total; i++) {
        if (msg_array[i] == NULL) {
            debug_print("message part %d not exist\n", i + 1);
            free(buf);
            return -1;
        }
    }

    /* concatenate parts */
    if ((tmp_fp = g_fopen(file, "wb")) == NULL) {
        FILE_OP_ERROR(file, "fopen");
        return -1;
    }

    for (i = 0; i < total; i++) {
        msginfo = msg_array[i];
        off_t out_size;
        gint empty_line_size = 0;

        fp = procmsg_open_message_decrypted(msginfo, &mimeinfo);
        if (!fp) {
            g_warning("cannot open message part %d\n", i + 1);
            fclose(tmp_fp);
            g_unlink(file);
            free(buf);
            return -1;
        }

        /* write out first headers */
        if (i == 0) {
            rewind(fp);
            while (procheader_get_one_field(buf, BUFFSIZE * sizeof (gchar), fp, NULL) != -1) {
                if (!g_ascii_strncasecmp(buf, "Content-", 8) ||
                    !g_ascii_strncasecmp(buf, "Subject", 7) ||
                    !g_ascii_strncasecmp(buf, "Message-ID", 10) ||
                    !g_ascii_strncasecmp(buf, "Encrypted", 9) ||
                    !g_ascii_strncasecmp(buf, "MIME-Version", 12))
                    continue;
                fputs(buf, tmp_fp);
                fputs("\n", tmp_fp);
            }

            while (procheader_get_one_field(buf, BUFFSIZE * sizeof (gchar), fp, NULL) != -1) {
                if (!g_ascii_strncasecmp(buf, "Content-", 8) ||
                    !g_ascii_strncasecmp(buf, "Subject", 7) ||
                    !g_ascii_strncasecmp(buf, "Message-ID", 10) ||
                    !g_ascii_strncasecmp(buf, "Encrypted", 9) ||
                    !g_ascii_strncasecmp(buf, "MIME-Version", 12)) {
                    fputs(buf, tmp_fp);
                    fputs("\n", tmp_fp);
                }
            }

            /* header-body separator */
            fputs("\n", tmp_fp);
        }

        out_size = get_left_file_size(fp);
        if (out_size < 0) {
            g_warning("cannot tell left file size of part %d\n", i + 1);
            procmime_mimeinfo_free_all(mimeinfo);
            fclose(fp);
            fclose(tmp_fp);
            g_unlink(file);
            free(buf);
            return -1;
        }
        empty_line_size = get_last_empty_line_size(fp, out_size);
        if (empty_line_size < 0) {
            g_warning("cannot get last empty line size of part %d\n", i + 1);
            procmime_mimeinfo_free_all(mimeinfo);
            fclose(fp);
            fclose(tmp_fp);
            g_unlink(file);
            free(buf);
            return -1;
        }

        if (append_file_part(fp, ftell(fp), out_size - empty_line_size,
                     tmp_fp) < 0) {
            g_warning("write failed\n");
            procmime_mimeinfo_free_all(mimeinfo);
            fclose(fp);
            fclose(tmp_fp);
            g_unlink(file);
            free(buf);
            return -1;
        }

        procmime_mimeinfo_free_all(mimeinfo);
        fclose(fp);
    }
    free(buf);

    fclose(tmp_fp);

    return 0;
}

static gboolean procmsg_get_flags(FolderItem *item, gint num,
                  MsgPermFlags *flags)
{
    FILE *fp;
    guint32 idata;
    gint read_num;
    MsgPermFlags perm_flags;
    gboolean found = FALSE;
    GSList *cur;

    g_return_val_if_fail(item != NULL, FALSE);
    g_return_val_if_fail(flags != NULL, FALSE);

    if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL)
        return FALSE;

    while (fread(&idata, sizeof(idata), 1, fp) == 1) {
        read_num = idata;
        if (fread(&idata, sizeof(idata), 1, fp) != 1)
            break;
        perm_flags = idata;
        if (read_num == num) {
            *flags = perm_flags;
            found = TRUE;
            break;
        }
    }

    fclose(fp);
    if (found)
        return TRUE;

    for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
        MsgFlagInfo *flaginfo = (MsgFlagInfo *)cur->data;

        if (flaginfo->msgnum == num) {
            *flags = flaginfo->flags.perm_flags;
            found = TRUE;
            break;
        }
    }

    return found;
}

MsgInfo *procmsg_get_msginfo(FolderItem *item, gint num)
{
    MsgInfo *msginfo;
    FolderType type;

    g_return_val_if_fail(item->folder != NULL, NULL);

    msginfo = folder_item_get_msginfo(item, num);
    if (!msginfo)
        return NULL;

    type = FOLDER_TYPE(item->folder);
    if (type == F_MH || type == F_IMAP) {
        if (item->stype == F_QUEUE) {
            MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
        } else if (item->stype == F_DRAFT) {
            MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
        }
    }
    if (type == F_IMAP) {
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
    } else if (type == F_NEWS) {
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
    }

    if (type == F_MH || type == F_NEWS) {
        MsgPermFlags flags = 0;
        if (procmsg_get_flags(item, num, &flags))
            msginfo->flags.perm_flags = flags;
    }

    return msginfo;
}

MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
{
    MsgInfo *newmsginfo;

    g_return_val_if_fail(msginfo != NULL, NULL);

    newmsginfo = g_new0(MsgInfo, 1);
//    newmsginfo->references = NULL;
//    newmsginfo->receivedtime = 0;
//    newmsginfo->encinfo = NULL;
//    newmsginfo->xface = NULL;
//    newmsginfo->fromname = NULL;
//    newmsginfo->date = NULL;
//    newmsginfo->from = NULL;
//    newmsginfo->to = NULL;
//    newmsginfo->cc = NULL;
//    newmsginfo->newsgroups = NULL;
//    newmsginfo->subject = NULL;
//    newmsginfo->msgid = NULL;
//    newmsginfo->inreplyto = NULL;
//    newmsginfo->xpriority = NULL;
//    newmsginfo->disposition_notification_to = NULL;
//    newmsginfo->separatesend = NULL;
//    newmsginfo->datetimesend = NULL;
//    newmsginfo->sendresultflags = NULL;
//    newmsginfo->references = NULL;
//    newmsginfo->file_path = NULL;

#define MEMBCOPY(mmb)	newmsginfo->mmb = msginfo->mmb
#define MEMBDUP(mmb)	newmsginfo->mmb = msginfo->mmb ? \
            g_strdup(msginfo->mmb) : NULL

    MEMBCOPY(msgnum);
    MEMBCOPY(size);
    MEMBCOPY(mtime);
    MEMBCOPY(date_t);
    MEMBCOPY(receivedtime);
    MEMBCOPY(attachmentnum);
    MEMBCOPY(fetch_large);

    MEMBCOPY(flags);

    MEMBDUP(fromname);

    MEMBDUP(separatesend);
    MEMBDUP(datetimesend);
    MEMBDUP(sendresultflags);

    MEMBDUP(date);
    MEMBDUP(from);
    MEMBDUP(to);
    MEMBDUP(cc);
    MEMBDUP(bcc);
    MEMBDUP(disposition_notification_to);
    MEMBDUP(xpriority);
    MEMBDUP(uos_msgid);
    MEMBDUP(newsgroups);
    MEMBDUP(subject);
    MEMBDUP(msgid);
    MEMBDUP(inreplyto);

    MEMBCOPY(folder);
    MEMBCOPY(to_folder);

    MEMBDUP(xface);

    MEMBDUP(file_path);

    newmsginfo->attachmentnum=msginfo->attachmentnum;
    newmsginfo->calendarflag = msginfo->calendarflag;

    if (msginfo->encinfo) {
        newmsginfo->encinfo = g_new0(MsgEncryptInfo, 1);
        MEMBDUP(encinfo->plaintext_file);
        MEMBDUP(encinfo->sigstatus);
        MEMBDUP(encinfo->sigstatus_full);
        MEMBCOPY(encinfo->decryption_failed);
    }

    return newmsginfo;
}

MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
{
    MsgInfo *full_msginfo;
    gchar *file;

    g_return_val_if_fail(msginfo != NULL, NULL);

    file = procmsg_get_message_file(msginfo);
    if (!file) {
        g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
        return NULL;
    }

    full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE);
    g_free(file);
    if (!full_msginfo) return NULL;

    full_msginfo->msgnum = msginfo->msgnum;
    full_msginfo->size = msginfo->size;
    full_msginfo->mtime = msginfo->mtime;
    full_msginfo->folder = msginfo->folder;
    full_msginfo->to_folder = msginfo->to_folder;

    full_msginfo->file_path = g_strdup(msginfo->file_path);

    if (msginfo->encinfo) {
        full_msginfo->encinfo = g_new0(MsgEncryptInfo, 1);
        full_msginfo->encinfo->plaintext_file =
            g_strdup(msginfo->encinfo->plaintext_file);
        full_msginfo->encinfo->sigstatus =
            g_strdup(msginfo->encinfo->sigstatus);
        full_msginfo->encinfo->sigstatus_full =
            g_strdup(msginfo->encinfo->sigstatus_full);
        full_msginfo->encinfo->decryption_failed =
            msginfo->encinfo->decryption_failed;
    }

    return full_msginfo;
}

gboolean procmsg_msginfo_equal(MsgInfo *msginfo_a, MsgInfo *msginfo_b)
{
    if (!msginfo_a || !msginfo_b)
        return FALSE;

    if (msginfo_a == msginfo_b)
        return TRUE;

    if (msginfo_a->folder == msginfo_b->folder &&
        msginfo_a->msgnum == msginfo_b->msgnum &&
        msginfo_a->size   == msginfo_b->size   &&
        msginfo_a->mtime  == msginfo_b->mtime)
        return TRUE;

    return FALSE;
}

void procmsg_msginfo_free(MsgInfo *msginfo)
{
    g_return_if_fail(msginfo != NULL);

    g_free(msginfo->xface);

    g_free(msginfo->fromname);

    g_free(msginfo->date);
    g_free(msginfo->from);
    g_free(msginfo->to);
    g_free(msginfo->cc);
    g_free(msginfo->bcc);
    g_free(msginfo->newsgroups);
    g_free(msginfo->subject);
    g_free(msginfo->msgid);
    g_free(msginfo->inreplyto);
    g_free(msginfo->xpriority);
    g_free(msginfo->uos_msgid);
    g_free(msginfo->disposition_notification_to);
    g_free(msginfo->separatesend);
    g_free(msginfo->datetimesend);
    g_free(msginfo->sendresultflags);

    slist_free_strings(msginfo->references);
    g_slist_free(msginfo->references);

    g_free(msginfo->file_path);

    if (msginfo->encinfo) {
        g_free(msginfo->encinfo->plaintext_file);
        g_free(msginfo->encinfo->sigstatus);
        g_free(msginfo->encinfo->sigstatus_full);
        g_free(msginfo->encinfo);
    }

    g_free(msginfo);
}

gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
{
    const MsgInfo *msginfo1 = a;
    const MsgInfo *msginfo2 = b;

    if (!msginfo1 || !msginfo2)
        return 0;

    return msginfo1->msgnum - msginfo2->msgnum;
}

#define CMP_FUNC_DEF(func_name, val)					\
static gint func_name(gconstpointer a, gconstpointer b)			\
{									\
    const MsgInfo *msginfo1 = a;					\
    const MsgInfo *msginfo2 = b;					\
    gint ret;							\
                                    \
    if (!msginfo1 || !msginfo2)					\
        return 0;						\
                                    \
    ret = (val);							\
    if (ret == 0)							\
        ret = msginfo1->date_t - msginfo2->date_t;		\
                                    \
    return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);	\
}

CMP_FUNC_DEF(procmsg_cmp_by_mark,
         MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
CMP_FUNC_DEF(procmsg_cmp_by_unread,
         MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
CMP_FUNC_DEF(procmsg_cmp_by_mime,
         MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags))
CMP_FUNC_DEF(procmsg_cmp_by_label,
         MSG_GET_COLORLABEL(msginfo1->flags) -
         MSG_GET_COLORLABEL(msginfo2->flags))
CMP_FUNC_DEF(procmsg_cmp_by_size, msginfo1->size - msginfo2->size)

#undef CMP_FUNC_DEF
#define CMP_FUNC_DEF(func_name, val)					\
static gint func_name(gconstpointer a, gconstpointer b)			\
{									\
    const MsgInfo *msginfo1 = a;					\
    const MsgInfo *msginfo2 = b;					\
                                    \
    if (!msginfo1 || !msginfo2)					\
        return 0;						\
                                    \
    return (val) * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);	\
}

CMP_FUNC_DEF(procmsg_cmp_by_number, msginfo1->msgnum - msginfo2->msgnum)
CMP_FUNC_DEF(procmsg_cmp_by_date, msginfo1->date_t - msginfo2->date_t)

#undef CMP_FUNC_DEF
#define CMP_FUNC_DEF(func_name, var_name)				\
static gint func_name(gconstpointer a, gconstpointer b)			\
{									\
    const MsgInfo *msginfo1 = a;					\
    const MsgInfo *msginfo2 = b;					\
    gint ret;							\
                                    \
    if (!msginfo1->var_name)					\
        return (msginfo2->var_name != NULL) *			\
            (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);\
    if (!msginfo2->var_name)					\
        return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);	\
                                    \
    ret = g_ascii_strcasecmp					\
        (msginfo1->var_name, msginfo2->var_name);		\
    if (ret == 0)							\
        ret = msginfo1->date_t - msginfo2->date_t;		\
                                    \
    return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);	\
}

CMP_FUNC_DEF(procmsg_cmp_by_from, fromname)
CMP_FUNC_DEF(procmsg_cmp_by_to, to)

#undef CMP_FUNC_DEF

static gint procmsg_cmp_by_subject(gconstpointer a, gconstpointer b)
{
    const MsgInfo *msginfo1 = a;
    const MsgInfo *msginfo2 = b;
    gint ret;

    if (!msginfo1->subject)
        return (msginfo2->subject != NULL) *
            (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);
    if (!msginfo2->subject)
        return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);

    ret = subject_compare_for_sort(msginfo1->subject, msginfo2->subject);
    if (ret == 0)
        ret = msginfo1->date_t - msginfo2->date_t;

    return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);
}

//读写 .backup_cache file 所需要的文件

static GMappedFile *procmsg_open_backup_cache_file_mmap(FolderItem *item,
                         DataOpenMode mode)
{
    gchar *cachefile;
    GMappedFile *map = NULL;
    GError *error = NULL;
    gsize size;
    guint32 data_ver = 0;
    gchar *p;

    if (mode != DATA_READ)
        return NULL;

    cachefile = folder_item_get_backup_file(item, Backup_Cache);
    if (cachefile) {
        map = g_mapped_file_new(cachefile, FALSE, &error);
        if (!map) {
            if (error && error->code == G_FILE_ERROR_NOENT)
                debug_print("%s: mark/cache file not found\n", cachefile);
            else if (error)
                g_warning("%s: cannot open mark/cache file: %s", cachefile, error->message);
            else
                g_warning("%s: cannot open mark/cache file", cachefile);
            if (error)
                g_error_free(error);
            g_free(cachefile);
            return NULL;
        }
        size = g_mapped_file_get_length(map);
        if (size < sizeof(data_ver)) {
            g_warning("%s: cannot read mark/cache file (truncated?)", cachefile);
            g_mapped_file_free(map);
            g_free(cachefile);
            return NULL;
        }
        p = g_mapped_file_get_contents(map);
        data_ver = *(guint32 *)p;
        if (CACHE_VERSION != data_ver) {
            g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
                  cachefile, data_ver, CACHE_VERSION);
            g_mapped_file_free(map);
            g_free(cachefile);
            return NULL;
        }
        g_free(cachefile);
    }

    return map;
}
// shangxiudong 供函数:procmsg_read_backup_mark_file调用
static FILE *procmsg_open_backup_mark_file(FolderItem *item, DataOpenMode mode)
{
    gchar *markfile;
    FILE *fp;

    markfile = folder_item_get_backup_file(item, Backup_Mark);
//    g_print("------procmsg_open_mark_file markfile:%s\n",markfile);
    fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0);
    g_free(markfile);

    return fp;
}
// shangxiudong 供函数:procmsg_set_backup_flags调用
static GHashTable *procmsg_read_backup_mark_file(FolderItem *item)
{
    FILE *fp;
    GHashTable *mark_table = NULL;
    guint32 idata;
    guint num;
    MsgFlags *flags;
    MsgPermFlags perm_flags;
    GSList *cur;

    if ((fp = procmsg_open_backup_mark_file(item, DATA_READ)) == NULL)
        return NULL;

    mark_table = g_hash_table_new(NULL, g_direct_equal);

    while (fread(&idata, sizeof(idata), 1, fp) == 1) {
        num = idata;
        if (fread(&idata, sizeof(idata), 1, fp) != 1) break;
        perm_flags = idata;

        flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num));
        if (flags != NULL)
            g_free(flags);

        flags = g_new0(MsgFlags, 1);
        flags->perm_flags = perm_flags;

        g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
    }

    fclose(fp);

    if (item->mark_queue) {
        g_hash_table_foreach(mark_table, mark_unset_new_func, NULL);
        item->mark_dirty = TRUE;
    }

    for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
        MsgFlagInfo *flaginfo = (MsgFlagInfo *)cur->data;

        flags = g_hash_table_lookup(mark_table,
                        GUINT_TO_POINTER(flaginfo->msgnum));
        if (flags != NULL)
            g_free(flags);

        flags = g_new0(MsgFlags, 1);
        flags->perm_flags = flaginfo->flags.perm_flags;

        g_hash_table_insert(mark_table,
                    GUINT_TO_POINTER(flaginfo->msgnum), flags);

    }

    if (item->mark_queue && !item->opened) {
        procmsg_write_mark_file(item, mark_table);
        procmsg_flaginfo_list_free(item->mark_queue);
        item->mark_queue = NULL;
        item->mark_dirty = FALSE;
    }

    return mark_table;
}
//初次读取时，用于恢复上次内存中的邮件列表标记信息，只用在:Libsylph::getMailList()函数中
void procmsg_set_backup_flags(GSList *mlist, FolderItem *item)
{
    GSList *cur;
    gint new = 0, unread = 0, total = 0;
    gint lastnum = 0;
    gint unflagged = 0;
    gboolean mark_queue_exist;
    MsgInfo *msginfo;
    GHashTable *mark_table;
    MsgFlags *flags;

    g_return_if_fail(item != NULL);
    g_return_if_fail(item->folder != NULL);

//    debug_print("Marking the messages...\n");

    mark_queue_exist = (item->mark_queue != NULL);
    mark_table = procmsg_read_backup_mark_file(item);
    if (!mark_table) {
        item->new1 = item->unread = item->total = g_slist_length(mlist);
        item->updated = TRUE;
        item->mark_dirty = TRUE;
        return;
    }

    /* unset new flags if new (unflagged) messages exist */
    if (!mark_queue_exist) {
        for (cur = mlist; cur != NULL; cur = cur->next) {
            msginfo = (MsgInfo *)cur->data;
            flags = g_hash_table_lookup
                (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
            if (!flags) {
                g_hash_table_foreach(mark_table,
                             mark_unset_new_func, NULL);
                item->mark_dirty = TRUE;
                break;
            }
        }
    }

    for (cur = mlist; cur != NULL; cur = cur->next) {
        msginfo = (MsgInfo *)cur->data;

        if (lastnum < msginfo->msgnum)
            lastnum = msginfo->msgnum;

        flags = g_hash_table_lookup
            (mark_table, GUINT_TO_POINTER(msginfo->msgnum));

        if (flags != NULL) {
            /* add the permanent flags only */
            msginfo->flags.perm_flags = flags->perm_flags;
            if (MSG_IS_NEW(*flags))
                ++new;
            if (MSG_IS_UNREAD(*flags))
                ++unread;
            if (FOLDER_TYPE(item->folder) == F_IMAP) {
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
            } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
            }
        } else {
            ++unflagged;
            ++new;
            ++unread;
        }

        ++total;
    }

    item->new1 = new;
    item->unread = unread;
    item->total = total;
    item->unmarked_num = unflagged;
    item->last_num = lastnum;
    item->updated = TRUE;

    if (unflagged > 0)
        item->mark_dirty = TRUE;

    debug_print("new: %d unread: %d unflagged: %d total: %d\n",
            new, unread, unflagged, total);

    hash_free_value_mem(mark_table);
    g_hash_table_destroy(mark_table);
}

//初次读取时，用于恢复上次内存中的邮件列表标记信息，只用在:Libsylph::getMailList()函数中
GSList *procmsg_read_backup_cache(FolderItem *item, gboolean scan_file)
{
    GSList *mlist = NULL;
    GSList *last = NULL;
    GMappedFile *mapfile;
    const gchar *filep;
    gsize file_len;
    const gchar *p, *endp;
    MsgInfo *msginfo;
    MsgFlags default_flags;
    guint32 num;
    guint refnum;
    FolderType type;

    g_return_val_if_fail(item != NULL, NULL);
    g_return_val_if_fail(item->folder != NULL, NULL);
    type = FOLDER_TYPE(item->folder);

    default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
    default_flags.tmp_flags = 0;
    if (type == F_MH ||type == F_POP || type == F_IMAP) {
        if (item->stype == F_QUEUE) {
            MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
        } else if (item->stype == F_DRAFT) {
            MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
        }
    }
    if (type == F_IMAP) {
        MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
    } else if (type == F_NEWS) {
        MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
    }

    if (type == F_MH||type == F_POP) {
        gchar *path;

        path = folder_item_get_path(item);
        if (change_dir(path) < 0) {
            g_free(path);
            return NULL;
        }
        g_free(path);
    }

    mapfile = procmsg_open_backup_cache_file_mmap(item, DATA_READ);
    if (!mapfile) {
        item->cache_dirty = TRUE;
        return NULL;
    }

    debug_print("Reading summary cache...\n");

    filep = g_mapped_file_get_contents(mapfile);

    file_len = g_mapped_file_get_length(mapfile);
    endp = filep + file_len;
    p = filep + sizeof(guint32); /* version */

    while (endp - p >= sizeof(num)) {
        msginfo = g_new0(MsgInfo, 1);

        READ_CACHE_DATA_INT(msginfo->msgnum);
        if(103==msginfo->msgnum){
           int a=0;
        }
        READ_CACHE_DATA_INT(msginfo->size);
        READ_CACHE_DATA_INT(msginfo->mtime);
        READ_CACHE_DATA_INT(msginfo->date_t);
        READ_CACHE_DATA_INT(msginfo->receivedtime);

        READ_CACHE_DATA_INT(msginfo->flags.tmp_flags);

        READ_CACHE_DATA_INT(msginfo->attachmentnum);//add shui

        gint reserveint=0;
        READ_CACHE_DATA_INT(msginfo->calendarflag);
        READ_CACHE_DATA_INT(reserveint);//add shui 备用
        READ_CACHE_DATA_INT(reserveint);//add shui 备用


        READ_CACHE_DATA(msginfo->fromname);

        READ_CACHE_DATA(msginfo->date);
        READ_CACHE_DATA(msginfo->from);
        READ_CACHE_DATA(msginfo->to);
        READ_CACHE_DATA(msginfo->newsgroups);
        READ_CACHE_DATA(msginfo->subject);
        READ_CACHE_DATA(msginfo->msgid);
        READ_CACHE_DATA(msginfo->inreplyto);
        READ_CACHE_DATA(msginfo->xpriority);
        READ_CACHE_DATA(msginfo->sendresultflags);
        READ_CACHE_DATA(msginfo->cc);
        READ_CACHE_DATA(msginfo->uos_msgid);
        READ_CACHE_DATA(msginfo->disposition_notification_to);

        READ_CACHE_DATA(msginfo->datetimesend);//add shang 定时发送字段
        READ_CACHE_DATA(msginfo->file_path);//add shang 数据库操作记录
        gchar *reservechar=NULL;
        READ_CACHE_DATA(reservechar);//add shui 备用
        if(reservechar) {
            g_free(reservechar);
            reservechar = NULL;
        }
        READ_CACHE_DATA_INT(refnum);
        for (; refnum != 0; refnum--) {
            gchar *ref;

            READ_CACHE_DATA(ref);
            msginfo->references =
                g_slist_prepend(msginfo->references, ref);
        }
        if (msginfo->references)
            msginfo->references =
                g_slist_reverse(msginfo->references);

        MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
        MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);

        /* if the message file doesn't exist or is changed,
           don't add the data */
        if (((type == F_MH||type == F_POP)&& scan_file &&
             folder_item_is_msg_changed(item, msginfo)) ||
             msginfo->msgnum == 0) {
            procmsg_msginfo_free(msginfo);
            item->cache_dirty = TRUE;
        } else {
            msginfo->folder = item;

            if (!mlist)
                last = mlist = g_slist_append(NULL, msginfo);
            else {
                last = g_slist_append(last, msginfo);
                last = last->next;
            }
        }
    }

    g_mapped_file_free(mapfile);

    if (item->cache_queue) {
        GSList *qlist;
        qlist = procmsg_read_cache_queue(item, scan_file);
        mlist = g_slist_concat(mlist, qlist);
    }

    return mlist;
}

void procmsg_write_backup_cache(MsgInfo *msginfo, FILE *fp)
{
    g_return_if_fail(msginfo != NULL);
    g_return_if_fail(fp != NULL);

    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);
    WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
    WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
    WRITE_CACHE_DATA_INT(msginfo->receivedtime, fp);
    WRITE_CACHE_DATA_INT(flags, fp);
    WRITE_CACHE_DATA_INT(msginfo->attachmentnum, fp);//add shui
    WRITE_CACHE_DATA_INT(msginfo->calendarflag, fp);
    WRITE_CACHE_DATA_INT(0, fp);//add shui 备用
    WRITE_CACHE_DATA_INT(0, fp);//add shui 备用

//    if(msginfo->attachmentnum>0){
//        g_print("write attachmentnum:%d\n",msginfo->attachmentnum);
//    }

    WRITE_CACHE_DATA(msginfo->fromname, fp);

    WRITE_CACHE_DATA(msginfo->date, fp);
    WRITE_CACHE_DATA(msginfo->from, fp);
    WRITE_CACHE_DATA(msginfo->to, fp);
    WRITE_CACHE_DATA(msginfo->newsgroups, fp);
    WRITE_CACHE_DATA(msginfo->subject, fp);
    WRITE_CACHE_DATA(msginfo->msgid, fp);
    WRITE_CACHE_DATA(msginfo->inreplyto, fp);
    WRITE_CACHE_DATA(msginfo->xpriority, fp);
    if(!msginfo->sendresultflags)
        msginfo->sendresultflags=g_strdup("1");
    WRITE_CACHE_DATA(msginfo->sendresultflags, fp);
    WRITE_CACHE_DATA(msginfo->cc, fp);
//    if(!msginfo->uos_msgid)
//        msginfo->uos_msgid=g_strdup("0");
    WRITE_CACHE_DATA(msginfo->uos_msgid, fp);
    WRITE_CACHE_DATA(msginfo->disposition_notification_to, fp);
    WRITE_CACHE_DATA(msginfo->datetimesend, fp);//add shang 定时发送字段
    WRITE_CACHE_DATA(msginfo->file_path, fp);//add shang 数据库操作记录
    WRITE_CACHE_DATA(NULL, fp);//add shui 备用

    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);
    }
}

void procmsg_write_backup_flags(MsgInfo *msginfo, FILE *fp)
{
    g_return_if_fail(msginfo != NULL);
    g_return_if_fail(fp != NULL);

    MsgPermFlags flags = msginfo->flags.perm_flags;

    WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
    WRITE_CACHE_DATA_INT(flags, fp);
}

#undef READ_CACHE_DATA
#undef READ_CACHE_DATA_INT

FILE *procmsg_open_backup_file(FolderItem *item, guint version, DataOpenMode mode, int backup_type)
{
    gchar *cachefile;
    FILE *fp;

    cachefile = folder_item_get_backup_file(item, backup_type);
//    g_print("--------procmsg_open_cache_file:%s\n",cachefile);
    fp = procmsg_open_data_file(cachefile, version, mode, NULL, 0);
    g_free(cachefile);

    return fp;
}

void mark_file_thread_mutext_trylock()
{
    pthread_mutex_trylock(&g_mutex_lock_mark_file);
}

void mark_file_thread_mutext_unlock()
{
    pthread_mutex_unlock(&g_mutex_lock_mark_file);
}

void mark_file_thread_mutext_destroy()
{
    pthread_mutex_destroy(&g_mutex_lock_mark_file);
}
