Statistics
| Revision:

root / libsylph / procmsg.c @ 3245

History | View | Annotate | Download (56 KB)

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

    
20
#include "defs.h"
21

    
22
#include <glib.h>
23
#include <glib/gi18n.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <errno.h>
27

    
28
#include "utils.h"
29
#include "procmsg.h"
30
#include "procheader.h"
31
#include "account.h"
32
#include "procmime.h"
33
#include "prefs_common.h"
34
#include "folder.h"
35
#include "codeconv.h"
36

    
37
typedef struct _MsgFlagInfo {
38
        guint msgnum;
39
        MsgFlags flags;
40
} MsgFlagInfo;
41

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

    
45
static void mark_sum_func                        (gpointer         key,
46
                                                 gpointer         value,
47
                                                 gpointer         data);
48

    
49
static GHashTable *procmsg_read_mark_file        (FolderItem        *item);
50
static void procmsg_write_mark_file                (FolderItem        *item,
51
                                                 GHashTable        *mark_table);
52

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

    
56
static gint procmsg_cmp_by_mark                        (gconstpointer         a,
57
                                                 gconstpointer         b);
58
static gint procmsg_cmp_by_unread                (gconstpointer         a,
59
                                                 gconstpointer         b);
60
static gint procmsg_cmp_by_mime                        (gconstpointer         a,
61
                                                 gconstpointer         b);
62
static gint procmsg_cmp_by_label                (gconstpointer         a,
63
                                                 gconstpointer         b);
64
static gint procmsg_cmp_by_number                (gconstpointer         a,
65
                                                 gconstpointer         b);
66
static gint procmsg_cmp_by_size                        (gconstpointer         a,
67
                                                 gconstpointer         b);
68
static gint procmsg_cmp_by_date                        (gconstpointer         a,
69
                                                 gconstpointer         b);
70
static gint procmsg_cmp_by_from                        (gconstpointer         a,
71
                                                 gconstpointer         b);
72
static gint procmsg_cmp_by_to                        (gconstpointer         a,
73
                                                 gconstpointer         b);
74
static gint procmsg_cmp_by_subject                (gconstpointer         a,
75
                                                 gconstpointer         b);
76

    
77

    
78
GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
79
{
80
        GHashTable *msg_table;
81

    
82
        if (mlist == NULL) return NULL;
83

    
84
        msg_table = g_hash_table_new(NULL, g_direct_equal);
85
        procmsg_msg_hash_table_append(msg_table, mlist);
86

    
87
        return msg_table;
88
}
89

    
90
void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
91
{
92
        GSList *cur;
93
        MsgInfo *msginfo;
94

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

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

    
100
                g_hash_table_insert(msg_table,
101
                                    GUINT_TO_POINTER(msginfo->msgnum),
102
                                    msginfo);
103
        }
104
}
105

    
106
GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
107
{
108
        GHashTable *msg_table;
109
        GSList *cur;
110
        MsgInfo *msginfo;
111

    
112
        if (mlist == NULL) return NULL;
113

    
114
        msg_table = g_hash_table_new(NULL, g_direct_equal);
115

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

    
121
        return msg_table;
122
}
123

    
124
gint procmsg_read_cache_data_str(FILE *fp, gchar **str)
125
{
126
        gchar buf[BUFFSIZE];
127
        guint32 len;
128
        gchar *tmp = NULL;
129

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

    
133
        if (len > G_MAXINT)
134
                return -1;
135

    
136
        while (len > 0) {
137
                size_t size = MIN(len, BUFFSIZE - 1);
138

    
139
                if (fread(buf, size, 1, fp) != 1) {
140
                        if (tmp)
141
                                g_free(tmp);
142
                        *str = NULL;
143
                        return -1;
144
                }
145

    
146
                buf[size] = '\0';
147
                if (tmp) {
148
                        *str = g_strconcat(tmp, buf, NULL);
149
                        g_free(tmp);
150
                        tmp = *str;
151
                } else
152
                        tmp = *str = g_strdup(buf);
153

    
154
                len -= size;
155
        }
156

    
157
        return 0;
158
}
159

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

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

    
167
        len = *(const guint32 *)(*p);
168
        *p += sizeof(len);
169
        if (len > G_MAXINT || len > endp - *p)
170
                return -1;
171

    
172
        if (len > 0) {
173
                *str = g_strndup(*p, len);
174
                *p += len;
175
        }
176

    
177
        return 0;
178
}
179

    
180
#define READ_CACHE_DATA(data)                                                \
181
{                                                                        \
182
        if (procmsg_read_cache_data_str_mem(&p, endp, &data) < 0) {        \
183
                g_warning("Cache data is corrupted\n");                        \
184
                procmsg_msginfo_free(msginfo);                                \
185
                procmsg_msg_list_free(mlist);                                \
186
                g_mapped_file_free(mapfile);                                \
187
                return NULL;                                                \
188
        }                                                                \
189
}
190

    
191
#define READ_CACHE_DATA_INT(n)                                        \
192
{                                                                \
193
        if (endp - p < sizeof(guint32)) {                        \
194
                g_warning("Cache data is corrupted\n");                \
195
                procmsg_msginfo_free(msginfo);                        \
196
                procmsg_msg_list_free(mlist);                        \
197
                g_mapped_file_free(mapfile);                        \
198
                return NULL;                                        \
199
        } else {                                                \
200
                n = *(const guint32 *)p;                        \
201
                p += sizeof(guint32);                                \
202
        }                                                        \
203
}
204

    
205
GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file)
206
{
207
        GSList *mlist = NULL;
208
        GSList *last = NULL;
209
        GMappedFile *mapfile;
210
        const gchar *filep;
211
        gsize file_len;
212
        const gchar *p, *endp;
213
        MsgInfo *msginfo;
214
        MsgFlags default_flags;
215
        guint32 num;
216
        guint refnum;
217
        FolderType type;
218

    
219
        g_return_val_if_fail(item != NULL, NULL);
220
        g_return_val_if_fail(item->folder != NULL, NULL);
221
        type = FOLDER_TYPE(item->folder);
222

    
223
        default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
224
        default_flags.tmp_flags = 0;
225
        if (type == F_MH || type == F_IMAP) {
226
                if (item->stype == F_QUEUE) {
227
                        MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
228
                } else if (item->stype == F_DRAFT) {
229
                        MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
230
                }
231
        }
232
        if (type == F_IMAP) {
233
                MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
234
        } else if (type == F_NEWS) {
235
                MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
236
        }
237

    
238
        if (type == F_MH) {
239
                gchar *path;
240

    
241
                path = folder_item_get_path(item);
242
                if (change_dir(path) < 0) {
243
                        g_free(path);
244
                        return NULL;
245
                }
246
                g_free(path);
247
        }
248

    
249
        mapfile = procmsg_open_cache_file_mmap(item, DATA_READ);
250
        if (!mapfile) {
251
                item->cache_dirty = TRUE;
252
                return NULL;
253
        }
254

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

    
257
        filep = g_mapped_file_get_contents(mapfile);
258
        file_len = g_mapped_file_get_length(mapfile);
259
        endp = filep + file_len;
260
        p = filep + sizeof(guint32); /* version */
261

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

    
265
                READ_CACHE_DATA_INT(msginfo->msgnum);
266

    
267
                READ_CACHE_DATA_INT(msginfo->size);
268
                READ_CACHE_DATA_INT(msginfo->mtime);
269
                READ_CACHE_DATA_INT(msginfo->date_t);
270
                READ_CACHE_DATA_INT(msginfo->flags.tmp_flags);
271

    
272
                READ_CACHE_DATA(msginfo->fromname);
273

    
274
                READ_CACHE_DATA(msginfo->date);
275
                READ_CACHE_DATA(msginfo->from);
276
                READ_CACHE_DATA(msginfo->to);
277
                READ_CACHE_DATA(msginfo->newsgroups);
278
                READ_CACHE_DATA(msginfo->subject);
279
                READ_CACHE_DATA(msginfo->msgid);
280
                READ_CACHE_DATA(msginfo->inreplyto);
281

    
282
                READ_CACHE_DATA_INT(refnum);
283
                for (; refnum != 0; refnum--) {
284
                        gchar *ref;
285

    
286
                        READ_CACHE_DATA(ref);
287
                        msginfo->references =
288
                                g_slist_prepend(msginfo->references, ref);
289
                }
290
                if (msginfo->references)
291
                        msginfo->references =
292
                                g_slist_reverse(msginfo->references);
293

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

    
297
                /* if the message file doesn't exist or is changed,
298
                   don't add the data */
299
                if ((type == F_MH && scan_file &&
300
                     folder_item_is_msg_changed(item, msginfo)) ||
301
                     msginfo->msgnum == 0) {
302
                        procmsg_msginfo_free(msginfo);
303
                        item->cache_dirty = TRUE;
304
                } else {
305
                        msginfo->folder = item;
306

    
307
                        if (!mlist)
308
                                last = mlist = g_slist_append(NULL, msginfo);
309
                        else {
310
                                last = g_slist_append(last, msginfo);
311
                                last = last->next;
312
                        }
313
                }
314
        }
315

    
316
        g_mapped_file_free(mapfile);
317

    
318
        if (item->cache_queue) {
319
                GSList *qlist;
320
                qlist = procmsg_read_cache_queue(item, scan_file);
321
                mlist = g_slist_concat(mlist, qlist);
322
        }
323

    
324
        debug_print("done.\n");
325

    
326
        return mlist;
