Statistics
| Revision:

root / libsylph / procmsg.c @ 548

History | View | Annotate | Download (32.4 kB)

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