Statistics
| Revision:

root / libsylph / procmsg.c @ 1082

History | View | Annotate | Download (37.2 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2006 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
27
#include "utils.h"
28
#include "procmsg.h"
29
#include "procheader.h"
30
#include "account.h"
31
#include "procmime.h"
32
#include "prefs_common.h"
33
#include "folder.h"
34
#include "codeconv.h"
35
36
static void mark_sum_func                        (gpointer         key,
37
                                                 gpointer         value,
38
                                                 gpointer         data);
39
40
static GHashTable *procmsg_read_mark_file        (FolderItem        *item);
41
static void procmsg_write_mark_file                (FolderItem        *item,
42
                                                 GHashTable        *mark_table);
43
44
static FILE *procmsg_open_cache_file_with_buffer(FolderItem        *item,
45
                                                 DataOpenMode         mode,
46
                                                 gchar                *buf,
47
                                                 size_t                 buf_size);
48
49
static gint procmsg_cmp_by_mark                        (gconstpointer         a,
50
                                                 gconstpointer         b);
51
static gint procmsg_cmp_by_unread                (gconstpointer         a,
52
                                                 gconstpointer         b);
53
static gint procmsg_cmp_by_mime                        (gconstpointer         a,
54
                                                 gconstpointer         b);
55
static gint procmsg_cmp_by_label                (gconstpointer         a,
56
                                                 gconstpointer         b);
57
static gint procmsg_cmp_by_number                (gconstpointer         a,
58
                                                 gconstpointer         b);
59
static gint procmsg_cmp_by_size                        (gconstpointer         a,
60
                                                 gconstpointer         b);
61
static gint procmsg_cmp_by_date                        (gconstpointer         a,
62
                                                 gconstpointer         b);
63
static gint procmsg_cmp_by_from                        (gconstpointer         a,
64
                                                 gconstpointer         b);
65
static gint procmsg_cmp_by_to                        (gconstpointer         a,
66
                                                 gconstpointer         b);
67
static gint procmsg_cmp_by_subject                (gconstpointer         a,
68
                                                 gconstpointer         b);
69
70
71
GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
72
{
73
        GHashTable *msg_table;
74
75
        if (mlist == NULL) return NULL;
76
77
        msg_table = g_hash_table_new(NULL, g_direct_equal);
78
        procmsg_msg_hash_table_append(msg_table, mlist);
79
80
        return msg_table;
81
}
82
83
void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
84
{
85
        GSList *cur;
86
        MsgInfo *msginfo;
87
88
        if (msg_table == NULL || mlist == NULL) return;
89
90
        for (cur = mlist; cur != NULL; cur = cur->next) {
91
                msginfo = (MsgInfo *)cur->data;
92
93
                g_hash_table_insert(msg_table,
94
                                    GUINT_TO_POINTER(msginfo->msgnum),
95
                                    msginfo);
96
        }
97
}
98
99
GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
100
{
101
        GHashTable *msg_table;
102
        GSList *cur;
103
        MsgInfo *msginfo;
104
105
        if (mlist == NULL) return NULL;
106
107
        msg_table = g_hash_table_new(NULL, g_direct_equal);
108
109
        for (cur = mlist; cur != NULL; cur = cur->next) {
110
                msginfo = (MsgInfo *)cur->data;
111
                g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
112
        }
113
114
        return msg_table;
115
}
116
117
gint procmsg_read_cache_data_str(FILE *fp, gchar **str)
118
{
119
        gchar buf[BUFFSIZE];
120
        gint ret = 0;
121
        guint32 len;
122
123
        if (fread(&len, sizeof(len), 1, fp) == 1) {
124
                if (len > G_MAXINT)
125
                        ret = -1;
126
                else {
127
                        gchar *tmp = NULL;
128
129
                        while (len > 0) {
130
                                size_t size = MIN(len, BUFFSIZE - 1);
131
132
                                if (fread(buf, size, 1, fp) != 1) {
133
                                        ret = -1;
134
                                        if (tmp) g_free(tmp);
135
                                        *str = NULL;
136
                                        break;
137
                                }
138
139
                                buf[size] = '\0';
140
                                if (tmp) {
141
                                        *str = g_strconcat(tmp, buf, NULL);
142
                                        g_free(tmp);
143
                                        tmp = *str;
144
                                } else
145
                                        tmp = *str = g_strdup(buf);
146
147
                                len -= size;
148
                        }
149
                }
150
        } else
151
                ret = -1;
152
153
        return ret;
154
}
155
156
#define READ_CACHE_DATA(data, fp)                                \
157
{                                                                \
158
        if (procmsg_read_cache_data_str(fp, &data) < 0) {        \
159
                procmsg_msginfo_free(msginfo);                        \
160
                procmsg_msg_list_free(mlist);                        \
161
                mlist = NULL;                                        \
162
                break;                                                \
163
        }                                                        \
164
}
165
166
#define READ_CACHE_DATA_INT(n, fp)                                \
167
{                                                                \
168
        guint32 idata;                                                \
169
                                                                \
170
        if (fread(&idata, sizeof(idata), 1, fp) != 1) {                \
171
                g_warning("Cache data is corrupted\n");                \
172
                procmsg_msginfo_free(msginfo);                        \
173
                procmsg_msg_list_free(mlist);                        \
174
                mlist = NULL;                                        \
175
                break;                                                \
176
        } else                                                        \
177
                n = idata;                                        \
178
}
179
180
GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file)
181
{
182
        GSList *mlist = NULL;
183
        GSList *last = NULL;
184
        FILE *fp;
185
        MsgInfo *msginfo;
186
        MsgFlags default_flags;
187
        gchar file_buf[BUFFSIZE];
188
        guint32 num;
189
        guint refnum;
190
        FolderType type;
191
192
        g_return_val_if_fail(item != NULL, NULL);
193
        g_return_val_if_fail(item->folder != NULL, NULL);
194
        type = FOLDER_TYPE(item->folder);
195
196
        default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
197
        default_flags.tmp_flags = 0;
198
        if (type == F_MH || type == F_IMAP) {
199
                if (item->stype == F_QUEUE) {
200
                        MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
201
                } else if (item->stype == F_DRAFT) {
202
                        MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
203
                }
204
        }
205
        if (type == F_IMAP) {
206
                MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
207
        } else if (type == F_NEWS) {
208
                MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
209
        }
210
211
        if (type == F_MH) {
212
                gchar *path;
213
214
                path = folder_item_get_path(item);
215
                if (change_dir(path) < 0) {
216
                        g_free(path);
217
                        return NULL;
218
                }
219
                g_free(path);
220
        }
221
222
        if ((fp = procmsg_open_cache_file_with_buffer
223
                (item, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
224
                item->cache_dirty = TRUE;
225
                return NULL;
226
        }
227
228
        debug_print("Reading summary cache...");
229
230
        while (fread(&num, sizeof(num), 1, fp) == 1) {
231
                msginfo = g_new0(MsgInfo, 1);
232
                msginfo->msgnum = num;
233
                READ_CACHE_DATA_INT(msginfo->size, fp);
234
                READ_CACHE_DATA_INT(msginfo->mtime, fp);
235
                READ_CACHE_DATA_INT(msginfo->date_t, fp);
236
                READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
237
238
                READ_CACHE_DATA(msginfo->fromname, fp);
239
240
                READ_CACHE_DATA(msginfo->date, fp);
241
                READ_CACHE_DATA(msginfo->from, fp);
242
                READ_CACHE_DATA(msginfo->to, fp);
243
                READ_CACHE_DATA(msginfo->newsgroups, fp);
244
                READ_CACHE_DATA(msginfo->subject, fp);
245
                READ_CACHE_DATA(msginfo->msgid, fp);
246
                READ_CACHE_DATA(msginfo->inreplyto, fp);
247
248
                READ_CACHE_DATA_INT(refnum, fp);
249
                for (; refnum != 0; refnum--) {
250
                        gchar *ref;
251
252
                        READ_CACHE_DATA(ref, fp);
253
                        msginfo->references =
254
                                g_slist_prepend(msginfo->references, ref);
255
                }
256
                if (msginfo->references)
257
                        msginfo->references =
258
                                g_slist_reverse(msginfo->references);
259
260
                MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
261
                MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);
262
263
                /* if the message file doesn't exist or is changed,
264
                   don't add the data */
265
                if ((type == F_MH && scan_file &&
266
                     folder_item_is_msg_changed(item, msginfo)) || num == 0) {
267
                        procmsg_msginfo_free(msginfo);
268
                        item->cache_dirty = TRUE;
269
                } else {
270
                        msginfo->folder = item;
271
272
                        if (!mlist)
273
                                last = mlist = g_slist_append(NULL, msginfo);
274
                        else {
275
                                last = g_slist_append(last, msginfo);
276
                                last = last->next;
277
                        }
278
                }
279
        }
280
281
        fclose(fp);
282
283
        debug_print("done.\n");
284
285
        return mlist;
286
}
287
288
#undef READ_CACHE_DATA
289
#undef READ_CACHE_DATA_INT
290
291
static void mark_unset_new_func(gpointer key, gpointer value, gpointer data)
292
{
293
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW);
294
}
295
296
void procmsg_set_flags(GSList *mlist, FolderItem *item)
297
{
298
        GSList *cur;
299
        gint new = 0, unread = 0, total = 0;
300
        gint lastnum = 0;
301
        gint unflagged = 0;
302
        gboolean mark_queue_exist;
303
        MsgInfo *msginfo;
304
        GHashTable *mark_table;
305
        MsgFlags *flags;
306
307
        g_return_if_fail(item != NULL);
308
        g_return_if_fail(item->folder != NULL);
309
310
        debug_print("Marking the messages...\n");
311
312
        mark_queue_exist = (item->mark_queue != NULL);
313
        mark_table = procmsg_read_mark_file(item);
314
        if (!mark_table) {
315
                item->new = item->unread = item->total = g_slist_length(mlist);
316
                item->updated = TRUE;
317
                item->mark_dirty = TRUE;
318
                return;
319
        }
320
321
        /* unset new flags if new (unflagged) messages exist */
322
        if (!mark_queue_exist) {
323
                for (cur = mlist; cur != NULL; cur = cur->next) {
324
                        msginfo = (MsgInfo *)cur->data;
325
                        flags = g_hash_table_lookup
326
                                (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
327
                        if (!flags) {
328
                                g_hash_table_foreach(mark_table,
329
                                                     mark_unset_new_func, NULL);
330
                                item->mark_dirty = TRUE;
331
                                break;
332
                        }
333
                }
334
        }
335
336
        for (cur = mlist; cur != NULL; cur = cur->next) {
337
                msginfo = (MsgInfo *)cur->data;
338
339
                if (lastnum < msginfo->msgnum)
340
                        lastnum = msginfo->msgnum;
341
342
                flags = g_hash_table_lookup
343
                        (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
344
345
                if (flags != NULL) {
346
                        /* add the permanent flags only */
347
                        msginfo->flags.perm_flags = flags->perm_flags;
348
                        if (MSG_IS_NEW(*flags))
349
                                ++new;
350
                        if (MSG_IS_UNREAD(*flags))
351
                                ++unread;
352
                        if (FOLDER_TYPE(item->folder) == F_IMAP) {
353
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
354
                        } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
355
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
356
                        }
357
                } else {
358
                        ++unflagged;
359
                        ++new;
360
                        ++unread;
361
                }
362
363
                ++total;
364
        }
365
366
        item->new = new;
367
        item->unread = unread;
368
        item->total = total;
369
        item->unmarked_num = unflagged;
370
        item->last_num = lastnum;
371
        item->updated = TRUE;
372
373
        if (unflagged > 0)
374
                item->mark_dirty = TRUE;
375
376
        debug_print("new: %d unread: %d unflagged: %d total: %d\n",
377
                    new, unread, unflagged, total);
378
379
        hash_free_value_mem(mark_table);
380
        g_hash_table_destroy(mark_table);
381
}
382
383
static void mark_all_read_func(gpointer key, gpointer value, gpointer data)
384
{
385
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW|MSG_UNREAD);
386
}
387
388
void procmsg_mark_all_read(FolderItem *item)
389
{
390
        GHashTable *mark_table;
391
392
        debug_print("Marking all messages as read\n");
393
394
        mark_table = procmsg_read_mark_file(item);
395
        if (mark_table) {
396
                g_hash_table_foreach(mark_table, mark_all_read_func, NULL);
397
                procmsg_write_mark_file(item, mark_table);
398
                hash_free_value_mem(mark_table);
399
                g_hash_table_destroy(mark_table);
400
        }
401
402
        if (item->mark_queue) {
403
                GSList *cur;
404
                MsgInfo *msginfo;
405
406
                for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
407
                        msginfo = (MsgInfo *)cur->data;
408
                        MSG_UNSET_PERM_FLAGS
409
                                (msginfo->flags, MSG_NEW|MSG_UNREAD);
410
                }
411
                item->mark_dirty = TRUE;
412
        }