327
}
328

    
329
#undef READ_CACHE_DATA
330
#undef READ_CACHE_DATA_INT
331

    
332
static GSList *procmsg_read_cache_queue(FolderItem *item, gboolean scan_file)
333
{
334
        FolderType type;
335
        MsgInfo *msginfo;
336
        MsgFlags default_flags;
337
        GSList *cur;
338
        GSList *qlist = NULL;
339

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

    
343
        if (!item->cache_queue)
344
                return NULL;
345

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

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

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

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

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

    
361
                if ((type == F_MH && scan_file &&
362
                     folder_item_is_msg_changed(item, msginfo))) {
363
                        procmsg_msginfo_free(msginfo);
364
                        item->cache_dirty = TRUE;
365
                } else {
366
                        msginfo->folder = item;
367
                        qlist = g_slist_prepend(qlist, msginfo);
368
                }
369
        }
370

    
371
        g_slist_free(item->cache_queue);
372
        item->cache_queue = NULL;
373
        item->cache_dirty = TRUE;
374

    
375
        return qlist;
376
}
377

    
378
static void mark_unset_new_func(gpointer key, gpointer value, gpointer data)
379
{
380
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW);
381
}
382

    
383
void procmsg_set_flags(GSList *mlist, FolderItem *item)
384
{
385
        GSList *cur;
386
        gint new = 0, unread = 0, total = 0;
387
        gint lastnum = 0;
388
        gint unflagged = 0;
389
        gboolean mark_queue_exist;
390
        MsgInfo *msginfo;
391
        GHashTable *mark_table;
392
        MsgFlags *flags;
393

    
394
        g_return_if_fail(item != NULL);
395
        g_return_if_fail(item->folder != NULL);
396

    
397
        debug_print("Marking the messages...\n");
398

    
399
        mark_queue_exist = (item->mark_queue != NULL);
400
        mark_table = procmsg_read_mark_file(item);
401
        if (!mark_table) {
402
                item->new = item->unread = item->total = g_slist_length(mlist);
403
                item->updated = TRUE;
404
                item->mark_dirty = TRUE;
405
                return;
406
        }
407

    
408
        /* unset new flags if new (unflagged) messages exist */
409
        if (!mark_queue_exist) {
410
                for (cur = mlist; cur != NULL; cur = cur->next) {
411
                        msginfo = (MsgInfo *)cur->data;
412
                        flags = g_hash_table_lookup
413
                                (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
414
                        if (!flags) {
415
                                g_hash_table_foreach(mark_table,
416
                                                     mark_unset_new_func, NULL);
417
                                item->mark_dirty = TRUE;
418
                                break;
419
                        }
420
                }
421
        }
422

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

    
426
                if (lastnum < msginfo->msgnum)
427
                        lastnum = msginfo->msgnum;
428

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

    
432
                if (flags != NULL) {
433
                        /* add the permanent flags only */
434
                        msginfo->flags.perm_flags = flags->perm_flags;
435
                        if (MSG_IS_NEW(*flags))
436
                                ++new;
437
                        if (MSG_IS_UNREAD(*flags))
438
                                ++unread;
439
                        if (FOLDER_TYPE(item->folder) == F_IMAP) {
440
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
441
                        } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
442
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
443
                        }
444
                } else {
445
                        ++unflagged;
446
                        ++new;
447
                        ++unread;
448
                }
449

    
450
                ++total;
451
        }
452

    
453
        item->new = new;
454
        item->unread = unread;
455
        item->total = total;
456
        item->unmarked_num = unflagged;
457
        item->last_num = lastnum;
458
        item->updated = TRUE;
459

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

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

    
466
        hash_free_value_mem(mark_table);
467
        g_hash_table_destroy(mark_table);
468
}
469

    
470
static void mark_all_read_func(gpointer key, gpointer value, gpointer data)
471
{
472
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW|MSG_UNREAD);
473
}
474

    
475
void procmsg_mark_all_read(FolderItem *item)
476
{
477
        GHashTable *mark_table;
478

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

    
481
        mark_table = procmsg_read_mark_file(item);
482
        if (mark_table) {
483
                g_hash_table_foreach(mark_table, mark_all_read_func, NULL);
484
                procmsg_write_mark_file(item, mark_table);
485
                hash_free_value_mem(mark_table);
486
                g_hash_table_destroy(mark_table);
487
        }
488

    
489
        if (item->mark_queue) {
490
                GSList *cur;
491
                MsgFlagInfo *flaginfo;
492

    
493
                for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
494
                        flaginfo = (MsgFlagInfo *)cur->data;
495
                        MSG_UNSET_PERM_FLAGS
496
                                (flaginfo->flags, MSG_NEW|MSG_UNREAD);
497
                }
498
                item->mark_dirty = TRUE;
499
        }
500

    
501
        item->new = item->unread = 0;
502
}
503

    
504
static FolderSortType cmp_func_sort_type;
505

    
506
GSList *procmsg_sort_msg_list(GSList *mlist, FolderSortKey sort_key,
507
                              FolderSortType sort_type)
508
{
509
        GCompareFunc cmp_func;
510

    
511
        switch (sort_key) {
512
        case SORT_BY_MARK:
513
                cmp_func = procmsg_cmp_by_mark; break;
514
        case SORT_BY_UNREAD:
515
                cmp_func = procmsg_cmp_by_unread; break;
516
        case SORT_BY_MIME:
517
                cmp_func = procmsg_cmp_by_mime; break;
518
        case SORT_BY_LABEL:
519
                cmp_func = procmsg_cmp_by_label; break;
520
        case SORT_BY_NUMBER:
521
                cmp_func = procmsg_cmp_by_number; break;
522
        case SORT_BY_SIZE:
523
                cmp_func = procmsg_cmp_by_size; break;
524
        case SORT_BY_DATE:
525
                cmp_func = procmsg_cmp_by_date; break;
526
        case SORT_BY_FROM:
527
                cmp_func = procmsg_cmp_by_from; break;
528
        case SORT_BY_SUBJECT:
529
                cmp_func = procmsg_cmp_by_subject; break;
530
        case SORT_BY_TO:
531
                cmp_func = procmsg_cmp_by_to; break;
532
        default:
533
                return mlist;
534
        }
535

    
536
        cmp_func_sort_type = sort_type;
537

    
538
        mlist = g_slist_sort(mlist, cmp_func);
539

    
540
        return mlist;
541
}
542

    
543
gint procmsg_get_last_num_in_msg_list(GSList *mlist)
544
{
545
        GSList *cur;
546
        MsgInfo *msginfo;
547
        gint last = 0;
548

    
549
        for (cur = mlist; cur != NULL; cur = cur->next) {
550
                msginfo = (MsgInfo *)cur->data;
551
                if (msginfo && msginfo->msgnum > last)
552
                        last = msginfo->msgnum;
553
        }
554

    
555
        return last;
556
}
557

    
558
void procmsg_msg_list_free(GSList *mlist)
559
{
560
        GSList *cur;
561
        MsgInfo *msginfo;
562

    
563
        for (cur = mlist; cur != NULL; cur = cur->next) {
564
                msginfo = (MsgInfo *)cur->data;
565
                procmsg_msginfo_free(msginfo);
566
        }
567
        g_slist_free(mlist);
568
}
569

    
570
void procmsg_write_cache(MsgInfo *msginfo, FILE *fp)
571
{
572
        MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
573
        GSList *cur;
574

    
575
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
576
        WRITE_CACHE_DATA_INT(msginfo->size, fp);
577
        WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
578
        WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
579
        WRITE_CACHE_DATA_INT(flags, fp);
580

    
581
        WRITE_CACHE_DATA(msginfo->fromname, fp);
582

    
583
        WRITE_CACHE_DATA(msginfo->date, fp);
584
        WRITE_CACHE_DATA(msginfo->from, fp);
585
        WRITE_CACHE_DATA(msginfo->to, fp);
586
        WRITE_CACHE_DATA(msginfo->newsgroups, fp);
587
        WRITE_CACHE_DATA(msginfo->subject, fp);
588
        WRITE_CACHE_DATA(msginfo->msgid, fp);
589
        WRITE_CACHE_DATA(msginfo->inreplyto, fp);
590

    
591
        WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
592
        for (cur = msginfo->references; cur != NULL; cur = cur->next) {
593
                WRITE_CACHE_DATA((gchar *)cur->data, fp);
594
        }
595
}
596

    
597
void procmsg_write_flags(MsgInfo *msginfo, FILE *fp)
598
{
599
        MsgPermFlags flags = msginfo->flags.perm_flags;
600

    
601
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
602
        WRITE_CACHE_DATA_INT(flags, fp);
603
}
604

    
605
void procmsg_write_cache_list(FolderItem *item, GSList *mlist)
606
{
607
        FILE *fp;
608
        GSList *cur;
609

    
610
        g_return_if_fail(item != NULL);
611

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

    
614
        fp = procmsg_open_cache_file(item, DATA_WRITE);
615
        if (fp == NULL)
616
                return;
617

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

    
623
        if (item->cache_queue)
624
                procmsg_flush_cache_queue(item, fp);
625

    
626
        fclose(fp);
627
        item->cache_dirty = FALSE;
628
}
629

    
630
void procmsg_write_flags_list(FolderItem *item, GSList *mlist)
631
{
632
        FILE *fp;
633
        GSList *cur;
634

    
635
        g_return_if_fail(item != NULL);
636

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

    
639
        fp = procmsg_open_mark_file(item, DATA_WRITE);
640
        if (fp == NULL)
641
                return;
642

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

    
648
        if (item->mark_queue)
649
                procmsg_flush_mark_queue(item, fp);
650

    
651
        fclose(fp);
652
        item->mark_dirty = FALSE;
653
}
654

    
655
static gint cmp_by_item(gconstpointer a, gconstpointer b)
656
{
657
        const MsgInfo *msginfo1 = a;
658
        const MsgInfo *msginfo2 = b;
659

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

    
663
        return msginfo1->folder - msginfo2->folder;
664
}
665

    
666
void procmsg_write_flags_for_multiple_folders(GSList *mlist)
667
{
668
        GSList *tmp_list, *cur;
669
        FolderItem *prev_item = NULL;
670
        FILE *fp = NULL;
671

    
672
        if (!mlist)
673
                return;
674

    
675
        tmp_list = g_slist_copy(mlist);
676
        tmp_list = g_slist_sort(tmp_list, cmp_by_item);
677

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

    
682
                if (prev_item != item) {
683
                        if (fp)
684
                                fclose(fp);
685
                        fp = procmsg_open_mark_file(item, DATA_APPEND);
686
                        if (!fp) {
687
                                g_warning("can't open mark file\n");
688
                                break;
689
                        }
690
                        item->updated = TRUE;
691
                }
692
                procmsg_write_flags(msginfo, fp);
693
                prev_item = item;
694
        }