413
414
        item->new = item->unread = 0;
415
}
416
417
static FolderSortType cmp_func_sort_type;
418
419
GSList *procmsg_sort_msg_list(GSList *mlist, FolderSortKey sort_key,
420
                              FolderSortType sort_type)
421
{
422
        GCompareFunc cmp_func;
423
424
        switch (sort_key) {
425
        case SORT_BY_MARK:
426
                cmp_func = procmsg_cmp_by_mark; break;
427
        case SORT_BY_UNREAD:
428
                cmp_func = procmsg_cmp_by_unread; break;
429
        case SORT_BY_MIME:
430
                cmp_func = procmsg_cmp_by_mime; break;
431
        case SORT_BY_LABEL:
432
                cmp_func = procmsg_cmp_by_label; break;
433
        case SORT_BY_NUMBER:
434
                cmp_func = procmsg_cmp_by_number; break;
435
        case SORT_BY_SIZE:
436
                cmp_func = procmsg_cmp_by_size; break;
437
        case SORT_BY_DATE:
438
                cmp_func = procmsg_cmp_by_date; break;
439
        case SORT_BY_FROM:
440
                cmp_func = procmsg_cmp_by_from; break;
441
        case SORT_BY_SUBJECT:
442
                cmp_func = procmsg_cmp_by_subject; break;
443
        case SORT_BY_TO:
444
                cmp_func = procmsg_cmp_by_to; break;
445
        default:
446
                return mlist;
447
        }
448
449
        cmp_func_sort_type = sort_type;
450
451
        mlist = g_slist_sort(mlist, cmp_func);
452
453
        return mlist;
454
}
455
456
gint procmsg_get_last_num_in_msg_list(GSList *mlist)
457
{
458
        GSList *cur;
459
        MsgInfo *msginfo;
460
        gint last = 0;
461
462
        for (cur = mlist; cur != NULL; cur = cur->next) {
463
                msginfo = (MsgInfo *)cur->data;
464
                if (msginfo && msginfo->msgnum > last)
465
                        last = msginfo->msgnum;
466
        }
467
468
        return last;
469
}
470
471
void procmsg_msg_list_free(GSList *mlist)
472
{
473
        GSList *cur;
474
        MsgInfo *msginfo;
475
476
        for (cur = mlist; cur != NULL; cur = cur->next) {
477
                msginfo = (MsgInfo *)cur->data;
478
                procmsg_msginfo_free(msginfo);
479
        }
480
        g_slist_free(mlist);
481
}
482
483
void procmsg_write_cache(MsgInfo *msginfo, FILE *fp)
484
{
485
        MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
486
        GSList *cur;
487
488
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
489
        WRITE_CACHE_DATA_INT(msginfo->size, fp);
490
        WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
491
        WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
492
        WRITE_CACHE_DATA_INT(flags, fp);
493
494
        WRITE_CACHE_DATA(msginfo->fromname, fp);
495
496
        WRITE_CACHE_DATA(msginfo->date, fp);
497
        WRITE_CACHE_DATA(msginfo->from, fp);
498
        WRITE_CACHE_DATA(msginfo->to, fp);
499
        WRITE_CACHE_DATA(msginfo->newsgroups, fp);
500
        WRITE_CACHE_DATA(msginfo->subject, fp);
501
        WRITE_CACHE_DATA(msginfo->msgid, fp);
502
        WRITE_CACHE_DATA(msginfo->inreplyto, fp);
503
504
        WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
505
        for (cur = msginfo->references; cur != NULL; cur = cur->next) {
506
                WRITE_CACHE_DATA((gchar *)cur->data, fp);
507
        }
508
}
509
510
void procmsg_write_flags(MsgInfo *msginfo, FILE *fp)
511
{
512
        MsgPermFlags flags = msginfo->flags.perm_flags;
513
514
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
515
        WRITE_CACHE_DATA_INT(flags, fp);
516
}
517
518
void procmsg_write_cache_list(FolderItem *item, GSList *mlist)
519
{
520
        FILE *fp;
521
        GSList *cur;
522
523
        g_return_if_fail(item != NULL);
524
525
        debug_print("Writing summary cache (%s)\n", item->path);
526
527
        fp = procmsg_open_cache_file(item, DATA_WRITE);
528
        if (fp == NULL)
529
                return;
530
531
        for (cur = mlist; cur != NULL; cur = cur->next) {
532
                MsgInfo *msginfo = (MsgInfo *)cur->data;
533
                procmsg_write_cache(msginfo, fp);
534
        }
535
536
        fclose(fp);
537
        item->cache_dirty = FALSE;
538
}
539
540
void procmsg_write_flags_list(FolderItem *item, GSList *mlist)
541
{
542
        FILE *fp;
543
        GSList *cur;
544
545
        g_return_if_fail(item != NULL);
546
547
        debug_print("Writing summary flags (%s)\n", item->path);
548
549
        fp = procmsg_open_mark_file(item, DATA_WRITE);
550
        if (fp == NULL)
551
                return;
552
553
        for (cur = mlist; cur != NULL; cur = cur->next) {
554
                MsgInfo *msginfo = (MsgInfo *)cur->data;
555
                procmsg_write_flags(msginfo, fp);
556
        }
557
558
        if (item->mark_queue)
559
                procmsg_flush_mark_queue(item, fp);
560
561
        fclose(fp);
562
        item->mark_dirty = FALSE;
563
}
564
565
static gint cmp_by_item(gconstpointer a, gconstpointer b)
566
{
567
        const MsgInfo *msginfo1 = a;
568
        const MsgInfo *msginfo2 = b;
569
570
        if (msginfo1->folder == msginfo2->folder)
571
                return msginfo1->msgnum - msginfo2->msgnum;
572
573
        return msginfo1->folder - msginfo2->folder;
574
}
575
576
void procmsg_write_flags_for_multiple_folders(GSList *mlist)
577
{
578
        GSList *tmp_list, *cur;
579
        FolderItem *prev_item = NULL;
580
        FILE *fp = NULL;
581
582
        if (!mlist)
583
                return;
584
585
        tmp_list = g_slist_copy(mlist);
586
        tmp_list = g_slist_sort(tmp_list, cmp_by_item);
587
588
        for (cur = tmp_list; cur != NULL; cur = cur->next) {
589
                MsgInfo *msginfo = (MsgInfo *)cur->data;
590
                FolderItem *item = msginfo->folder;
591
592
                if (prev_item != item) {
593
                        if (fp)
594
                                fclose(fp);
595
                        fp = procmsg_open_mark_file(item, DATA_APPEND);
596
                        if (!fp) {
597
                                g_warning("can't open mark file\n");
598
                                break;
599
                        }
600
                        item->updated = TRUE;
601
                }
602
                procmsg_write_flags(msginfo, fp);
603
                prev_item = item;
604
        }
605
606
        if (fp)
607
                fclose(fp);
608
        g_slist_free(tmp_list);
609
}
610
611
void procmsg_flush_mark_queue(FolderItem *item, FILE *fp)
612
{
613
        MsgInfo *flaginfo;
614
615
        g_return_if_fail(item != NULL);
616
        g_return_if_fail(fp != NULL);
617
618
        if (item->mark_queue)
619
                debug_print("flushing mark_queue...\n");
620
621
        while (item->mark_queue != NULL) {
622
                flaginfo = (MsgInfo *)item->mark_queue->data;
623
                procmsg_write_flags(flaginfo, fp);
624
                procmsg_msginfo_free(flaginfo);
625
                item->mark_queue = g_slist_remove(item->mark_queue, flaginfo);
626
        }
627
}
628
629
void procmsg_add_mark_queue(FolderItem *item, gint num, MsgFlags flags)
630
{
631
        MsgInfo *queue_msginfo;
632
633
        queue_msginfo = g_new0(MsgInfo, 1);
634
        queue_msginfo->msgnum = num;
635
        queue_msginfo->flags = flags;
636
        item->mark_queue = g_slist_append
637
                (item->mark_queue, queue_msginfo);
638
        return;
639
}
640
641
void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags)
642
{
643
        FILE *fp;
644
        MsgInfo msginfo;
645
646
        g_return_if_fail(item != NULL);
647
648
        if (item->opened) {
649
                procmsg_add_mark_queue(item, num, flags);
650
                return;
651
        }
652
653
        if ((fp = procmsg_open_mark_file(item, DATA_APPEND)) == NULL) {
654
                g_warning(_("can't open mark file\n"));
655
                return;
656
        }
657
658
        msginfo.msgnum = num;
659
        msginfo.flags = flags;
660
661
        procmsg_write_flags(&msginfo, fp);
662
        fclose(fp);
663
}
664
665
struct MarkSum {
666
        gint *new;
667
        gint *unread;
668
        gint *total;
669
        gint *min;
670
        gint *max;
671
        gint first;
672
};
673
674
static void mark_sum_func(gpointer key, gpointer value, gpointer data)
675
{
676
        MsgFlags *flags = value;
677
        gint num = GPOINTER_TO_INT(key);
678
        struct MarkSum *marksum = data;
679
680
        if (marksum->first <= num) {
681
                if (MSG_IS_NEW(*flags)) (*marksum->new)++;
682
                if (MSG_IS_UNREAD(*flags)) (*marksum->unread)++;
683
                if (num > *marksum->max) *marksum->max = num;
684
                if (num < *marksum->min || *marksum->min == 0) *marksum->min = num;
685
                (*marksum->total)++;
686
        }
687
688
        g_free(flags);
689
}
690
691
void procmsg_get_mark_sum(FolderItem *item,
692
                          gint *new, gint *unread, gint *total,
693
                          gint *min, gint *max,
694
                          gint first)