695

    
696
        if (fp)
697
                fclose(fp);
698
        g_slist_free(tmp_list);
699
}
700

    
701
void procmsg_flush_mark_queue(FolderItem *item, FILE *fp)
702
{
703
        MsgFlagInfo *flaginfo;
704
        MsgInfo msginfo = {0};
705
        gboolean append = FALSE;
706
        GSList *qlist, *cur;
707

    
708
        g_return_if_fail(item != NULL);
709

    
710
        if (!item->mark_queue)
711
                return;
712

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

    
715
        if (!fp) {
716
                append =  TRUE;
717
                fp = procmsg_open_mark_file(item, DATA_APPEND);
718
                g_return_if_fail(fp != NULL);
719
        }
720

    
721
        qlist = g_slist_reverse(item->mark_queue);
722
        item->mark_queue = NULL;
723

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

    
727
                msginfo.msgnum = flaginfo->msgnum;
728
                msginfo.flags = flaginfo->flags;
729
                procmsg_write_flags(&msginfo, fp);
730
                g_free(flaginfo);
731
        }
732

    
733
        g_slist_free(qlist);
734

    
735
        if (append)
736
                fclose(fp);
737
}
738

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

    
743
        flaginfo = g_new(MsgFlagInfo, 1);
744
        flaginfo->msgnum = num;
745
        flaginfo->flags = flags;
746
        item->mark_queue = g_slist_prepend(item->mark_queue, flaginfo);
747
}
748

    
749
void procmsg_flaginfo_list_free(GSList *flaglist)
750
{
751
        GSList *cur;
752
        MsgFlagInfo *flaginfo;
753

    
754
        for (cur = flaglist; cur != NULL; cur = cur->next) {
755
                flaginfo = (MsgFlagInfo *)cur->data;
756
                g_free(flaginfo);
757
        }
758
        g_slist_free(flaglist);
759
}
760

    
761
void procmsg_flush_cache_queue(FolderItem *item, FILE *fp)
762
{
763
        MsgInfo *msginfo;
764
        gboolean append = FALSE;
765
        GSList *qlist, *cur;
766

    
767
        g_return_if_fail(item != NULL);
768

    
769
        if (!item->cache_queue)
770
                return;
771

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

    
774
        if (!fp) {
775
                append =  TRUE;
776
                fp = procmsg_open_cache_file(item, DATA_APPEND);
777
                g_return_if_fail(fp != NULL);
778
        }
779

    
780
        qlist = g_slist_reverse(item->cache_queue);
781
        item->cache_queue = NULL;
782

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

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

    
792
        g_slist_free(qlist);
793

    
794
        if (append)
795
                fclose(fp);
796
}
797

    
798
void procmsg_add_cache_queue(FolderItem *item, gint num, MsgInfo *msginfo)
799
{
800
        MsgInfo *queue_msginfo;
801

    
802
        g_return_if_fail(msginfo != NULL);
803

    
804
        queue_msginfo = procmsg_msginfo_copy(msginfo);
805
        queue_msginfo->msgnum = num;
806
        queue_msginfo->folder = item;
807
        if (queue_msginfo->file_path) {
808
                g_free(queue_msginfo->file_path);
809
                queue_msginfo->file_path = NULL;
810
        }
811

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

    
817
gboolean procmsg_flush_folder(FolderItem *item)
818
{
819
        gboolean flushed = FALSE;
820
        gint n_new, n_unread, n_total, n_min, n_max;
821

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

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

    
830
        if (item->mark_queue && !item->opened)
831
                flushed = TRUE;
832
        procmsg_get_mark_sum(item, &n_new, &n_unread, &n_total, &n_min, &n_max,
833
                             0);
834
        item->unmarked_num = 0;
835
        item->new = n_new;
836
        item->unread = n_unread;
837
        item->total = n_total;
838

    
839
        if (item->cache_queue && !item->opened) {
840
                procmsg_flush_cache_queue(item, NULL);
841
                flushed = TRUE;
842
        }
843

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

    
847
        return flushed;
848
}
849

    
850
static void procmsg_flush_folder_foreach_func(gpointer key, gpointer val,
851
                                              gpointer data)
852
{
853
        procmsg_flush_folder(FOLDER_ITEM(key));
854
}
855

    
856
void procmsg_flush_folder_foreach(GHashTable *folder_table)
857
{
858
        g_hash_table_foreach(folder_table, procmsg_flush_folder_foreach_func,
859
                             NULL);
860
}
861

    
862
void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags)
863
{
864
        FILE *fp;
865
        MsgInfo msginfo;
866

    
867
        g_return_if_fail(item != NULL);
868

    
869
        if (item->opened) {
870
                procmsg_add_mark_queue(item, num, flags);
871
                return;
872
        }
873

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

    
879
        msginfo.msgnum = num;
880
        msginfo.flags = flags;
881

    
882
        procmsg_write_flags(&msginfo, fp);
883
        fclose(fp);
884
}
885

    
886
struct MarkSum {
887
        gint *new;
888
        gint *unread;
889
        gint *total;
890
        gint *min;
891
        gint *max;
892
        gint first;
893
};
894

    
895
static void mark_sum_func(gpointer key, gpointer value, gpointer data)
896
{
897
        MsgFlags *flags = value;
898
        gint num = GPOINTER_TO_INT(key);
899
        struct MarkSum *marksum = data;
900

    
901
        if (marksum->first <= num) {
902
                if (MSG_IS_NEW(*flags)) (*marksum->new)++;
903
                if (MSG_IS_UNREAD(*flags)) (*marksum->unread)++;
904
                if (num > *marksum->max) *marksum->max = num;
905
                if (num < *marksum->min || *marksum->min == 0) *marksum->min = num;
906
                (*marksum->total)++;
907
        }
908

    
909
        g_free(flags);
910
}
911

    
912
void procmsg_get_mark_sum(FolderItem *item,
913
                          gint *new, gint *unread, gint *total,
914
                          gint *min, gint *max,
915
                          gint first)
916
{
917
        GHashTable *mark_table;
918
        struct MarkSum marksum;
919

    
920
        *new = *unread = *total = *min = *max = 0;
921
        marksum.new    = new;
922
        marksum.unread = unread;
923
        marksum.total  = total;
924
        marksum.min    = min;
925
        marksum.max    = max;
926
        marksum.first  = first;
927

    
928
        mark_table = procmsg_read_mark_file(item);
929

    
930
        if (mark_table) {
931
                g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
932
                g_hash_table_destroy(mark_table);
933
        }
934
}
935

    
936
static GHashTable *procmsg_read_mark_file(FolderItem *item)
937
{
938
        FILE *fp;
939
        GHashTable *mark_table = NULL;
940
        guint32 idata;
941
        guint num;
942
        MsgFlags *flags;
943
        MsgPermFlags perm_flags;
944
        GSList *cur;
945

    
946
        if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL)
947
                return NULL;
948

    
949
        mark_table = g_hash_table_new(NULL, g_direct_equal);
950

    
951
        while (fread(&idata, sizeof(idata), 1, fp) == 1) {
952
                num = idata;
953
                if (fread(&idata, sizeof(idata), 1, fp) != 1) break;
954
                perm_flags = idata;
955

    
956
                flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num));
957
                if (flags != NULL)
958
                        g_free(flags);
959

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

    
963
                g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
964
        }
965

    
966
        fclose(fp);
967

    
968
        if (item->mark_queue) {
969
                g_hash_table_foreach(mark_table, mark_unset_new_func, NULL);
970
                item->mark_dirty = TRUE;
971
        }
972

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

    
976
                flags = g_hash_table_lookup(mark_table,
977
                                            GUINT_TO_POINTER(flaginfo->msgnum));
978
                if (flags != NULL)
979
                        g_free(flags);
980

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

    
984
                g_hash_table_insert(mark_table,
985
                                    GUINT_TO_POINTER(flaginfo->msgnum), flags);
986
                                    
987
        }
988

    
989
        if (item->mark_queue && !item->opened) {
990
                procmsg_write_mark_file(item, mark_table);
991
                procmsg_flaginfo_list_free(item->mark_queue);
992
                item->mark_queue = NULL;
993
                item->mark_dirty = FALSE;
994
        }
995

    
996
        return mark_table;
997
}
998

    
999
static void write_mark_func(gpointer key, gpointer value, gpointer data)
1000
{
1001
        MsgInfo msginfo;
1002

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

    
1008
static void procmsg_write_mark_file(FolderItem *item, GHashTable *mark_table)
1009
{
1010
        FILE *fp;
1011

    
1012
        if ((fp = procmsg_open_mark_file(item, DATA_WRITE)) == NULL) {
1013
                g_warning("procmsg_write_mark_file: cannot open mark file.");
1014
                return;
1015
        }
1016
        g_hash_table_foreach(mark_table, write_mark_func, fp);
1017
        fclose(fp);
1018
}
1019

    
1020
FILE *procmsg_open_data_file(const gchar *file, guint version,
1021
                             DataOpenMode mode, gchar *buf, size_t buf_size)
1022
{
1023
        FILE *fp;
1024
        guint32 data_ver = 0;
1025

    
1026
        g_return_val_if_fail(file != NULL, NULL);
1027

    
1028
        if (mode == DATA_WRITE) {
1029
                if ((fp = g_fopen(file, "wb")) == NULL) {
1030
                        if (errno == EACCES) {
1031
                                change_file_mode_rw(NULL, file);
1032
                                if ((fp = g_fopen(file, "wb")) == NULL) {
1033
                                        FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
1034
                                        return NULL;
1035
                                }
1036
                        } else {
1037
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
1038
                                return NULL;
1039
                        }
1040
                }
1041
                if (change_file_mode_rw(fp, file) < 0)
1042
                        FILE_OP_ERROR(file, "chmod");
1043

    
1044
                WRITE_CACHE_DATA_INT(version, fp);
1045
                return fp;
1046
        }
1047

    
1048
        /* check version */
1049
        if ((fp = g_fopen(file, "rb")) == NULL) {
1050
                if (errno == EACCES) {
1051
                        change_file_mode_rw(NULL, file);
1052
                        if ((fp = g_fopen(file, "rb")) == NULL) {
1053
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
1054
                        }
1055
                } else {
1056
                        debug_print("Mark/Cache file '%s' not found\n", file);
1057
                }
1058
        }
1059

    
1060
        if (fp) {
1061
                if (buf && buf_size > 0)
1062
                        setvbuf(fp, buf, _IOFBF, buf_size);
1063
                if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1) {
1064
                        g_warning("%s: cannot read mark/cache file (truncated?)\n", file);
1065
                        fclose(fp);
1066
                        fp = NULL;
1067
                } else if (version != data_ver) {
1068
                        g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
1069
                                  file, data_ver, version);
1070
                        fclose(fp);
1071
                        fp = NULL;
1072
                }
1073
        }
1074

    
1075
        if (mode == DATA_READ)
1076
                return fp;
1077

    
1078
        if (fp) {
1079
                /* reopen with append mode */
1080
                fclose(fp);
1081
                if ((fp = g_fopen(file, "ab")) == NULL) {
1082
                        if (errno == EACCES) {
1083
                                change_file_mode_rw(NULL, file);
1084
                                if ((fp = g_fopen(file, "ab")) == NULL) {
1085
                                        FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
1086
                                }
1087
                        } else {
1088
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
1089
                        }
1090
                }
1091
        } else {
1092
                /* open with overwrite mode if mark file doesn't exist or
1093
                   version is different */
1094
                fp = procmsg_open_data_file(file, version, DATA_WRITE, buf,
1095
                                            buf_size);
1096
        }
1097

    
1098
        return fp;
1099
}
1100

    
1101
static GMappedFile *procmsg_open_cache_file_mmap(FolderItem *item,
1102
                                                 DataOpenMode mode)
1103
{
1104
        gchar *cachefile;
1105
        GMappedFile *map = NULL;
1106
        GError *error = NULL;
1107
        gsize size;
1108
        guint32 data_ver = 0;
1109
        gchar *p;
1110

    
1111
        if (mode != DATA_READ)
1112
                return NULL;
1113

    
1114
        cachefile = folder_item_get_cache_file(item);
1115
        if (cachefile) {
1116
                map = g_mapped_file_new(cachefile, FALSE, &error);
1117
                if (!map) {
1118
                        if (error && error->code == G_FILE_ERROR_NOENT)
1119
                                debug_print("%s: mark/cache file not found\n", cachefile);
1120
                        else if (error)
1121
                                g_warning("%s: cannot open mark/cache file: %s", cachefile, error->message);
1122
                        else
1123
                                g_warning("%s: cannot open mark/cache file", cachefile);
1124
                        if (error)
1125
                                g_error_free(error);
1126
                        g_free(cachefile);
1127
                        return NULL;
1128
                }
1129
                size = g_mapped_file_get_length(map);
1130
                if (size < sizeof(data_ver)) {
1131
                        g_warning("%s: cannot read mark/cache file (truncated?)", cachefile);
1132
                        g_mapped_file_free(map);
1133
                        g_free(cachefile);
1134
                        return NULL;
1135
                }
1136
                p = g_mapped_file_get_contents(map);
1137
                data_ver = *(guint32 *)p;
1138
                if (CACHE_VERSION != data_ver) {
1139
                        g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
1140
                                  cachefile, data_ver, CACHE_VERSION);
1141
                        g_mapped_file_free(map);
1142
                        g_free(cachefile);
1143
                        return NULL;
1144
                }
1145
                g_free(cachefile);
1146
        }
1147

    
1148
        return map;
1149
}
1150

    
1151
FILE *procmsg_open_cache_file(FolderItem *item, DataOpenMode mode)
1152
{
1153
        gchar *cachefile;
1154
        FILE *fp;
1155

    
1156
        cachefile = folder_item_get_cache_file(item);
1157
        fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, NULL, 0);
1158
        g_free(cachefile);
1159

    
1160
        return fp;
1161
}
1162

    
1163
FILE *procmsg_open_mark_file(FolderItem *item, DataOpenMode mode)
1164
{
1165
        gchar *markfile;
1166
        FILE *fp;
1167

    
1168
        markfile = folder_item_get_mark_file(item);
1169
        fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0);
1170
        g_free(markfile);
1171

    
1172
        return fp;
1173
}
1174

    
1175
void procmsg_clear_cache(FolderItem *item)
1176
{
1177
        FILE *fp;
1178

    
1179
        fp = procmsg_open_cache_file(item, DATA_WRITE);
1180
        if (fp)
1181
                fclose(fp);
1182
}
1183

    
1184
void procmsg_clear_mark(FolderItem *item)
1185
{
1186
        FILE *fp;
1187

    
1188
        fp = procmsg_open_mark_file(item, DATA_WRITE);
1189
        if (fp)
1190
                fclose(fp);
1191
}
1192

    
1193
/* return the reversed thread tree */
1194
GNode *procmsg_get_thread_tree(GSList *mlist)
1195
{
1196
        GNode *root, *parent, *node, *next;
1197
        GHashTable *table;
1198
        MsgInfo *msginfo;
1199
        const gchar *msgid;
1200
        GSList *reflist;
1201

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

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

    
1209
                /* only look for the real parent first */
1210
                if (msginfo->inreplyto) {
1211
                        parent = g_hash_table_lookup(table, msginfo->inreplyto);
1212
                        if (parent == NULL)
1213
                                parent = root;
1214
                }
1215

    
1216
                node = g_node_insert_data_before
1217
                        (parent, parent == root ? parent->children : NULL,
1218
                         msginfo);
1219
                if ((msgid = msginfo->msgid) &&
1220
                    g_hash_table_lookup(table, msgid) == NULL)
1221
                        g_hash_table_insert(table, (gchar *)msgid, node);
1222
        }
1223

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

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

    
1233
                /* try looking for the indirect parent */
1234
                if (!parent && msginfo->references) {
1235
                        for (reflist = msginfo->references;
1236
                             reflist != NULL; reflist = reflist->next)
1237
                                if ((parent = g_hash_table_lookup
1238
                                        (table, reflist->data)) != NULL)
1239
                                        break;
1240
                }
1241

    
1242
                /* node should not be the parent, and node should not
1243
                   be an ancestor of parent (circular reference) */
1244
                if (parent && parent != node &&
1245
                    !g_node_is_ancestor(node, parent)) {
1246
                        g_node_unlink(node);
1247
                        g_node_insert_before
1248
                                (parent, parent->children, node);
1249
                }
1250
                node = next;
1251
        }
1252

    
1253
        g_hash_table_destroy(table);
1254

    
1255
        return root;
1256
}
1257

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

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

    
1266
        return FALSE;