695
{
696
        GHashTable *mark_table;
697
        struct MarkSum marksum;
698
699
        *new = *unread = *total = *min = *max = 0;
700
        marksum.new    = new;
701
        marksum.unread = unread;
702
        marksum.total  = total;
703
        marksum.min    = min;
704
        marksum.max    = max;
705
        marksum.first  = first;
706
707
        mark_table = procmsg_read_mark_file(item);
708
709
        if (mark_table) {
710
                g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
711
                g_hash_table_destroy(mark_table);
712
        }
713
}
714
715
static GHashTable *procmsg_read_mark_file(FolderItem *item)
716
{
717
        FILE *fp;
718
        GHashTable *mark_table = NULL;
719
        guint32 idata;
720
        guint num;
721
        MsgFlags *flags;
722
        MsgPermFlags perm_flags;
723
        GSList *cur;
724
725
        if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL)
726
                return NULL;
727
728
        mark_table = g_hash_table_new(NULL, g_direct_equal);
729
730
        while (fread(&idata, sizeof(idata), 1, fp) == 1) {
731
                num = idata;
732
                if (fread(&idata, sizeof(idata), 1, fp) != 1) break;
733
                perm_flags = idata;
734
735
                flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num));
736
                if (flags != NULL)
737
                        g_free(flags);