1267
}
1268

    
1269
guint procmsg_get_thread_date(GNode *node)
1270
{
1271
        guint tdate = 0;
1272

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

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

    
1279
        return tdate;
1280
}
1281

    
1282
gint procmsg_move_messages(GSList *mlist)
1283
{
1284
        GSList *cur, *movelist = NULL;
1285
        MsgInfo *msginfo;
1286
        FolderItem *dest = NULL;
1287
        GHashTable *hash;
1288
        gint val = 0;
1289

    
1290
        if (!mlist) return 0;
1291

    
1292
        hash = procmsg_to_folder_hash_table_create(mlist);
1293
        folder_item_scan_foreach(hash);
1294
        g_hash_table_destroy(hash);
1295

    
1296
        for (cur = mlist; cur != NULL; cur = cur->next) {
1297
                msginfo = (MsgInfo *)cur->data;
1298
                if (!dest) {
1299
                        dest = msginfo->to_folder;
1300
                        movelist = g_slist_append(movelist, msginfo);
1301
                } else if (dest == msginfo->to_folder) {
1302
                        movelist = g_slist_append(movelist, msginfo);
1303
                } else {
1304
                        val = folder_item_move_msgs(dest, movelist);
1305
                        g_slist_free(movelist);
1306
                        movelist = NULL;
1307
                        if (val == -1)
1308
                                return val;
1309
                        dest = msginfo->to_folder;
1310
                        movelist = g_slist_append(movelist, msginfo);
1311
                }
1312
        }
1313

    
1314
        if (movelist) {
1315
                val = folder_item_move_msgs(dest, movelist);
1316
                g_slist_free(movelist);
1317
        }
1318

    
1319
        return val == -1 ? -1 : 0;
1320
}
1321

    
1322
gint procmsg_copy_messages(GSList *mlist)
1323
{
1324
        GSList *cur, *copylist = NULL;
1325
        MsgInfo *msginfo;
1326
        FolderItem *dest = NULL;
1327
        GHashTable *hash;
1328
        gint val = 0;
1329

    
1330
        if (!mlist) return 0;
1331

    
1332
        hash = procmsg_to_folder_hash_table_create(mlist);
1333
        folder_item_scan_foreach(hash);
1334
        g_hash_table_destroy(hash);
1335

    
1336
        for (cur = mlist; cur != NULL; cur = cur->next) {
1337
                msginfo = (MsgInfo *)cur->data;
1338
                if (!dest) {
1339
                        dest = msginfo->to_folder;
1340
                        copylist = g_slist_append(copylist, msginfo);
1341
                } else if (dest == msginfo->to_folder) {
1342
                        copylist = g_slist_append(copylist, msginfo);
1343
                } else {
1344
                        val = folder_item_copy_msgs(dest, copylist);
1345
                        g_slist_free(copylist);
1346
                        copylist = NULL;
1347
                        if (val == -1)
1348
                                return val;
1349
                        dest = msginfo->to_folder;
1350
                        copylist = g_slist_append(copylist, msginfo);
1351
                }
1352
        }
1353

    
1354
        if (copylist) {
1355
                val = folder_item_copy_msgs(dest, copylist);
1356
                g_slist_free(copylist);
1357
        }
1358

    
1359
        return val == -1 ? -1 : 0;
1360
}
1361

    
1362
gint procmsg_add_messages_from_queue(FolderItem *dest, GSList *mlist,
1363
                                     gboolean is_move)
1364
{
1365
        MsgInfo *msginfo;
1366
        GSList *cur;
1367
        gchar *file;
1368
        FILE *fp;
1369
        gchar buf[BUFFSIZE];
1370
        gchar *dest_file;
1371
        gboolean is_error = FALSE;
1372
        FolderItem *src;
1373
        MsgFlags flags;
1374

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

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

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

    
1385
        for (cur = mlist; cur != NULL; cur = cur->next) {
1386
                msginfo = (MsgInfo *)cur->data;
1387
                flags = msginfo->flags;
1388
                if (!MSG_IS_QUEUED(flags))
1389
                        return -1;
1390
                MSG_UNSET_TMP_FLAGS(flags, MSG_QUEUED);
1391
                src = msginfo->folder;
1392
                file = procmsg_get_message_file(msginfo);
1393
                if (!file)
1394
                        return -1;
1395
                if ((fp = g_fopen(file, "rb")) == NULL) {
1396
                        FILE_OP_ERROR(file, "folder_item_move_msgs: fopen");
1397
                        g_free(file);
1398
                        return -1;
1399
                }
1400
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1401
                        if (buf[0] == '\r' || buf[0] == '\n')
1402
                                break;
1403
                }
1404
                if (ferror(fp)) {
1405
                        fclose(fp);
1406
                        g_free(file);
1407
                        return -1;
1408
                }
1409

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

    
1413
                if (copy_file_part(fp, ftell(fp), G_MAXINT, dest_file) < 0) {
1414
                        fclose(fp);
1415
                        is_error = TRUE;
1416
                } else {
1417
                        fclose(fp);
1418
                        if (folder_item_add_msg(dest, dest_file, &flags, TRUE) < 0) {
1419
                                g_unlink(dest_file);
1420
                                is_error = TRUE;
1421
                        } else if (is_move &&
1422
                                   folder_item_remove_msg(src, msginfo) < 0)
1423
                                is_error = TRUE;
1424
                }
1425

    
1426
                g_free(dest_file);
1427
                g_free(file);
1428
                if (is_error)
1429
                        return -1;
1430
        }
1431

    
1432
        return 0;
1433
}
1434

    
1435
gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
1436
{
1437
        gchar *path, *file;
1438

    
1439
        g_return_val_if_fail(msginfo != NULL, NULL);
1440

    
1441
        if (msginfo->encinfo && msginfo->encinfo->plaintext_file)
1442
                file = g_strdup(msginfo->encinfo->plaintext_file);
1443
        else if (msginfo->file_path)
1444
                return g_strdup(msginfo->file_path);
1445
        else {
1446
                gchar nstr[16];
1447
                path = folder_item_get_path(msginfo->folder);
1448
                file = g_strconcat(path, G_DIR_SEPARATOR_S,
1449
                                   utos_buf(nstr, msginfo->msgnum), NULL);
1450
                g_free(path);
1451
        }
1452

    
1453
        return file;
1454
}
1455

    
1456
gchar *procmsg_get_message_file(MsgInfo *msginfo)
1457
{
1458
        gchar *filename = NULL;
1459

    
1460
        g_return_val_if_fail(msginfo != NULL, NULL);
1461

    
1462
        if (msginfo->file_path)
1463
                return g_strdup(msginfo->file_path);
1464

    
1465
        filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
1466
        if (!filename)
1467
                debug_print(_("can't fetch message %d\n"), msginfo->msgnum);
1468

    
1469
        return filename;
1470
}
1471

    
1472
GSList *procmsg_get_message_file_list(GSList *mlist)
1473
{
1474
        GSList *file_list = NULL;
1475
        MsgInfo *msginfo;
1476
        MsgFileInfo *fileinfo;
1477
        gchar *file;
1478

    
1479
        while (mlist != NULL) {
1480
                msginfo = (MsgInfo *)mlist->data;
1481
                file = procmsg_get_message_file(msginfo);
1482
                if (!file) {
1483
                        procmsg_message_file_list_free(file_list);
1484
                        return NULL;
1485
                }
1486
                fileinfo = g_new(MsgFileInfo, 1);
1487
                fileinfo->file = file;
1488
                fileinfo->flags = g_new(MsgFlags, 1);
1489
                *fileinfo->flags = msginfo->flags;
1490
                file_list = g_slist_prepend(file_list, fileinfo);
1491
                mlist = mlist->next;
1492
        }
1493

    
1494
        file_list = g_slist_reverse(file_list);
1495

    
1496
        return file_list;
1497
}
1498

    
1499
void procmsg_message_file_list_free(GSList *file_list)
1500
{
1501
        GSList *cur;
1502
        MsgFileInfo *fileinfo;
1503

    
1504
        for (cur = file_list; cur != NULL; cur = cur->next) {
1505
                fileinfo = (MsgFileInfo *)cur->data;
1506
                g_free(fileinfo->file);
1507
                g_free(fileinfo->flags);
1508
                g_free(fileinfo);
1509
        }
1510

    
1511
        g_slist_free(file_list);
1512
}
1513

    
1514
FILE *procmsg_open_message(MsgInfo *msginfo)
1515
{
1516
        FILE *fp;
1517
        gchar *file;
1518

    
1519
        g_return_val_if_fail(msginfo != NULL, NULL);
1520

    
1521
        file = procmsg_get_message_file_path(msginfo);
1522
        g_return_val_if_fail(file != NULL, NULL);
1523

    
1524
        if (!is_file_exist(file)) {
1525
                g_free(file);
1526
                file = procmsg_get_message_file(msginfo);
1527
                if (!file)
1528
                        return NULL;
1529
        }
1530

    
1531
        if ((fp = g_fopen(file, "rb")) == NULL) {
1532
                FILE_OP_ERROR(file, "procmsg_open_message: fopen");
1533
                g_free(file);
1534
                return NULL;
1535
        }
1536

    
1537
        g_free(file);
1538

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

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

    
1546
        return fp;
1547
}
1548

    
1549
static DecryptMessageFunc decrypt_message_func = NULL;
1550
static gboolean auto_decrypt = TRUE;
1551

    
1552
void procmsg_set_decrypt_message_func(DecryptMessageFunc func)
1553
{
1554
        decrypt_message_func = func;
1555
}
1556

    
1557
void procmsg_set_auto_decrypt_message(gboolean enabled)
1558
{
1559
        auto_decrypt = enabled;
1560
}
1561

    
1562
FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
1563
{
1564
        FILE *fp;
1565

    
1566
        if (decrypt_message_func && auto_decrypt)
1567
                return decrypt_message_func(msginfo, mimeinfo);
1568

    
1569
        *mimeinfo = NULL;
1570
        if ((fp = procmsg_open_message(msginfo)) == NULL)
1571
                return NULL;
1572
        *mimeinfo = procmime_scan_mime_header(fp);
1573

    
1574
        return fp;
1575
}
1576

    
1577
gboolean procmsg_msg_exist(MsgInfo *msginfo)
1578
{
1579
        gchar *path;
1580
        gboolean ret;
1581

    
1582
        if (!msginfo) return FALSE;
1583

    
1584
        path = folder_item_get_path(msginfo->folder);
1585
        change_dir(path);
1586
        ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
1587
        g_free(path);
1588

    
1589
        return ret;
1590
}
1591

    
1592
gboolean procmsg_trash_messages_exist(void)
1593
{
1594
        FolderItem *trash;
1595
        GList *cur;
1596

    
1597
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1598
                trash = FOLDER(cur->data)->trash;
1599
                if (trash && trash->total > 0)
1600
                        return TRUE;
1601
        }