738
739
                flags = g_new0(MsgFlags, 1);
740
                flags->perm_flags = perm_flags;
741
742
                g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
743
        }
744
745
        fclose(fp);
746
747
        if (item->mark_queue) {
748
                g_hash_table_foreach(mark_table, mark_unset_new_func, NULL);
749
                item->mark_dirty = TRUE;
750
        }
751
752
        for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
753
                MsgInfo *msginfo = (MsgInfo *)cur->data;
754
755
                flags = g_hash_table_lookup(mark_table,
756
                                            GUINT_TO_POINTER(msginfo->msgnum));
757
                if (flags != NULL)
758
                        g_free(flags);
759
760
                flags = g_new0(MsgFlags, 1);
761
                flags->perm_flags = msginfo->flags.perm_flags;
762
763
                g_hash_table_insert(mark_table,
764
                                    GUINT_TO_POINTER(msginfo->msgnum), flags);
765
                                    
766
        }
767
768
        if (item->mark_queue && !item->opened) {
769
                procmsg_write_mark_file(item, mark_table);
770
                procmsg_msg_list_free(item->mark_queue);
771
                item->mark_queue = NULL;
772
                item->mark_dirty = FALSE;
773
        }
774
775
        return mark_table;
776
}
777
778
static void write_mark_func(gpointer key, gpointer value, gpointer data)
779
{
780
        MsgInfo msginfo;
781
782
        msginfo.msgnum = GPOINTER_TO_UINT(key);
783
        msginfo.flags.perm_flags = ((MsgFlags *)value)->perm_flags;
784
        procmsg_write_flags(&msginfo, (FILE *)data);
785
}
786
787
static void procmsg_write_mark_file(FolderItem *item, GHashTable *mark_table)
788
{
789
        FILE *fp;
790
791
        fp = procmsg_open_mark_file(item, DATA_WRITE);
792
        g_hash_table_foreach(mark_table, write_mark_func, fp);
793
        fclose(fp);
794
}
795
796
FILE *procmsg_open_data_file(const gchar *file, guint version,
797
                             DataOpenMode mode, gchar *buf, size_t buf_size)
798
{
799
        FILE *fp;
800
        guint32 data_ver;
801
802
        g_return_val_if_fail(file != NULL, NULL);
803
804
        if (mode == DATA_WRITE) {
805
                if ((fp = g_fopen(file, "wb")) == NULL) {
806
                        FILE_OP_ERROR(file, "fopen");
807
                        return NULL;
808
                }
809
                if (change_file_mode_rw(fp, file) < 0)
810
                        FILE_OP_ERROR(file, "chmod");
811
812
                WRITE_CACHE_DATA_INT(version, fp);
813
                return fp;
814
        }
815
816
        /* check version */
817
        if ((fp = g_fopen(file, "rb")) == NULL)
818
                debug_print("Mark/Cache file '%s' not found\n", file);
819
        else {
820
                if (buf && buf_size > 0)
821
                        setvbuf(fp, buf, _IOFBF, buf_size);
822
                if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1 ||
823
                    version != data_ver) {
824
                        g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
825
                                  file, data_ver, version);
826
                        fclose(fp);
827
                        fp = NULL;
828
                }
829
        }
830
831
        if (mode == DATA_READ)
832
                return fp;