1602

    
1603
        return FALSE;
1604
}
1605

    
1606
void procmsg_empty_trash(FolderItem *trash)
1607
{
1608
        if (!trash)
1609
                return;
1610

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

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

    
1616
                folder_item_remove_all_msg(trash);
1617
                procmsg_clear_cache(trash);
1618
                procmsg_clear_mark(trash);
1619
                trash->cache_dirty = FALSE;
1620
                trash->mark_dirty = FALSE;
1621
        }
1622
}
1623

    
1624
void procmsg_empty_all_trash(void)
1625
{
1626
        FolderItem *trash;
1627
        GList *cur;
1628

    
1629
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1630
                trash = FOLDER(cur->data)->trash;
1631
                procmsg_empty_trash(trash);
1632
        }
1633
}
1634

    
1635
static gboolean remove_all_cached_messages_func(GNode *node, gpointer data)
1636
{
1637
        FolderItem *item;
1638
        gchar *dir;
1639

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

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

    
1646
        dir = folder_item_get_path(item);
1647
        if (is_dir_exist(dir)) {
1648
                debug_print("removing all cached messages in '%s' ...\n",
1649
                            item->path);
1650
                remove_all_numbered_files(dir);
1651
        }
1652
        g_free(dir);
1653

    
1654
        return FALSE;
1655
}
1656

    
1657
void procmsg_remove_all_cached_messages(Folder *folder)
1658
{
1659
        g_return_if_fail(folder != NULL);
1660
        g_return_if_fail(FOLDER_IS_REMOTE(folder));
1661

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

    
1665
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1666
                        remove_all_cached_messages_func, NULL);
1667
}
1668

    
1669
gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file)
1670
{
1671
        gint num;
1672
        MsgFlags flag = {0, 0};
1673

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

    
1676
        if (!outbox)
1677
                outbox = folder_get_default_outbox();
1678
        g_return_val_if_fail(outbox != NULL, -1);
1679

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

    
1687
        return 0;
1688
}
1689

    
1690
static guint print_id = 0;
1691

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

    
1697
#ifdef G_OS_WIN32
1698
        if (canonicalize_file_replace(file) < 0)
1699
                return -1;
1700
#endif
1701

    
1702
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
1703
                g_snprintf(buf, sizeof(buf) - 1, cmdline, file);
1704
        else {
1705
                if (cmdline) {
1706
                        g_warning(_("Print command line is invalid: `%s'\n"),
1707
                                  cmdline);
1708
                        return -1;
1709
                }
1710

    
1711
#ifdef G_OS_WIN32
1712
                execute_print_file(file);
1713
                return 0;
1714
#else
1715
                g_snprintf(buf, sizeof(buf) - 1, def_cmd, file);
1716
#endif
1717
        }
1718

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

    
1723
        system(buf);
1724

    
1725
        return 0;
1726
}
1727

    
1728
static void procmsg_write_headers(MsgInfo *msginfo, MimeInfo *partinfo,
1729
                                  FILE *fp, FILE *dest_fp,
1730
                                  const gchar *encoding, gboolean all_headers)
1731
{
1732
        GPtrArray *headers;
1733
        gint i;
1734

    
1735
        if (all_headers)
1736
                headers = procheader_get_header_array_asis(fp, NULL);
1737
        else
1738
                headers = procheader_get_header_array_for_display(fp, NULL);
1739

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

    
1745
                hdr = g_ptr_array_index(headers, i);
1746

    
1747
                if (partinfo) {
1748
                        if (!g_ascii_strcasecmp(hdr->name, "Subject") ||
1749
                            !g_ascii_strcasecmp(hdr->name, "From") ||
1750
                            !g_ascii_strcasecmp(hdr->name, "To") ||
1751
                            !g_ascii_strcasecmp(hdr->name, "Cc")) {
1752
                                unfold_line(hdr->body);
1753
                        }
1754

    
1755
                        body = hdr->body;
1756
                        while (g_ascii_isspace(*body))
1757
                                body++;
1758
                } else {
1759
                        if (!g_ascii_strcasecmp(hdr->name, "Subject"))
1760
                                body = msginfo->subject;
1761
                        else if (!g_ascii_strcasecmp(hdr->name, "From"))
1762
                                body = msginfo->from;
1763
                        else if (!g_ascii_strcasecmp(hdr->name, "To"))
1764
                                body = msginfo->to;
1765
                        else if (!g_ascii_strcasecmp(hdr->name, "Cc")) {
1766
                                unfold_line(hdr->body);
1767
                                body = hdr->body;
1768
                                while (g_ascii_isspace(*body))
1769
                                        body++;
1770
                        } else {
1771
                                body = hdr->body;
1772
                                while (g_ascii_isspace(*body))
1773
                                        body++;
1774
                        }
1775
                }
1776

    
1777
                if (body && *body != '\0') {
1778
                        file_str = conv_codeset_strdup
1779
                                (body, CS_INTERNAL, encoding);
1780
                        fprintf(dest_fp, "%s: %s\n", hdr->name,
1781
                                file_str ? file_str : body);
1782
                        g_free(file_str);
1783
                } else {
1784
                        fprintf(dest_fp, "%s: (none)\n", hdr->name);
1785
                }
1786
        }
1787

    
1788
        procheader_header_array_destroy(headers);
1789
}
1790

    
1791
void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline,
1792
                           gboolean all_headers)
1793
{
1794
        gchar *prtmp;
1795
        FILE *msgfp, *tmpfp, *prfp;
1796
        gchar buf[BUFFSIZE];
1797

    
1798
        g_return_if_fail(msginfo != NULL);
1799

    
1800
        if ((tmpfp = procmime_get_first_text_content
1801
                (msginfo, conv_get_locale_charset_str())) == NULL) {
1802
                g_warning("Can't get text part\n");
1803
                return;
1804
        }
1805

    
1806
        prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
1807
                                get_mime_tmp_dir(), G_DIR_SEPARATOR,
1808
                                print_id++);
1809

    
1810
        if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1811
                FILE_OP_ERROR(prtmp, "procmsg_print_message: fopen");
1812
                g_free(prtmp);
1813
                fclose(tmpfp);
1814
                return;
1815
        }
1816

    
1817
        if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
1818
                fclose(prfp);
1819
                g_free(prtmp);
1820
                fclose(tmpfp);
1821
                return;
1822
        }
1823

    
1824
        procmsg_write_headers(msginfo, NULL, msgfp, prfp,
1825
                              conv_get_locale_charset_str(), all_headers);
1826

    
1827
        fclose(msgfp);
1828

    
1829
        fputc('\n', prfp);
1830

    
1831
        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1832
                fputs(buf, prfp);
1833

    
1834
        fclose(prfp);
1835
        fclose(tmpfp);
1836

    
1837
        print_command_exec(prtmp, cmdline);
1838

    
1839
        g_free(prtmp);
1840
}
1841

    
1842
void procmsg_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo,
1843
                                const gchar *cmdline, gboolean all_headers)
1844
{
1845
        FILE *msgfp, *tmpfp, *prfp;
1846
        gchar *prtmp;
1847
        gchar buf[BUFFSIZE];
1848

    
1849
        if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
1850
                return;
1851
        }
1852

    
1853
        if ((tmpfp = procmime_get_text_content
1854
                (partinfo, msgfp, conv_get_locale_charset_str())) == NULL) {
1855
                fclose(msgfp);
1856
                return;
1857
        }
1858
        fclose(msgfp);
1859

    
1860
        prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
1861
                                get_mime_tmp_dir(), G_DIR_SEPARATOR,
1862
                                print_id++);
1863
        if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1864
                FILE_OP_ERROR(prtmp, "procmsg_print_message_part: fopen");
1865
                g_free(prtmp);
1866
                fclose(tmpfp);
1867
                return;
1868
        }
1869

    
1870
        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1871
                fputs(buf, prfp);
1872

    
1873
        fclose(prfp);
1874
        fclose(tmpfp);
1875

    
1876
        print_command_exec(prtmp, cmdline);
1877

    
1878
        g_free(prtmp);
1879
}
1880

    
1881
gint procmsg_save_message_as_text(MsgInfo *msginfo, const gchar *dest,
1882
                                  const gchar *encoding, gboolean all_headers)
1883
{
1884
        MimeInfo *mimeinfo, *partinfo;
1885
        FILE *fp;
1886
        FILE *tmpfp;
1887
        FILE *destfp;
1888
        gchar buf[BUFFSIZE];
1889

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

    
1893
        mimeinfo = procmime_scan_message(msginfo);
1894
        if (!mimeinfo)
1895
                return -1;
1896
        if ((fp = procmsg_open_message(msginfo)) == NULL) {
1897
                procmime_mimeinfo_free_all(mimeinfo);
1898
                return -1;
1899
        }
1900
        if ((destfp = g_fopen(dest, "wb")) == NULL) {
1901
                fclose(fp);
1902
                procmime_mimeinfo_free_all(mimeinfo);
1903
                return -1;
1904
        }
1905
        procmsg_write_headers(msginfo, mimeinfo, fp, destfp, encoding, all_headers);
1906
        fputc('\n', destfp);
1907

    
1908
        partinfo = mimeinfo;
1909

    
1910
        while (partinfo != NULL) {
1911
                if (fseek(fp, partinfo->fpos, SEEK_SET) < 0)
1912
                        break;
1913

    
1914
                if (partinfo->filename || partinfo->name)
1915
                        g_snprintf(buf, sizeof(buf), "\n[%s  %s (%s)]\n",
1916
                                   partinfo->filename ? partinfo->filename :
1917
                                   partinfo->name,
1918
                                   partinfo->content_type,
1919
                                   to_human_readable(partinfo->content_size));
1920
                else
1921
                        g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n",
1922
                                   partinfo->content_type,
1923
                                   to_human_readable(partinfo->content_size));
1924

    
1925
                if (partinfo->mime_type == MIME_TEXT ||
1926
                    partinfo->mime_type == MIME_TEXT_HTML) {
1927
                        if (!partinfo->main &&
1928
                            partinfo->parent &&
1929
                            partinfo->parent->children != partinfo) {
1930
                                fputs(buf, destfp);
1931
                        }
1932

    
1933
                        if ((tmpfp = procmime_get_text_content(partinfo, fp, encoding)) == NULL)
1934
                                break;
1935
                        if (copy_file_stream(tmpfp, destfp) < 0) {
1936
                                fclose(tmpfp);
1937
                                break;
1938
                        }
1939

    
1940
                        fclose(tmpfp);
1941
                } else if (partinfo->mime_type == MIME_MESSAGE_RFC822) {
1942
                        fputs(buf, destfp);
1943
                        while (fgets(buf, sizeof(buf), fp) != NULL)
1944
                                if (buf[0] == '\r' || buf[0] == '\n') break;
1945
                        procmsg_write_headers(msginfo, partinfo, fp, destfp, encoding, all_headers);
1946
                        fputc('\n', destfp);
1947
                } else if (partinfo->mime_type != MIME_MULTIPART) {
1948
                        fputs(buf, destfp);
1949
                }
1950

    
1951
                if (partinfo->parent && partinfo->parent->content_type &&
1952
                    !g_ascii_strcasecmp(partinfo->parent->content_type,
1953
                                        "multipart/alternative"))
1954
                        partinfo = partinfo->parent->next;
1955
                else
1956
                        partinfo = procmime_mimeinfo_next(partinfo);
1957
        }
1958

    
1959
        fclose(destfp);
1960
        fclose(fp);
1961
        procmime_mimeinfo_free_all(mimeinfo);
1962

    
1963
        return 0;
1964
}
1965

    
1966
/**
1967
 * procmsg_concat_partial_messages:
1968
 * @mlist: list of MsgInfo* including message/partial messages.
1969
 * @file: output file name of concatenated message.
1970
 *
1971
 * Concatenate @mlist which consists of message/partial messages and
1972
 * output to @file. If @mlist has different partial id, the first one
1973
 * is used.
1974
 *
1975
 * Return value: 0 on success, or -1 if failed.
1976
 **/
1977
gint procmsg_concat_partial_messages(GSList *mlist, const gchar *file)
1978
{
1979
        static HeaderEntry hentry[] = {{"Content-Type:", NULL, FALSE},
1980
                                       {NULL, NULL, FALSE}};
1981
        FILE *fp;
1982
        gchar buf[BUFFSIZE];
1983
        FILE *tmp_fp;
1984
        gchar *part_id = NULL;
1985
        gint total = 0;
1986
        MsgInfo *msg_array[100] = {NULL};
1987
        MsgInfo *msginfo;
1988
        MimeInfo *mimeinfo;
1989
        GSList *cur;
1990
        gint i;
1991

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

    
1995
        debug_print("procmsg_concat_partial_messages\n");
1996

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

    
2002
                msginfo = (MsgInfo *)cur->data;
2003

    
2004
                fp = procmsg_open_message_decrypted(msginfo, &mimeinfo);
2005
                if (!fp)
2006
                        continue;
2007
                if (!mimeinfo->content_type ||
2008
                    g_ascii_strcasecmp(mimeinfo->content_type, "message/partial") != 0)
2009
                        goto skip;
2010

    
2011
                rewind(fp);
2012
                if (procheader_get_one_field(buf, sizeof(buf), fp, hentry) == -1)
2013
                        goto skip;
2014

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

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

    
2029
                if ((t > 0 && total != t) || (total > 0 && n > total) ||
2030
                    strcmp(part_id, cur_id) != 0) {
2031
                        debug_print("skip\n");
2032
                        g_free(cur_id);
2033
                        goto skip;
2034
                }
2035

    
2036
                msg_array[n - 1] = msginfo;
2037

    
2038
                g_free(cur_id);
2039
skip:
2040
                fclose(fp);
2041
                procmime_mimeinfo_free_all(mimeinfo);
2042
        }
2043

    
2044
        if (!part_id) {
2045
                debug_print("piece not found\n");
2046
                return -1;
2047
        }
2048

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

    
2052
        if (total == 0) {
2053
                debug_print("total number not found\n");
2054
                return -1;
2055
        }
2056

    
2057
        /* check if all pieces exist */
2058
        for (i = 0; i < total; i++) {
2059
                if (msg_array[i] == NULL) {
2060
                        debug_print("message part %d not exist\n", i + 1);
2061
                        return -1;
2062
                }
2063
        }
2064

    
2065
        /* concatenate parts */
2066
        if ((tmp_fp = g_fopen(file, "wb")) == NULL) {
2067
                FILE_OP_ERROR(file, "fopen");
2068
                return -1;
2069
        }
2070

    
2071
        for (i = 0; i < total; i++) {
2072
                msginfo = msg_array[i];
2073
                off_t out_size;
2074
                gint empty_line_size = 0;
2075

    
2076
                fp = procmsg_open_message_decrypted(msginfo, &mimeinfo);
2077
                if (!fp) {
2078
                        g_warning("cannot open message part %d\n", i + 1);
2079
                        fclose(tmp_fp);
2080
                        g_unlink(file);
2081
                        return -1;
2082
                }
2083

    
2084
                /* write out first headers */
2085
                if (i == 0) {
2086
                        rewind(fp);
2087
                        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
2088
                                if (!g_ascii_strncasecmp(buf, "Content-", 8) ||
2089
                                    !g_ascii_strncasecmp(buf, "Subject", 7) ||
2090
                                    !g_ascii_strncasecmp(buf, "Message-ID", 10) ||
2091
                                    !g_ascii_strncasecmp(buf, "Encrypted", 9) ||
2092
                                    !g_ascii_strncasecmp(buf, "MIME-Version", 12))
2093
                                        continue;
2094
                                fputs(buf, tmp_fp);
2095
                                fputs("\n", tmp_fp);
2096
                        }
2097

    
2098
                        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
2099
                                if (!g_ascii_strncasecmp(buf, "Content-", 8) ||
2100
                                    !g_ascii_strncasecmp(buf, "Subject", 7) ||
2101
                                    !g_ascii_strncasecmp(buf, "Message-ID", 10) ||
2102
                                    !g_ascii_strncasecmp(buf, "Encrypted", 9) ||
2103
                                    !g_ascii_strncasecmp(buf, "MIME-Version", 12)) {
2104
                                        fputs(buf, tmp_fp);
2105
                                        fputs("\n", tmp_fp);
2106
                                }
2107
                        }
2108

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

    
2113
                out_size = get_left_file_size(fp);
2114
                if (out_size < 0) {
2115
                        g_warning("cannot tell left file size of part %d\n", i + 1);
2116
                        fclose(tmp_fp);
2117
                        g_unlink(file);
2118
                        return -1;
2119
                }
2120
                empty_line_size = get_last_empty_line_size(fp, out_size);
2121
                if (empty_line_size < 0) {
2122
                        g_warning("cannot get last empty line size of part %d\n", i + 1);
2123
                        fclose(tmp_fp);
2124
                        g_unlink(file);
2125
                        return -1;
2126
                }
2127

    
2128
                if (append_file_part(fp, ftell(fp), out_size - empty_line_size,
2129
                                     tmp_fp) < 0) {
2130
                        g_warning("write failed\n");
2131
                        fclose(tmp_fp);
2132
                        g_unlink(file);
2133
                        return -1;
2134
                }
2135

    
2136
                fclose(fp);
2137
                procmime_mimeinfo_free_all(mimeinfo);
2138
        }
2139

    
2140
        fclose(tmp_fp);
2141

    
2142
        return 0;
2143
}
2144

    
2145
static gboolean procmsg_get_flags(FolderItem *item, gint num,
2146
                                  MsgPermFlags *flags)
2147
{
2148
        FILE *fp;
2149
        guint32 idata;
2150
        gint read_num;
2151
        MsgPermFlags perm_flags;
2152
        gboolean found = FALSE;
2153
        GSList *cur;
2154

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

    
2158
        while (fread(&idata, sizeof(idata), 1, fp) == 1) {
2159
                read_num = idata;
2160
                if (fread(&idata, sizeof(idata), 1, fp) != 1)
2161
                        break;
2162
                perm_flags = idata;
2163
                if (read_num == num) {
2164
                        *flags = perm_flags;
2165
                        found = TRUE;
2166
                        break;
2167
                }
2168
        }
2169

    
2170
        fclose(fp);
2171
        if (found)
2172
                return TRUE;
2173

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

    
2177
                if (flaginfo->msgnum == num) {
2178
                        *flags = flaginfo->flags.perm_flags;
2179
                        found = TRUE;
2180
                        break;
2181
                }
2182
        }