833
834
        if (fp) {
835
                /* reopen with append mode */
836
                fclose(fp);
837
                if ((fp = g_fopen(file, "ab")) == NULL)
838
                        FILE_OP_ERROR(file, "fopen");
839
        } else {
840
                /* open with overwrite mode if mark file doesn't exist or
841
                   version is different */
842
                fp = procmsg_open_data_file(file, version, DATA_WRITE, buf,
843
                                            buf_size);
844
        }
845
846
        return fp;
847
}
848
849
static FILE *procmsg_open_cache_file_with_buffer(FolderItem *item,
850
                                                 DataOpenMode mode,
851
                                                 gchar *buf, size_t buf_size)
852
{
853
        gchar *cachefile;
854
        FILE *fp;
855
856
        cachefile = folder_item_get_cache_file(item);
857
        fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, buf,
858
                                    buf_size);
859
        g_free(cachefile);
860
861
        return fp;
862
}
863
864
FILE *procmsg_open_cache_file(FolderItem *item, DataOpenMode mode)
865
{
866
        gchar *cachefile;
867
        FILE *fp;
868
869
        cachefile = folder_item_get_cache_file(item);
870
        fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, NULL, 0);
871
        g_free(cachefile);
872
873
        return fp;
874
}
875
876
FILE *procmsg_open_mark_file(FolderItem *item, DataOpenMode mode)
877
{
878
        gchar *markfile;
879
        FILE *fp;
880
881
        markfile = folder_item_get_mark_file(item);
882
        fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0);
883
        g_free(markfile);
884
885
        return fp;
886
}
887
888
void procmsg_clear_cache(FolderItem *item)
889
{
890
        FILE *fp;
891
892
        fp = procmsg_open_cache_file(item, DATA_WRITE);
893
        if (fp)
894
                fclose(fp);
895
}
896
897
void procmsg_clear_mark(FolderItem *item)
898
{
899
        FILE *fp;
900
901
        fp = procmsg_open_mark_file(item, DATA_WRITE);
902
        if (fp)
903
                fclose(fp);
904
}
905
906
/* return the reversed thread tree */
907
GNode *procmsg_get_thread_tree(GSList *mlist)
908
{
909
        GNode *root, *parent, *node, *next;
910
        GHashTable *table;
911
        MsgInfo *msginfo;
912
        const gchar *msgid;
913
        GSList *reflist;
914
915
        root = g_node_new(NULL);
916
        table = g_hash_table_new(g_str_hash, g_str_equal);
917
918
        for (; mlist != NULL; mlist = mlist->next) {
919
                msginfo = (MsgInfo *)mlist->data;
920
                parent = root;
921
922
                /* only look for the real parent first */
923
                if (msginfo->inreplyto) {
924
                        parent = g_hash_table_lookup(table, msginfo->inreplyto);
925
                        if (parent == NULL)
926
                                parent = root;
927
                }
928
929
                node = g_node_insert_data_before
930
                        (parent, parent == root ? parent->children : NULL,
931
                         msginfo);
932
                if ((msgid = msginfo->msgid) &&
933
                    g_hash_table_lookup(table, msgid) == NULL)
934
                        g_hash_table_insert(table, (gchar *)msgid, node);
935
        }
936
937
        /* complete the unfinished threads */
938
        for (node = root->children; node != NULL; ) {
939
                next = node->next;
940
                msginfo = (MsgInfo *)node->data;
941
                parent = NULL;
942
943
                if (msginfo->inreplyto)
944
                        parent = g_hash_table_lookup(table, msginfo->inreplyto);
945
946
                /* try looking for the indirect parent */
947
                if (!parent && msginfo->references) {
948
                        for (reflist = msginfo->references;
949
                             reflist != NULL; reflist = reflist->next)
950
                                if ((parent = g_hash_table_lookup
951
                                        (table, reflist->data)) != NULL)
952
                                        break;
953
                }
954
955
                /* node should not be the parent, and node should not
956
                   be an ancestor of parent (circular reference) */
957
                if (parent && parent != node &&
958
                    !g_node_is_ancestor(node, parent)) {
959
                        g_node_unlink(node);
960
                        g_node_insert_before
961
                                (parent, parent->children, node);
962
                }
963
                node = next;
964
        }
965
966
        g_hash_table_destroy(table);
967
968
        return root;
969
}
970
971
static gboolean procmsg_thread_date_func(GNode *node, gpointer data)
972
{
973
        guint *tdate = (guint *)data;
974
        MsgInfo *msginfo = (MsgInfo *)node->data;
975
976
        if (*tdate < msginfo->date_t)
977
                *tdate = msginfo->date_t;
978
979
        return FALSE;
980
}
981
982
guint procmsg_get_thread_date(GNode *node)
983
{
984
        guint tdate = 0;
985
986
        g_return_val_if_fail(node != NULL && node->parent != NULL &&
987
                             node->parent->parent == NULL, 0);
988
989
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
990
                        procmsg_thread_date_func, &tdate);
991
992
        return tdate;