2183

    
2184
        return found;
2185
}
2186

    
2187
MsgInfo *procmsg_get_msginfo(FolderItem *item, gint num)
2188
{
2189
        MsgInfo *msginfo;
2190
        FolderType type;
2191

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

    
2194
        msginfo = folder_item_get_msginfo(item, num);
2195
        if (!msginfo)
2196
                return NULL;
2197

    
2198
        type = FOLDER_TYPE(item->folder);
2199
        if (type == F_MH || type == F_IMAP) {
2200
                if (item->stype == F_QUEUE) {
2201
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2202
                } else if (item->stype == F_DRAFT) {
2203
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2204
                }
2205
        }
2206
        if (type == F_IMAP) {
2207
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
2208
        } else if (type == F_NEWS) {
2209
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
2210
        }
2211

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

    
2218
        return msginfo;
2219
}
2220

    
2221
MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
2222
{
2223
        MsgInfo *newmsginfo;
2224

    
2225
        if (msginfo == NULL) return NULL;
2226

    
2227
        newmsginfo = g_new0(MsgInfo, 1);
2228

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

    
2233
        MEMBCOPY(msgnum);
2234
        MEMBCOPY(size);
2235
        MEMBCOPY(mtime);
2236
        MEMBCOPY(date_t);
2237

    
2238
        MEMBCOPY(flags);
2239

    
2240
        MEMBDUP(fromname);
2241

    
2242
        MEMBDUP(date);
2243
        MEMBDUP(from);
2244
        MEMBDUP(to);
2245
        MEMBDUP(cc);
2246
        MEMBDUP(newsgroups);
2247
        MEMBDUP(subject);
2248
        MEMBDUP(msgid);
2249
        MEMBDUP(inreplyto);
2250

    
2251
        MEMBCOPY(folder);
2252
        MEMBCOPY(to_folder);
2253

    
2254
        MEMBDUP(xface);
2255

    
2256
        MEMBDUP(file_path);
2257

    
2258
        if (msginfo->encinfo) {
2259
                newmsginfo->encinfo = g_new0(MsgEncryptInfo, 1);
2260
                MEMBDUP(encinfo->plaintext_file);
2261
                MEMBDUP(encinfo->sigstatus);
2262
                MEMBDUP(encinfo->sigstatus_full);
2263
                MEMBCOPY(encinfo->decryption_failed);
2264
        }
2265

    
2266
        return newmsginfo;
2267
}
2268

    
2269
MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
2270
{
2271
        MsgInfo *full_msginfo;
2272
        gchar *file;
2273

    
2274
        if (msginfo == NULL) return NULL;
2275

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

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

    
2286
        full_msginfo->msgnum = msginfo->msgnum;
2287
        full_msginfo->size = msginfo->size;
2288
        full_msginfo->mtime = msginfo->mtime;
2289
        full_msginfo->folder = msginfo->folder;
2290
        full_msginfo->to_folder = msginfo->to_folder;
2291

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

    
2294
        if (msginfo->encinfo) {
2295
                full_msginfo->encinfo = g_new0(MsgEncryptInfo, 1);
2296
                full_msginfo->encinfo->plaintext_file =
2297
                        g_strdup(msginfo->encinfo->plaintext_file);
2298
                full_msginfo->encinfo->sigstatus =
2299
                        g_strdup(msginfo->encinfo->sigstatus);
2300
                full_msginfo->encinfo->sigstatus_full =
2301
                        g_strdup(msginfo->encinfo->sigstatus_full);
2302
                full_msginfo->encinfo->decryption_failed =
2303
                        msginfo->encinfo->decryption_failed;
2304
        }
2305

    
2306
        return full_msginfo;
2307
}
2308

    
2309
gboolean procmsg_msginfo_equal(MsgInfo *msginfo_a, MsgInfo *msginfo_b)
2310
{
2311
        if (!msginfo_a || !msginfo_b)
2312
                return FALSE;
2313

    
2314
        if (msginfo_a == msginfo_b)
2315
                return TRUE;
2316

    
2317
        if (msginfo_a->folder == msginfo_b->folder &&
2318
            msginfo_a->msgnum == msginfo_b->msgnum &&
2319
            msginfo_a->size   == msginfo_b->size   &&
2320
            msginfo_a->mtime  == msginfo_b->mtime)
2321
                return TRUE;
2322

    
2323
        return FALSE;
2324
}
2325

    
2326
void procmsg_msginfo_free(MsgInfo *msginfo)
2327
{
2328
        if (msginfo == NULL) return;
2329

    
2330
        g_free(msginfo->xface);
2331

    
2332
        g_free(msginfo->fromname);
2333

    
2334
        g_free(msginfo->date);
2335
        g_free(msginfo->from);
2336
        g_free(msginfo->to);
2337
        g_free(msginfo->cc);
2338
        g_free(msginfo->newsgroups);
2339
        g_free(msginfo->subject);
2340
        g_free(msginfo->msgid);
2341
        g_free(msginfo->inreplyto);
2342

    
2343
        slist_free_strings(msginfo->references);
2344
        g_slist_free(msginfo->references);
2345

    
2346
        g_free(msginfo->file_path);
2347

    
2348
        if (msginfo->encinfo) {
2349
                g_free(msginfo->encinfo->plaintext_file);
2350
                g_free(msginfo->encinfo->sigstatus);
2351
                g_free(msginfo->encinfo->sigstatus_full);
2352
                g_free(msginfo->encinfo);
2353
        }
2354

    
2355
        g_free(msginfo);
2356
}
2357

    
2358
gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
2359
{
2360
        const MsgInfo *msginfo1 = a;
2361
        const MsgInfo *msginfo2 = b;
2362

    
2363
        if (!msginfo1 || !msginfo2)
2364
                return 0;
2365

    
2366
        return msginfo1->msgnum - msginfo2->msgnum;
2367
}
2368

    
2369
#define CMP_FUNC_DEF(func_name, val)                                        \
2370
static gint func_name(gconstpointer a, gconstpointer b)                        \
2371
{                                                                        \
2372
        const MsgInfo *msginfo1 = a;                                        \
2373
        const MsgInfo *msginfo2 = b;                                        \
2374
        gint ret;                                                        \
2375
                                                                        \
2376
        if (!msginfo1 || !msginfo2)                                        \
2377
                return 0;                                                \
2378
                                                                        \
2379
        ret = (val);                                                        \
2380
        if (ret == 0)                                                        \
2381
                ret = msginfo1->date_t - msginfo2->date_t;                \
2382
                                                                        \
2383
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
2384
}
2385

    
2386
CMP_FUNC_DEF(procmsg_cmp_by_mark,
2387
             MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
2388
CMP_FUNC_DEF(procmsg_cmp_by_unread,
2389
             MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
2390
CMP_FUNC_DEF(procmsg_cmp_by_mime,
2391
             MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags))
2392
CMP_FUNC_DEF(procmsg_cmp_by_label,
2393
             MSG_GET_COLORLABEL(msginfo1->flags) -
2394
             MSG_GET_COLORLABEL(msginfo2->flags))
2395
CMP_FUNC_DEF(procmsg_cmp_by_size, msginfo1->size - msginfo2->size)
2396

    
2397
#undef CMP_FUNC_DEF
2398
#define CMP_FUNC_DEF(func_name, val)                                        \
2399
static gint func_name(gconstpointer a, gconstpointer b)                        \
2400
{                                                                        \
2401
        const MsgInfo *msginfo1 = a;                                        \
2402
        const MsgInfo *msginfo2 = b;                                        \
2403
                                                                        \
2404
        if (!msginfo1 || !msginfo2)                                        \
2405
                return 0;                                                \
2406
                                                                        \
2407
        return (val) * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
2408
}
2409

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

    
2413
#undef CMP_FUNC_DEF
2414
#define CMP_FUNC_DEF(func_name, var_name)                                \
2415
static gint func_name(gconstpointer a, gconstpointer b)                        \
2416
{                                                                        \
2417
        const MsgInfo *msginfo1 = a;                                        \
2418
        const MsgInfo *msginfo2 = b;                                        \
2419
        gint ret;                                                        \
2420
                                                                        \
2421
        if (!msginfo1->var_name)                                        \
2422
                return (msginfo2->var_name != NULL) *                        \
2423
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);\
2424
        if (!msginfo2->var_name)                                        \
2425
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
2426
                                                                        \
2427
        ret = g_ascii_strcasecmp                                        \
2428
                (msginfo1->var_name, msginfo2->var_name);                \
2429
        if (ret == 0)                                                        \
2430
                ret = msginfo1->date_t - msginfo2->date_t;                \
2431
                                                                        \
2432
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
2433
}
2434

    
2435
CMP_FUNC_DEF(procmsg_cmp_by_from, fromname)
2436
CMP_FUNC_DEF(procmsg_cmp_by_to, to)
2437

    
2438
#undef CMP_FUNC_DEF
2439

    
2440
static gint procmsg_cmp_by_subject(gconstpointer a, gconstpointer b)
2441
{
2442
        const MsgInfo *msginfo1 = a;
2443
        const MsgInfo *msginfo2 = b;
2444
        gint ret;
2445

    
2446
        if (!msginfo1->subject)
2447
                return (msginfo2->subject != NULL) *
2448
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);
2449
        if (!msginfo2->subject)
2450
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);
2451

    
2452
        ret = subject_compare_for_sort(msginfo1->subject, msginfo2->subject);
2453
        if (ret == 0)
2454
                ret = msginfo1->date_t - msginfo2->date_t;
2455

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