993
}
994
995
gint procmsg_move_messages(GSList *mlist)
996
{
997
        GSList *cur, *movelist = NULL;
998
        MsgInfo *msginfo;
999
        FolderItem *dest = NULL;
1000
        GHashTable *hash;
1001
        gint val = 0;
1002
1003
        if (!mlist) return 0;
1004
1005
        hash = procmsg_to_folder_hash_table_create(mlist);
1006
        folder_item_scan_foreach(hash);
1007
        g_hash_table_destroy(hash);
1008
1009
        for (cur = mlist; cur != NULL; cur = cur->next) {
1010
                msginfo = (MsgInfo *)cur->data;
1011
                if (!dest) {
1012
                        dest = msginfo->to_folder;
1013
                        movelist = g_slist_append(movelist, msginfo);
1014
                } else if (dest == msginfo->to_folder) {
1015
                        movelist = g_slist_append(movelist, msginfo);
1016
                } else {
1017
                        val = folder_item_move_msgs(dest, movelist);
1018
                        g_slist_free(movelist);
1019
                        movelist = NULL;
1020
                        if (val == -1)
1021
                                return val;
1022
                        dest = msginfo->to_folder;
1023
                        movelist = g_slist_append(movelist, msginfo);
1024
                }
1025
        }
1026
1027
        if (movelist) {
1028
                val = folder_item_move_msgs(dest, movelist);
1029
                g_slist_free(movelist);
1030
        }
1031
1032
        return val == -1 ? -1 : 0;
1033
}
1034
1035
gint procmsg_copy_messages(GSList *mlist)
1036
{
1037
        GSList *cur, *copylist = NULL;
1038
        MsgInfo *msginfo;
1039
        FolderItem *dest = NULL;
1040
        GHashTable *hash;
1041
        gint val = 0;
1042
1043
        if (!mlist) return 0;
1044
1045
        hash = procmsg_to_folder_hash_table_create(mlist);
1046
        folder_item_scan_foreach(hash);
1047
        g_hash_table_destroy(hash);
1048
1049
        for (cur = mlist; cur != NULL; cur = cur->next) {
1050
                msginfo = (MsgInfo *)cur->data;
1051
                if (!dest) {
1052
                        dest = msginfo->to_folder;
1053
                        copylist = g_slist_append(copylist, msginfo);
1054
                } else if (dest == msginfo->to_folder) {
1055
                        copylist = g_slist_append(copylist, msginfo);
1056
                } else {
1057
                        val = folder_item_copy_msgs(dest, copylist);
1058
                        g_slist_free(copylist);
1059
                        copylist = NULL;
1060
                        if (val == -1)
1061
                                return val;
1062
                        dest = msginfo->to_folder;
1063
                        copylist = g_slist_append(copylist, msginfo);
1064
                }
1065
        }
1066
1067
        if (copylist) {
1068
                val = folder_item_copy_msgs(dest, copylist);
1069
                g_slist_free(copylist);
1070
        }
1071
1072
        return val == -1 ? -1 : 0;
1073
}
1074
1075
gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
1076
{
1077
        gchar *path, *file;
1078
1079
        g_return_val_if_fail(msginfo != NULL, NULL);
1080
1081
        if (msginfo->plaintext_file)
1082
                file = g_strdup(msginfo->plaintext_file);
1083
        else if (msginfo->file_path)
1084
                return g_strdup(msginfo->file_path);
1085
        else {
1086
                path = folder_item_get_path(msginfo->folder);
1087
                file = g_strconcat(path, G_DIR_SEPARATOR_S,
1088
                                   itos(msginfo->msgnum), NULL);
1089
                g_free(path);
1090
        }
1091
1092
        return file;
1093
}
1094
1095
gchar *procmsg_get_message_file(MsgInfo *msginfo)
1096
{
1097
        gchar *filename = NULL;
1098
1099
        g_return_val_if_fail(msginfo != NULL, NULL);
1100
1101
        if (msginfo->file_path)
1102
                return g_strdup(msginfo->file_path);
1103
1104
        filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
1105
        if (!filename)
1106
                debug_print(_("can't fetch message %d\n"), msginfo->msgnum);
1107
1108
        return filename;
1109
}
1110
1111
GSList *procmsg_get_message_file_list(GSList *mlist)
1112
{
1113
        GSList *file_list = NULL;
1114
        MsgInfo *msginfo;
1115
        MsgFileInfo *fileinfo;
1116
        gchar *file;
1117
1118
        while (mlist != NULL) {
1119
                msginfo = (MsgInfo *)mlist->data;
1120
                file = procmsg_get_message_file(msginfo);
1121
                if (!file) {
1122
                        procmsg_message_file_list_free(file_list);
1123
                        return NULL;
1124
                }
1125
                fileinfo = g_new(MsgFileInfo, 1);
1126
                fileinfo->file = file;
1127
                fileinfo->flags = g_new(MsgFlags, 1);
1128
                *fileinfo->flags = msginfo->flags;
1129
                file_list = g_slist_prepend(file_list, fileinfo);
1130
                mlist = mlist->next;
1131
        }
1132
1133
        file_list = g_slist_reverse(file_list);
1134
1135
        return file_list;
1136
}
1137
1138
void procmsg_message_file_list_free(GSList *file_list)
1139
{
1140
        GSList *cur;
1141
        MsgFileInfo *fileinfo;
1142
1143
        for (cur = file_list; cur != NULL; cur = cur->next) {
1144
                fileinfo = (MsgFileInfo *)cur->data;
1145
                g_free(fileinfo->file);
1146
                g_free(fileinfo->flags);
1147
                g_free(fileinfo);
1148
        }
1149
1150
        g_slist_free(file_list);
1151
}
1152
1153
FILE *procmsg_open_message(MsgInfo *msginfo)
1154
{
1155
        FILE *fp;
1156
        gchar *file;
1157
1158
        g_return_val_if_fail(msginfo != NULL, NULL);
1159
1160
        file = procmsg_get_message_file_path(msginfo);
1161
        g_return_val_if_fail(file != NULL, NULL);
1162
1163
        if (!is_file_exist(file)) {
1164
                g_free(file);
1165
                file = procmsg_get_message_file(msginfo);
1166
                if (!file)
1167
                        return NULL;
1168
        }
1169
1170
        if ((fp = g_fopen(file, "rb")) == NULL) {
1171
                FILE_OP_ERROR(file, "fopen");
1172
                g_free(file);
1173
                return NULL;
1174
        }
1175
1176
        g_free(file);
1177
1178
        if (MSG_IS_QUEUED(msginfo->flags)) {
1179
                gchar buf[BUFFSIZE];
1180
1181
                while (fgets(buf, sizeof(buf), fp) != NULL)
1182
                        if (buf[0] == '\r' || buf[0] == '\n') break;
1183
        }
1184
1185
        return fp;
1186
}
1187
1188
static DecryptMessageFunc decrypt_message_func = NULL;
1189
1190
void procmsg_set_decrypt_message_func(DecryptMessageFunc func)
1191
{
1192
        decrypt_message_func = func;
1193
}
1194
1195
FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
1196
{
1197
        FILE *fp;
1198
1199
        if (decrypt_message_func)
1200
                return decrypt_message_func(msginfo, mimeinfo);
1201
1202
        *mimeinfo = NULL;
1203
        if ((fp = procmsg_open_message(msginfo)) == NULL)
1204
                return NULL;
1205
        *mimeinfo = procmime_scan_mime_header(fp);
1206
1207
        return fp;
1208
}
1209
1210
gboolean procmsg_msg_exist(MsgInfo *msginfo)
1211
{
1212
        gchar *path;
1213
        gboolean ret;
1214
1215
        if (!msginfo) return FALSE;
1216
1217
        path = folder_item_get_path(msginfo->folder);
1218
        change_dir(path);
1219
        ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
1220
        g_free(path);
1221
1222
        return ret;
1223
}
1224
1225
gboolean procmsg_trash_messages_exist(void)
1226
{
1227
        FolderItem *trash;
1228
        GList *cur;
1229
1230
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1231
                trash = FOLDER(cur->data)->trash;
1232
                if (trash && trash->total > 0)
1233
                        return TRUE;
1234
        }
1235
1236
        return FALSE;
1237
}
1238
1239
void procmsg_empty_trash(FolderItem *trash)
1240
{
1241
        if (trash && trash->total > 0) {
1242
                debug_print("Emptying messages in %s ...\n", trash->path);
1243
1244
                folder_item_remove_all_msg(trash);
1245
                procmsg_clear_cache(trash);
1246
                procmsg_clear_mark(trash);
1247
                trash->cache_dirty = FALSE;
1248
                trash->mark_dirty = FALSE;
1249
        }
1250
}
1251
1252
void procmsg_empty_all_trash(void)
1253
{
1254
        FolderItem *trash;
1255
        GList *cur;
1256
1257
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1258
                trash = FOLDER(cur->data)->trash;
1259
                procmsg_empty_trash(trash);
1260
        }
1261
}
1262
1263
static gboolean remove_all_cached_messages_func(GNode *node, gpointer data)
1264
{
1265
        FolderItem *item;
1266
        gchar *dir;
1267
1268
        g_return_val_if_fail(node->data != NULL, FALSE);
1269
1270
        item = FOLDER_ITEM(node->data);
1271
        if (!item->path || item->stype == F_VIRTUAL)
1272
                return FALSE;
1273
1274
        dir = folder_item_get_path(item);
1275
        if (is_dir_exist(dir)) {
1276
                debug_print("removing all cached messages in '%s' ...\n",
1277
                            item->path);
1278
                remove_all_numbered_files(dir);
1279
        }
1280
        g_free(dir);
1281
1282
        return FALSE;
1283
}
1284
1285
void procmsg_remove_all_cached_messages(Folder *folder)
1286
{
1287
        g_return_if_fail(folder != NULL);
1288
        g_return_if_fail(FOLDER_IS_REMOTE(folder));
1289
1290
        debug_print("Removing all caches in the mailbox '%s' ...\n",
1291
                    folder->name);
1292
1293
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1294
                        remove_all_cached_messages_func, NULL);
1295
}
1296
1297
gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file)
1298
{
1299
        gint num;
1300
        MsgFlags flag = {0, 0};
1301
1302
        debug_print("saving sent message...\n");
1303
1304
        if (!outbox)
1305
                outbox = folder_get_default_outbox();
1306
        g_return_val_if_fail(outbox != NULL, -1);
1307
1308
        folder_item_scan(outbox);
1309
        if ((num = folder_item_add_msg(outbox, file, &flag, FALSE)) < 0) {
1310
                g_warning("can't save message\n");
1311
                return -1;
1312
        }
1313
1314
        return 0;
1315
}
1316
1317
void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline,
1318
                           gboolean all_headers)
1319
{
1320
        static const gchar *def_cmd = "lpr %s";
1321
        static guint id = 0;
1322
        gchar *prtmp;
1323
        FILE *msgfp, *tmpfp, *prfp;
1324
        GPtrArray *headers;
1325
        gint i;
1326
        gchar buf[1024];
1327
1328
        g_return_if_fail(msginfo != NULL);
1329
1330
        if ((tmpfp = procmime_get_first_text_content
1331
                (msginfo, conv_get_locale_charset_str())) == NULL) {
1332
                g_warning("Can't get text part\n");
1333
                return;
1334
        }
1335
1336
        prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
1337
                                get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1338
1339
        if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1340
                FILE_OP_ERROR(prtmp, "fopen");
1341
                g_free(prtmp);
1342
                fclose(tmpfp);
1343
                return;
1344
        }
1345
1346
        if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
1347
                fclose(prfp);
1348
                g_free(prtmp);
1349
                fclose(tmpfp);
1350
                return;
1351
        }
1352
1353
        if (all_headers)
1354
                headers = procheader_get_header_array_asis(msgfp, NULL);
1355
        else
1356
                headers = procheader_get_header_array_for_display(msgfp, NULL);
1357
1358
        fclose(msgfp);
1359
1360
        for (i = 0; i < headers->len; i++) {
1361
                Header *hdr;
1362
                gchar *locale_str;
1363
                const gchar *body;
1364
1365
                hdr = g_ptr_array_index(headers, i);
1366
1367
                if (!g_ascii_strcasecmp(hdr->name, "Subject"))
1368
                        body = msginfo->subject;
1369
                else if (!g_ascii_strcasecmp(hdr->name, "From"))
1370
                        body = msginfo->from;
1371
                else if (!g_ascii_strcasecmp(hdr->name, "To"))
1372
                        body = msginfo->to;
1373
                else if (!g_ascii_strcasecmp(hdr->name, "Cc")) {
1374
                        unfold_line(hdr->body);
1375
                        body = hdr->body;
1376
                        while (g_ascii_isspace(*body))
1377
                                body++;
1378
                } else {
1379
                        body = hdr->body;
1380
                        while (g_ascii_isspace(*body))
1381
                                body++;
1382
                }
1383
1384
                locale_str = conv_codeset_strdup
1385
                        (body, CS_INTERNAL, conv_get_locale_charset_str());
1386
                fprintf(prfp, "%s: %s\n", hdr->name,
1387
                        locale_str ? locale_str : body);
1388
                g_free(locale_str);
1389
        }
1390
1391
        procheader_header_array_destroy(headers);
1392
1393
        fputc('\n', prfp);
1394
1395
        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1396
                fputs(buf, prfp);
1397
1398
        fclose(prfp);
1399
        fclose(tmpfp);
1400
1401
#ifdef G_OS_WIN32
1402
        if (canonicalize_file_replace(prtmp) < 0) {
1403
                g_free(prtmp);
1404
                return;
1405
        }
1406
#endif
1407
1408
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
1409
                g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1410
        else {
1411
                if (cmdline) {
1412
                        g_warning(_("Print command line is invalid: `%s'\n"),
1413
                                  cmdline);
1414
                        g_free(prtmp);
1415
                        return;
1416
                }
1417
1418
#ifdef G_OS_WIN32
1419
                execute_print_file(prtmp);
1420
                g_free(prtmp);
1421
                return;
1422
#else
1423
                g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1424
#endif
1425
        }
1426
1427
        g_free(prtmp);
1428
1429
        g_strchomp(buf);
1430
        if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1431
        system(buf);
1432
}
1433
1434
MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1435
{
1436
        MsgInfo *newmsginfo;
1437
1438
        if (msginfo == NULL) return NULL;
1439
1440
        newmsginfo = g_new0(MsgInfo, 1);
1441
1442
#define MEMBCOPY(mmb)        newmsginfo->mmb = msginfo->mmb
1443
#define MEMBDUP(mmb)        newmsginfo->mmb = msginfo->mmb ? \
1444
                        g_strdup(msginfo->mmb) : NULL
1445
1446
        MEMBCOPY(msgnum);
1447
        MEMBCOPY(size);
1448
        MEMBCOPY(mtime);
1449
        MEMBCOPY(date_t);
1450
1451
        MEMBCOPY(flags);
1452
1453
        MEMBDUP(fromname);
1454
1455
        MEMBDUP(date);
1456
        MEMBDUP(from);
1457
        MEMBDUP(to);
1458
        MEMBDUP(cc);
1459
        MEMBDUP(newsgroups);
1460
        MEMBDUP(subject);
1461
        MEMBDUP(msgid);
1462
        MEMBDUP(inreplyto);
1463
1464
        MEMBCOPY(folder);
1465
        MEMBCOPY(to_folder);
1466
1467
        MEMBDUP(xface);
1468
1469
        MEMBDUP(file_path);
1470
1471
        MEMBDUP(plaintext_file);
1472
        MEMBCOPY(decryption_failed);
1473
1474
        return newmsginfo;
1475
}
1476
1477
MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1478
{
1479
        MsgInfo *full_msginfo;
1480
        gchar *file;
1481
1482
        if (msginfo == NULL) return NULL;
1483
1484
        file = procmsg_get_message_file(msginfo);
1485
        if (!file) {
1486
                g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1487
                return NULL;
1488
        }
1489
1490
        full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE);
1491
        g_free(file);
1492
        if (!full_msginfo) return NULL;
1493
1494
        full_msginfo->msgnum = msginfo->msgnum;
1495
        full_msginfo->size = msginfo->size;
1496
        full_msginfo->mtime = msginfo->mtime;
1497
        full_msginfo->folder = msginfo->folder;
1498
        full_msginfo->to_folder = msginfo->to_folder;
1499
1500
        full_msginfo->file_path = g_strdup(msginfo->file_path);
1501
1502
        full_msginfo->plaintext_file = g_strdup(msginfo->plaintext_file);
1503
        full_msginfo->decryption_failed = msginfo->decryption_failed;
1504
1505
        return full_msginfo;
1506
}
1507
1508
gboolean procmsg_msginfo_equal(MsgInfo *msginfo_a, MsgInfo *msginfo_b)
1509
{
1510
        if (!msginfo_a || !msginfo_b)
1511
                return FALSE;
1512
1513
        if (msginfo_a == msginfo_b)
1514
                return TRUE;
1515
1516
        if (msginfo_a->folder == msginfo_b->folder &&
1517
            msginfo_a->msgnum == msginfo_b->msgnum &&
1518
            msginfo_a->size   == msginfo_b->size   &&
1519
            msginfo_a->mtime  == msginfo_b->mtime)
1520
                return TRUE;
1521
1522
        return FALSE;
1523
}
1524
1525
void procmsg_msginfo_free(MsgInfo *msginfo)
1526
{
1527
        if (msginfo == NULL) return;
1528
1529
        g_free(msginfo->xface);
1530
1531
        g_free(msginfo->fromname);
1532
1533
        g_free(msginfo->date);
1534
        g_free(msginfo->from);
1535
        g_free(msginfo->to);
1536
        g_free(msginfo->cc);
1537
        g_free(msginfo->newsgroups);
1538
        g_free(msginfo->subject);
1539
        g_free(msginfo->msgid);
1540
        g_free(msginfo->inreplyto);
1541
1542
        slist_free_strings(msginfo->references);
1543
        g_slist_free(msginfo->references);
1544
1545
        g_free(msginfo->file_path);
1546
1547
        g_free(msginfo->plaintext_file);
1548
1549
        g_free(msginfo);
1550
}
1551
1552
gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1553
{
1554
        const MsgInfo *msginfo1 = a;
1555
        const MsgInfo *msginfo2 = b;
1556
1557
        if (!msginfo1 || !msginfo2)
1558
                return 0;
1559
1560
        return msginfo1->msgnum - msginfo2->msgnum;
1561
}
1562
1563
#define CMP_FUNC_DEF(func_name, val)                                        \
1564
static gint func_name(gconstpointer a, gconstpointer b)                        \
1565
{                                                                        \
1566
        const MsgInfo *msginfo1 = a;                                        \
1567
        const MsgInfo *msginfo2 = b;                                        \
1568
        gint ret;                                                        \
1569
                                                                        \
1570
        if (!msginfo1 || !msginfo2)                                        \
1571
                return 0;                                                \
1572
                                                                        \
1573
        ret = (val);                                                        \
1574
        if (ret == 0)                                                        \
1575
                ret = msginfo1->date_t - msginfo2->date_t;                \
1576
                                                                        \
1577
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1578
}
1579
1580
CMP_FUNC_DEF(procmsg_cmp_by_mark,
1581
             MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
1582
CMP_FUNC_DEF(procmsg_cmp_by_unread,
1583
             MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
1584
CMP_FUNC_DEF(procmsg_cmp_by_mime,
1585
             MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags))
1586
CMP_FUNC_DEF(procmsg_cmp_by_label,
1587
             MSG_GET_COLORLABEL(msginfo1->flags) -
1588
             MSG_GET_COLORLABEL(msginfo2->flags))
1589
CMP_FUNC_DEF(procmsg_cmp_by_size, msginfo1->size - msginfo2->size)
1590
1591
#undef CMP_FUNC_DEF
1592
#define CMP_FUNC_DEF(func_name, val)                                        \
1593
static gint func_name(gconstpointer a, gconstpointer b)                        \
1594
{                                                                        \
1595
        const MsgInfo *msginfo1 = a;                                        \
1596
        const MsgInfo *msginfo2 = b;                                        \
1597
                                                                        \
1598
        if (!msginfo1 || !msginfo2)                                        \
1599
                return 0;                                                \
1600
                                                                        \
1601
        return (val) * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1602
}
1603
1604
CMP_FUNC_DEF(procmsg_cmp_by_number, msginfo1->msgnum - msginfo2->msgnum)
1605
CMP_FUNC_DEF(procmsg_cmp_by_date, msginfo1->date_t - msginfo2->date_t)
1606
1607
#undef CMP_FUNC_DEF
1608
#define CMP_FUNC_DEF(func_name, var_name)                                \
1609
static gint func_name(gconstpointer a, gconstpointer b)                        \
1610
{                                                                        \
1611
        const MsgInfo *msginfo1 = a;                                        \
1612
        const MsgInfo *msginfo2 = b;                                        \
1613
        gint ret;                                                        \
1614
                                                                        \
1615
        if (!msginfo1->var_name)                                        \
1616
                return (msginfo2->var_name != NULL) *                        \
1617
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);\
1618
        if (!msginfo2->var_name)                                        \
1619
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1620
                                                                        \
1621
        ret = g_ascii_strcasecmp                                        \
1622
                (msginfo1->var_name, msginfo2->var_name);                \
1623
        if (ret == 0)                                                        \
1624
                ret = msginfo1->date_t - msginfo2->date_t;                \
1625
                                                                        \
1626
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1627
}
1628
1629
CMP_FUNC_DEF(procmsg_cmp_by_from, fromname)
1630
CMP_FUNC_DEF(procmsg_cmp_by_to, to)
1631
1632
#undef CMP_FUNC_DEF
1633
1634
static gint procmsg_cmp_by_subject(gconstpointer a, gconstpointer b)
1635
{
1636
        const MsgInfo *msginfo1 = a;
1637
        const MsgInfo *msginfo2 = b;
1638
        gint ret;
1639
1640
        if (!msginfo1->subject)
1641
                return (msginfo2->subject != NULL) *
1642
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);
1643
        if (!msginfo2->subject)
1644
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);
1645
1646
        ret = subject_compare_for_sort(msginfo1->subject, msginfo2->subject);
1647
        if (ret == 0)
1648
                ret = msginfo1->date_t - msginfo2->date_t;
1649
1650
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);
1651
}