Statistics
| Revision:

root / libsylph / procmsg.c @ 2164

History | View | Annotate | Download (41.5 KB)

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

    
20
#include "defs.h"
21

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

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

    
37
static void mark_sum_func                        (gpointer         key,
38
                                                 gpointer         value,
39
                                                 gpointer         data);
40

    
41
static GHashTable *procmsg_read_mark_file        (FolderItem        *item);
42
static void procmsg_write_mark_file                (FolderItem        *item,
43
                                                 GHashTable        *mark_table);
44

    
45
static FILE *procmsg_open_cache_file_with_buffer(FolderItem        *item,
46
                                                 DataOpenMode         mode,
47
                                                 gchar                *buf,
48
                                                 size_t                 buf_size);
49

    
50
static gint procmsg_cmp_by_mark                        (gconstpointer         a,
51
                                                 gconstpointer         b);
52
static gint procmsg_cmp_by_unread                (gconstpointer         a,
53
                                                 gconstpointer         b);
54
static gint procmsg_cmp_by_mime                        (gconstpointer         a,
55
                                                 gconstpointer         b);
56
static gint procmsg_cmp_by_label                (gconstpointer         a,
57
                                                 gconstpointer         b);
58
static gint procmsg_cmp_by_number                (gconstpointer         a,
59
                                                 gconstpointer         b);
60
static gint procmsg_cmp_by_size                        (gconstpointer         a,
61
                                                 gconstpointer         b);
62
static gint procmsg_cmp_by_date                        (gconstpointer         a,
63
                                                 gconstpointer         b);
64
static gint procmsg_cmp_by_from                        (gconstpointer         a,
65
                                                 gconstpointer         b);
66
static gint procmsg_cmp_by_to                        (gconstpointer         a,
67
                                                 gconstpointer         b);
68
static gint procmsg_cmp_by_subject                (gconstpointer         a,
69
                                                 gconstpointer         b);
70

    
71

    
72
GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
73
{
74
        GHashTable *msg_table;
75

    
76
        if (mlist == NULL) return NULL;
77

    
78
        msg_table = g_hash_table_new(NULL, g_direct_equal);
79
        procmsg_msg_hash_table_append(msg_table, mlist);
80

    
81
        return msg_table;
82
}
83

    
84
void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
85
{
86
        GSList *cur;
87
        MsgInfo *msginfo;
88

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

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

    
94
                g_hash_table_insert(msg_table,
95
                                    GUINT_TO_POINTER(msginfo->msgnum),
96
                                    msginfo);
97
        }
98
}
99

    
100
GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
101
{
102
        GHashTable *msg_table;
103
        GSList *cur;
104
        MsgInfo *msginfo;
105

    
106
        if (mlist == NULL) return NULL;
107

    
108
        msg_table = g_hash_table_new(NULL, g_direct_equal);
109

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

    
115
        return msg_table;
116
}
117

    
118
gint procmsg_read_cache_data_str(FILE *fp, gchar **str)
119
{
120
        gchar buf[BUFFSIZE];
121
        gint ret = 0;
122
        guint32 len;
123

    
124
        if (fread(&len, sizeof(len), 1, fp) == 1) {
125
                if (len > G_MAXINT)
126
                        ret = -1;
127
                else {
128
                        gchar *tmp = NULL;
129

    
130
                        while (len > 0) {
131
                                size_t size = MIN(len, BUFFSIZE - 1);
132

    
133
                                if (fread(buf, size, 1, fp) != 1) {
134
                                        ret = -1;
135
                                        if (tmp) g_free(tmp);
136
                                        *str = NULL;
137
                                        break;
138
                                }
139

    
140
                                buf[size] = '\0';
141
                                if (tmp) {
142
                                        *str = g_strconcat(tmp, buf, NULL);
143
                                        g_free(tmp);
144
                                        tmp = *str;
145
                                } else
146
                                        tmp = *str = g_strdup(buf);
147

    
148
                                len -= size;
149
                        }
150
                }
151
        } else
152
                ret = -1;
153

    
154
        return ret;
155
}
156

    
157
#define READ_CACHE_DATA(data, fp)                                \
158
{                                                                \
159
        if (procmsg_read_cache_data_str(fp, &data) < 0) {        \
160
                g_warning("Cache data is corrupted\n");                \
161
                procmsg_msginfo_free(msginfo);                        \
162
                procmsg_msg_list_free(mlist);                        \
163
                fclose(fp);                                        \
164
                return NULL;                                        \
165
        }                                                        \
166
}
167

    
168
#define READ_CACHE_DATA_INT(n, fp)                                \
169
{                                                                \
170
        guint32 idata;                                                \
171
                                                                \
172
        if (fread(&idata, sizeof(idata), 1, fp) != 1) {                \
173
                g_warning("Cache data is corrupted\n");                \
174
                procmsg_msginfo_free(msginfo);                        \
175
                procmsg_msg_list_free(mlist);                        \
176
                fclose(fp);                                        \
177
                return NULL;                                        \
178
        } else                                                        \
179
                n = idata;                                        \
180
}
181

    
182
GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file)
183
{
184
        GSList *mlist = NULL;
185
        GSList *last = NULL;
186
        FILE *fp;
187
        MsgInfo *msginfo;
188
        MsgFlags default_flags;
189
        gchar file_buf[BUFFSIZE];
190
        guint32 num;
191
        guint refnum;
192
        FolderType type;
193

    
194
        g_return_val_if_fail(item != NULL, NULL);
195
        g_return_val_if_fail(item->folder != NULL, NULL);
196
        type = FOLDER_TYPE(item->folder);
197

    
198
        default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
199
        default_flags.tmp_flags = 0;
200
        if (type == F_MH || type == F_IMAP) {
201
                if (item->stype == F_QUEUE) {
202
                        MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
203
                } else if (item->stype == F_DRAFT) {
204
                        MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
205
                }
206
        }
207
        if (type == F_IMAP) {
208
                MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
209
        } else if (type == F_NEWS) {
210
                MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
211
        }
212

    
213
        if (type == F_MH) {
214
                gchar *path;
215

    
216
                path = folder_item_get_path(item);
217
                if (change_dir(path) < 0) {
218
                        g_free(path);
219
                        return NULL;
220
                }
221
                g_free(path);
222
        }
223

    
224
        if ((fp = procmsg_open_cache_file_with_buffer
225
                (item, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
226
                item->cache_dirty = TRUE;
227
                return NULL;
228
        }
229

    
230
        debug_print("Reading summary cache...");
231

    
232
        while (fread(&num, sizeof(num), 1, fp) == 1) {
233
                msginfo = g_new0(MsgInfo, 1);
234
                msginfo->msgnum = num;
235
                READ_CACHE_DATA_INT(msginfo->size, fp);
236
                READ_CACHE_DATA_INT(msginfo->mtime, fp);
237
                READ_CACHE_DATA_INT(msginfo->date_t, fp);
238
                READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
239

    
240
                READ_CACHE_DATA(msginfo->fromname, fp);
241

    
242
                READ_CACHE_DATA(msginfo->date, fp);
243
                READ_CACHE_DATA(msginfo->from, fp);
244
                READ_CACHE_DATA(msginfo->to, fp);
245
                READ_CACHE_DATA(msginfo->newsgroups, fp);
246
                READ_CACHE_DATA(msginfo->subject, fp);
247
                READ_CACHE_DATA(msginfo->msgid, fp);
248
                READ_CACHE_DATA(msginfo->inreplyto, fp);
249

    
250
                READ_CACHE_DATA_INT(refnum, fp);
251
                for (; refnum != 0; refnum--) {
252
                        gchar *ref;
253

    
254
                        READ_CACHE_DATA(ref, fp);
255
                        msginfo->references =
256
                                g_slist_prepend(msginfo->references, ref);
257
                }
258
                if (msginfo->references)
259
                        msginfo->references =
260
                                g_slist_reverse(msginfo->references);
261

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

    
265
                /* if the message file doesn't exist or is changed,
266
                   don't add the data */
267
                if ((type == F_MH && scan_file &&
268
                     folder_item_is_msg_changed(item, msginfo)) || num == 0) {
269
                        procmsg_msginfo_free(msginfo);
270
                        item->cache_dirty = TRUE;
271
                } else {
272
                        msginfo->folder = item;
273

    
274
                        if (!mlist)
275
                                last = mlist = g_slist_append(NULL, msginfo);
276
                        else {
277
                                last = g_slist_append(last, msginfo);
278
                                last = last->next;
279
                        }
280
                }
281
        }
282

    
283
        fclose(fp);
284

    
285
        debug_print("done.\n");
286

    
287
        return mlist;
288
}
289

    
290
#undef READ_CACHE_DATA
291
#undef READ_CACHE_DATA_INT
292

    
293
static void mark_unset_new_func(gpointer key, gpointer value, gpointer data)
294
{
295
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW);
296
}
297

    
298
void procmsg_set_flags(GSList *mlist, FolderItem *item)
299
{
300
        GSList *cur;
301
        gint new = 0, unread = 0, total = 0;
302
        gint lastnum = 0;
303
        gint unflagged = 0;
304
        gboolean mark_queue_exist;
305
        MsgInfo *msginfo;
306
        GHashTable *mark_table;
307
        MsgFlags *flags;
308

    
309
        g_return_if_fail(item != NULL);
310
        g_return_if_fail(item->folder != NULL);
311

    
312
        debug_print("Marking the messages...\n");
313

    
314
        mark_queue_exist = (item->mark_queue != NULL);
315
        mark_table = procmsg_read_mark_file(item);
316
        if (!mark_table) {
317
                item->new = item->unread = item->total = g_slist_length(mlist);
318
                item->updated = TRUE;
319
                item->mark_dirty = TRUE;
320
                return;
321
        }
322

    
323
        /* unset new flags if new (unflagged) messages exist */
324
        if (!mark_queue_exist) {
325
                for (cur = mlist; cur != NULL; cur = cur->next) {
326
                        msginfo = (MsgInfo *)cur->data;
327
                        flags = g_hash_table_lookup
328
                                (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
329
                        if (!flags) {
330
                                g_hash_table_foreach(mark_table,
331
                                                     mark_unset_new_func, NULL);
332
                                item->mark_dirty = TRUE;
333
                                break;
334
                        }
335
                }
336
        }
337

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

    
341
                if (lastnum < msginfo->msgnum)
342
                        lastnum = msginfo->msgnum;
343

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

    
347
                if (flags != NULL) {
348
                        /* add the permanent flags only */
349
                        msginfo->flags.perm_flags = flags->perm_flags;
350
                        if (MSG_IS_NEW(*flags))
351
                                ++new;
352
                        if (MSG_IS_UNREAD(*flags))
353
                                ++unread;
354
                        if (FOLDER_TYPE(item->folder) == F_IMAP) {
355
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
356
                        } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
357
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
358
                        }
359
                } else {
360
                        ++unflagged;
361
                        ++new;
362
                        ++unread;
363
                }
364

    
365
                ++total;
366
        }
367

    
368
        item->new = new;
369
        item->unread = unread;
370
        item->total = total;
371
        item->unmarked_num = unflagged;
372
        item->last_num = lastnum;
373
        item->updated = TRUE;
374

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

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

    
381
        hash_free_value_mem(mark_table);
382
        g_hash_table_destroy(mark_table);
383
}
384

    
385
static void mark_all_read_func(gpointer key, gpointer value, gpointer data)
386
{
387
        MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW|MSG_UNREAD);
388
}
389

    
390
void procmsg_mark_all_read(FolderItem *item)
391
{
392
        GHashTable *mark_table;
393

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

    
396
        mark_table = procmsg_read_mark_file(item);
397
        if (mark_table) {
398
                g_hash_table_foreach(mark_table, mark_all_read_func, NULL);
399
                procmsg_write_mark_file(item, mark_table);
400
                hash_free_value_mem(mark_table);
401
                g_hash_table_destroy(mark_table);
402
        }
403

    
404
        if (item->mark_queue) {
405
                GSList *cur;
406
                MsgInfo *msginfo;
407

    
408
                for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
409
                        msginfo = (MsgInfo *)cur->data;
410
                        MSG_UNSET_PERM_FLAGS
411
                                (msginfo->flags, MSG_NEW|MSG_UNREAD);
412
                }
413
                item->mark_dirty = TRUE;
414
        }
415

    
416
        item->new = item->unread = 0;
417
}
418

    
419
static FolderSortType cmp_func_sort_type;
420

    
421
GSList *procmsg_sort_msg_list(GSList *mlist, FolderSortKey sort_key,
422
                              FolderSortType sort_type)
423
{
424
        GCompareFunc cmp_func;
425

    
426
        switch (sort_key) {
427
        case SORT_BY_MARK:
428
                cmp_func = procmsg_cmp_by_mark; break;
429
        case SORT_BY_UNREAD:
430
                cmp_func = procmsg_cmp_by_unread; break;
431
        case SORT_BY_MIME:
432
                cmp_func = procmsg_cmp_by_mime; break;
433
        case SORT_BY_LABEL:
434
                cmp_func = procmsg_cmp_by_label; break;
435
        case SORT_BY_NUMBER:
436
                cmp_func = procmsg_cmp_by_number; break;
437
        case SORT_BY_SIZE:
438
                cmp_func = procmsg_cmp_by_size; break;
439
        case SORT_BY_DATE:
440
                cmp_func = procmsg_cmp_by_date; break;
441
        case SORT_BY_FROM:
442
                cmp_func = procmsg_cmp_by_from; break;
443
        case SORT_BY_SUBJECT:
444
                cmp_func = procmsg_cmp_by_subject; break;
445
        case SORT_BY_TO:
446
                cmp_func = procmsg_cmp_by_to; break;
447
        default:
448
                return mlist;
449
        }
450

    
451
        cmp_func_sort_type = sort_type;
452

    
453
        mlist = g_slist_sort(mlist, cmp_func);
454

    
455
        return mlist;
456
}
457

    
458
gint procmsg_get_last_num_in_msg_list(GSList *mlist)
459
{
460
        GSList *cur;
461
        MsgInfo *msginfo;
462
        gint last = 0;
463

    
464
        for (cur = mlist; cur != NULL; cur = cur->next) {
465
                msginfo = (MsgInfo *)cur->data;
466
                if (msginfo && msginfo->msgnum > last)
467
                        last = msginfo->msgnum;
468
        }
469

    
470
        return last;
471
}
472

    
473
void procmsg_msg_list_free(GSList *mlist)
474
{
475
        GSList *cur;
476
        MsgInfo *msginfo;
477

    
478
        for (cur = mlist; cur != NULL; cur = cur->next) {
479
                msginfo = (MsgInfo *)cur->data;
480
                procmsg_msginfo_free(msginfo);
481
        }
482
        g_slist_free(mlist);
483
}
484

    
485
void procmsg_write_cache(MsgInfo *msginfo, FILE *fp)
486
{
487
        MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
488
        GSList *cur;
489

    
490
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
491
        WRITE_CACHE_DATA_INT(msginfo->size, fp);
492
        WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
493
        WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
494
        WRITE_CACHE_DATA_INT(flags, fp);
495

    
496
        WRITE_CACHE_DATA(msginfo->fromname, fp);
497

    
498
        WRITE_CACHE_DATA(msginfo->date, fp);
499
        WRITE_CACHE_DATA(msginfo->from, fp);
500
        WRITE_CACHE_DATA(msginfo->to, fp);
501
        WRITE_CACHE_DATA(msginfo->newsgroups, fp);
502
        WRITE_CACHE_DATA(msginfo->subject, fp);
503
        WRITE_CACHE_DATA(msginfo->msgid, fp);
504
        WRITE_CACHE_DATA(msginfo->inreplyto, fp);
505

    
506
        WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
507
        for (cur = msginfo->references; cur != NULL; cur = cur->next) {
508
                WRITE_CACHE_DATA((gchar *)cur->data, fp);
509
        }
510
}
511

    
512
void procmsg_write_flags(MsgInfo *msginfo, FILE *fp)
513
{
514
        MsgPermFlags flags = msginfo->flags.perm_flags;
515

    
516
        WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
517
        WRITE_CACHE_DATA_INT(flags, fp);
518
}
519

    
520
void procmsg_write_cache_list(FolderItem *item, GSList *mlist)
521
{
522
        FILE *fp;
523
        GSList *cur;
524

    
525
        g_return_if_fail(item != NULL);
526

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

    
529
        fp = procmsg_open_cache_file(item, DATA_WRITE);
530
        if (fp == NULL)
531
                return;
532

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

    
538
        fclose(fp);
539
        item->cache_dirty = FALSE;
540
}
541

    
542
void procmsg_write_flags_list(FolderItem *item, GSList *mlist)
543
{
544
        FILE *fp;
545
        GSList *cur;
546

    
547
        g_return_if_fail(item != NULL);
548

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

    
551
        fp = procmsg_open_mark_file(item, DATA_WRITE);
552
        if (fp == NULL)
553
                return;
554

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

    
560
        if (item->mark_queue)
561
                procmsg_flush_mark_queue(item, fp);
562

    
563
        fclose(fp);
564
        item->mark_dirty = FALSE;
565
}
566

    
567
static gint cmp_by_item(gconstpointer a, gconstpointer b)
568
{
569
        const MsgInfo *msginfo1 = a;
570
        const MsgInfo *msginfo2 = b;
571

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

    
575
        return msginfo1->folder - msginfo2->folder;
576
}
577

    
578
void procmsg_write_flags_for_multiple_folders(GSList *mlist)
579
{
580
        GSList *tmp_list, *cur;
581
        FolderItem *prev_item = NULL;
582
        FILE *fp = NULL;
583

    
584
        if (!mlist)
585
                return;
586

    
587
        tmp_list = g_slist_copy(mlist);
588
        tmp_list = g_slist_sort(tmp_list, cmp_by_item);
589

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

    
594
                if (prev_item != item) {
595
                        if (fp)
596
                                fclose(fp);
597
                        fp = procmsg_open_mark_file(item, DATA_APPEND);
598
                        if (!fp) {
599
                                g_warning("can't open mark file\n");
600
                                break;
601
                        }
602
                        item->updated = TRUE;
603
                }
604
                procmsg_write_flags(msginfo, fp);
605
                prev_item = item;
606
        }
607

    
608
        if (fp)
609
                fclose(fp);
610
        g_slist_free(tmp_list);
611
}
612

    
613
void procmsg_flush_mark_queue(FolderItem *item, FILE *fp)
614
{
615
        MsgInfo *flaginfo;
616

    
617
        g_return_if_fail(item != NULL);
618
        g_return_if_fail(fp != NULL);
619

    
620
        if (item->mark_queue)
621
                debug_print("flushing mark_queue...\n");
622

    
623
        while (item->mark_queue != NULL) {
624
                flaginfo = (MsgInfo *)item->mark_queue->data;
625
                procmsg_write_flags(flaginfo, fp);
626
                procmsg_msginfo_free(flaginfo);
627
                item->mark_queue = g_slist_remove(item->mark_queue, flaginfo);
628
        }
629
}
630

    
631
void procmsg_add_mark_queue(FolderItem *item, gint num, MsgFlags flags)
632
{
633
        MsgInfo *queue_msginfo;
634

    
635
        queue_msginfo = g_new0(MsgInfo, 1);
636
        queue_msginfo->msgnum = num;
637
        queue_msginfo->flags = flags;
638
        item->mark_queue = g_slist_append
639
                (item->mark_queue, queue_msginfo);
640
        return;
641
}
642

    
643
void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags)
644
{
645
        FILE *fp;
646
        MsgInfo msginfo;
647

    
648
        g_return_if_fail(item != NULL);
649

    
650
        if (item->opened) {
651
                procmsg_add_mark_queue(item, num, flags);
652
                return;
653
        }
654

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

    
660
        msginfo.msgnum = num;
661
        msginfo.flags = flags;
662

    
663
        procmsg_write_flags(&msginfo, fp);
664
        fclose(fp);
665
}
666

    
667
struct MarkSum {
668
        gint *new;
669
        gint *unread;
670
        gint *total;
671
        gint *min;
672
        gint *max;
673
        gint first;
674
};
675

    
676
static void mark_sum_func(gpointer key, gpointer value, gpointer data)
677
{
678
        MsgFlags *flags = value;
679
        gint num = GPOINTER_TO_INT(key);
680
        struct MarkSum *marksum = data;
681

    
682
        if (marksum->first <= num) {
683
                if (MSG_IS_NEW(*flags)) (*marksum->new)++;
684
                if (MSG_IS_UNREAD(*flags)) (*marksum->unread)++;
685
                if (num > *marksum->max) *marksum->max = num;
686
                if (num < *marksum->min || *marksum->min == 0) *marksum->min = num;
687
                (*marksum->total)++;
688
        }
689

    
690
        g_free(flags);
691
}
692

    
693
void procmsg_get_mark_sum(FolderItem *item,
694
                          gint *new, gint *unread, gint *total,
695
                          gint *min, gint *max,
696
                          gint first)
697
{
698
        GHashTable *mark_table;
699
        struct MarkSum marksum;
700

    
701
        *new = *unread = *total = *min = *max = 0;
702
        marksum.new    = new;
703
        marksum.unread = unread;
704
        marksum.total  = total;
705
        marksum.min    = min;
706
        marksum.max    = max;
707
        marksum.first  = first;
708

    
709
        mark_table = procmsg_read_mark_file(item);
710

    
711
        if (mark_table) {
712
                g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
713
                g_hash_table_destroy(mark_table);
714
        }
715
}
716

    
717
static GHashTable *procmsg_read_mark_file(FolderItem *item)
718
{
719
        FILE *fp;
720
        GHashTable *mark_table = NULL;
721
        guint32 idata;
722
        guint num;
723
        MsgFlags *flags;
724
        MsgPermFlags perm_flags;
725
        GSList *cur;
726

    
727
        if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL)
728
                return NULL;
729

    
730
        mark_table = g_hash_table_new(NULL, g_direct_equal);
731

    
732
        while (fread(&idata, sizeof(idata), 1, fp) == 1) {
733
                num = idata;
734
                if (fread(&idata, sizeof(idata), 1, fp) != 1) break;
735
                perm_flags = idata;
736

    
737
                flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num));
738
                if (flags != NULL)
739
                        g_free(flags);
740

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

    
744
                g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
745
        }
746

    
747
        fclose(fp);
748

    
749
        if (item->mark_queue) {
750
                g_hash_table_foreach(mark_table, mark_unset_new_func, NULL);
751
                item->mark_dirty = TRUE;
752
        }
753

    
754
        for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
755
                MsgInfo *msginfo = (MsgInfo *)cur->data;
756

    
757
                flags = g_hash_table_lookup(mark_table,
758
                                            GUINT_TO_POINTER(msginfo->msgnum));
759
                if (flags != NULL)
760
                        g_free(flags);
761

    
762
                flags = g_new0(MsgFlags, 1);
763
                flags->perm_flags = msginfo->flags.perm_flags;
764

    
765
                g_hash_table_insert(mark_table,
766
                                    GUINT_TO_POINTER(msginfo->msgnum), flags);
767
                                    
768
        }
769

    
770
        if (item->mark_queue && !item->opened) {
771
                procmsg_write_mark_file(item, mark_table);
772
                procmsg_msg_list_free(item->mark_queue);
773
                item->mark_queue = NULL;
774
                item->mark_dirty = FALSE;
775
        }
776

    
777
        return mark_table;
778
}
779

    
780
static void write_mark_func(gpointer key, gpointer value, gpointer data)
781
{
782
        MsgInfo msginfo;
783

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

    
789
static void procmsg_write_mark_file(FolderItem *item, GHashTable *mark_table)
790
{
791
        FILE *fp;
792

    
793
        if ((fp = procmsg_open_mark_file(item, DATA_WRITE)) == NULL) {
794
                g_warning("procmsg_write_mark_file: cannot open mark file.");
795
                return;
796
        }
797
        g_hash_table_foreach(mark_table, write_mark_func, fp);
798
        fclose(fp);
799
}
800

    
801
FILE *procmsg_open_data_file(const gchar *file, guint version,
802
                             DataOpenMode mode, gchar *buf, size_t buf_size)
803
{
804
        FILE *fp;
805
        guint32 data_ver = 0;
806

    
807
        g_return_val_if_fail(file != NULL, NULL);
808

    
809
        if (mode == DATA_WRITE) {
810
                if ((fp = g_fopen(file, "wb")) == NULL) {
811
                        if (errno == EACCES) {
812
                                change_file_mode_rw(NULL, file);
813
                                if ((fp = g_fopen(file, "wb")) == NULL) {
814
                                        FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
815
                                        return NULL;
816
                                }
817
                        } else {
818
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
819
                                return NULL;
820
                        }
821
                }
822
                if (change_file_mode_rw(fp, file) < 0)
823
                        FILE_OP_ERROR(file, "chmod");
824

    
825
                WRITE_CACHE_DATA_INT(version, fp);
826
                return fp;
827
        }
828

    
829
        /* check version */
830
        if ((fp = g_fopen(file, "rb")) == NULL) {
831
                if (errno == EACCES) {
832
                        change_file_mode_rw(NULL, file);
833
                        if ((fp = g_fopen(file, "rb")) == NULL) {
834
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
835
                        }
836
                } else {
837
                        debug_print("Mark/Cache file '%s' not found\n", file);
838
                }
839
        }
840

    
841
        if (fp) {
842
                if (buf && buf_size > 0)
843
                        setvbuf(fp, buf, _IOFBF, buf_size);
844
                if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1) {
845
                        g_warning("%s: cannot read mark/cache file (truncated?)\n", file);
846
                        fclose(fp);
847
                        fp = NULL;
848
                } else if (version != data_ver) {
849
                        g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n",
850
                                  file, data_ver, version);
851
                        fclose(fp);
852
                        fp = NULL;
853
                }
854
        }
855

    
856
        if (mode == DATA_READ)
857
                return fp;
858

    
859
        if (fp) {
860
                /* reopen with append mode */
861
                fclose(fp);
862
                if ((fp = g_fopen(file, "ab")) == NULL) {
863
                        if (errno == EACCES) {
864
                                change_file_mode_rw(NULL, file);
865
                                if ((fp = g_fopen(file, "ab")) == NULL) {
866
                                        FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
867
                                }
868
                        } else {
869
                                FILE_OP_ERROR(file, "procmsg_open_data_file: fopen");
870
                        }
871
                }
872
        } else {
873
                /* open with overwrite mode if mark file doesn't exist or
874
                   version is different */
875
                fp = procmsg_open_data_file(file, version, DATA_WRITE, buf,
876
                                            buf_size);
877
        }
878

    
879
        return fp;
880
}
881

    
882
static FILE *procmsg_open_cache_file_with_buffer(FolderItem *item,
883
                                                 DataOpenMode mode,
884
                                                 gchar *buf, size_t buf_size)
885
{
886
        gchar *cachefile;
887
        FILE *fp;
888

    
889
        cachefile = folder_item_get_cache_file(item);
890
        fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, buf,
891
                                    buf_size);
892
        g_free(cachefile);
893

    
894
        return fp;
895
}
896

    
897
FILE *procmsg_open_cache_file(FolderItem *item, DataOpenMode mode)
898
{
899
        gchar *cachefile;
900
        FILE *fp;
901

    
902
        cachefile = folder_item_get_cache_file(item);
903
        fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, NULL, 0);
904
        g_free(cachefile);
905

    
906
        return fp;
907
}
908

    
909
FILE *procmsg_open_mark_file(FolderItem *item, DataOpenMode mode)
910
{
911
        gchar *markfile;
912
        FILE *fp;
913

    
914
        markfile = folder_item_get_mark_file(item);
915
        fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0);
916
        g_free(markfile);
917

    
918
        return fp;
919
}
920

    
921
void procmsg_clear_cache(FolderItem *item)
922
{
923
        FILE *fp;
924

    
925
        fp = procmsg_open_cache_file(item, DATA_WRITE);
926
        if (fp)
927
                fclose(fp);
928
}
929

    
930
void procmsg_clear_mark(FolderItem *item)
931
{
932
        FILE *fp;
933

    
934
        fp = procmsg_open_mark_file(item, DATA_WRITE);
935
        if (fp)
936
                fclose(fp);
937
}
938

    
939
/* return the reversed thread tree */
940
GNode *procmsg_get_thread_tree(GSList *mlist)
941
{
942
        GNode *root, *parent, *node, *next;
943
        GHashTable *table;
944
        MsgInfo *msginfo;
945
        const gchar *msgid;
946
        GSList *reflist;
947

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

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

    
955
                /* only look for the real parent first */
956
                if (msginfo->inreplyto) {
957
                        parent = g_hash_table_lookup(table, msginfo->inreplyto);
958
                        if (parent == NULL)
959
                                parent = root;
960
                }
961

    
962
                node = g_node_insert_data_before
963
                        (parent, parent == root ? parent->children : NULL,
964
                         msginfo);
965
                if ((msgid = msginfo->msgid) &&
966
                    g_hash_table_lookup(table, msgid) == NULL)
967
                        g_hash_table_insert(table, (gchar *)msgid, node);
968
        }
969

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

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

    
979
                /* try looking for the indirect parent */
980
                if (!parent && msginfo->references) {
981
                        for (reflist = msginfo->references;
982
                             reflist != NULL; reflist = reflist->next)
983
                                if ((parent = g_hash_table_lookup
984
                                        (table, reflist->data)) != NULL)
985
                                        break;
986
                }
987

    
988
                /* node should not be the parent, and node should not
989
                   be an ancestor of parent (circular reference) */
990
                if (parent && parent != node &&
991
                    !g_node_is_ancestor(node, parent)) {
992
                        g_node_unlink(node);
993
                        g_node_insert_before
994
                                (parent, parent->children, node);
995
                }
996
                node = next;
997
        }
998

    
999
        g_hash_table_destroy(table);
1000

    
1001
        return root;
1002
}
1003

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

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

    
1012
        return FALSE;
1013
}
1014

    
1015
guint procmsg_get_thread_date(GNode *node)
1016
{
1017
        guint tdate = 0;
1018

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

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

    
1025
        return tdate;
1026
}
1027

    
1028
gint procmsg_move_messages(GSList *mlist)
1029
{
1030
        GSList *cur, *movelist = NULL;
1031
        MsgInfo *msginfo;
1032
        FolderItem *dest = NULL;
1033
        GHashTable *hash;
1034
        gint val = 0;
1035

    
1036
        if (!mlist) return 0;
1037

    
1038
        hash = procmsg_to_folder_hash_table_create(mlist);
1039
        folder_item_scan_foreach(hash);
1040
        g_hash_table_destroy(hash);
1041

    
1042
        for (cur = mlist; cur != NULL; cur = cur->next) {
1043
                msginfo = (MsgInfo *)cur->data;
1044
                if (!dest) {
1045
                        dest = msginfo->to_folder;
1046
                        movelist = g_slist_append(movelist, msginfo);
1047
                } else if (dest == msginfo->to_folder) {
1048
                        movelist = g_slist_append(movelist, msginfo);
1049
                } else {
1050
                        val = folder_item_move_msgs(dest, movelist);
1051
                        g_slist_free(movelist);
1052
                        movelist = NULL;
1053
                        if (val == -1)
1054
                                return val;
1055
                        dest = msginfo->to_folder;
1056
                        movelist = g_slist_append(movelist, msginfo);
1057
                }
1058
        }
1059

    
1060
        if (movelist) {
1061
                val = folder_item_move_msgs(dest, movelist);
1062
                g_slist_free(movelist);
1063
        }
1064

    
1065
        return val == -1 ? -1 : 0;
1066
}
1067

    
1068
gint procmsg_copy_messages(GSList *mlist)
1069
{
1070
        GSList *cur, *copylist = NULL;
1071
        MsgInfo *msginfo;
1072
        FolderItem *dest = NULL;
1073
        GHashTable *hash;
1074
        gint val = 0;
1075

    
1076
        if (!mlist) return 0;
1077

    
1078
        hash = procmsg_to_folder_hash_table_create(mlist);
1079
        folder_item_scan_foreach(hash);
1080
        g_hash_table_destroy(hash);
1081

    
1082
        for (cur = mlist; cur != NULL; cur = cur->next) {
1083
                msginfo = (MsgInfo *)cur->data;
1084
                if (!dest) {
1085
                        dest = msginfo->to_folder;
1086
                        copylist = g_slist_append(copylist, msginfo);
1087
                } else if (dest == msginfo->to_folder) {
1088
                        copylist = g_slist_append(copylist, msginfo);
1089
                } else {
1090
                        val = folder_item_copy_msgs(dest, copylist);
1091
                        g_slist_free(copylist);
1092
                        copylist = NULL;
1093
                        if (val == -1)
1094
                                return val;
1095
                        dest = msginfo->to_folder;
1096
                        copylist = g_slist_append(copylist, msginfo);
1097
                }
1098
        }
1099

    
1100
        if (copylist) {
1101
                val = folder_item_copy_msgs(dest, copylist);
1102
                g_slist_free(copylist);
1103
        }
1104

    
1105
        return val == -1 ? -1 : 0;
1106
}
1107

    
1108
gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
1109
{
1110
        gchar *path, *file;
1111

    
1112
        g_return_val_if_fail(msginfo != NULL, NULL);
1113

    
1114
        if (msginfo->encinfo && msginfo->encinfo->plaintext_file)
1115
                file = g_strdup(msginfo->encinfo->plaintext_file);
1116
        else if (msginfo->file_path)
1117
                return g_strdup(msginfo->file_path);
1118
        else {
1119
                path = folder_item_get_path(msginfo->folder);
1120
                file = g_strconcat(path, G_DIR_SEPARATOR_S,
1121
                                   itos(msginfo->msgnum), NULL);
1122
                g_free(path);
1123
        }
1124

    
1125
        return file;
1126
}
1127

    
1128
gchar *procmsg_get_message_file(MsgInfo *msginfo)
1129
{
1130
        gchar *filename = NULL;
1131

    
1132
        g_return_val_if_fail(msginfo != NULL, NULL);
1133

    
1134
        if (msginfo->file_path)
1135
                return g_strdup(msginfo->file_path);
1136

    
1137
        filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
1138
        if (!filename)
1139
                debug_print(_("can't fetch message %d\n"), msginfo->msgnum);
1140

    
1141
        return filename;
1142
}
1143

    
1144
GSList *procmsg_get_message_file_list(GSList *mlist)
1145
{
1146
        GSList *file_list = NULL;
1147
        MsgInfo *msginfo;
1148
        MsgFileInfo *fileinfo;
1149
        gchar *file;
1150

    
1151
        while (mlist != NULL) {
1152
                msginfo = (MsgInfo *)mlist->data;
1153
                file = procmsg_get_message_file(msginfo);
1154
                if (!file) {
1155
                        procmsg_message_file_list_free(file_list);
1156
                        return NULL;
1157
                }
1158
                fileinfo = g_new(MsgFileInfo, 1);
1159
                fileinfo->file = file;
1160
                fileinfo->flags = g_new(MsgFlags, 1);
1161
                *fileinfo->flags = msginfo->flags;
1162
                file_list = g_slist_prepend(file_list, fileinfo);
1163
                mlist = mlist->next;
1164
        }
1165

    
1166
        file_list = g_slist_reverse(file_list);
1167

    
1168
        return file_list;
1169
}
1170

    
1171
void procmsg_message_file_list_free(GSList *file_list)
1172
{
1173
        GSList *cur;
1174
        MsgFileInfo *fileinfo;
1175

    
1176
        for (cur = file_list; cur != NULL; cur = cur->next) {
1177
                fileinfo = (MsgFileInfo *)cur->data;
1178
                g_free(fileinfo->file);
1179
                g_free(fileinfo->flags);
1180
                g_free(fileinfo);
1181
        }
1182

    
1183
        g_slist_free(file_list);
1184
}
1185

    
1186
FILE *procmsg_open_message(MsgInfo *msginfo)
1187
{
1188
        FILE *fp;
1189
        gchar *file;
1190

    
1191
        g_return_val_if_fail(msginfo != NULL, NULL);
1192

    
1193
        file = procmsg_get_message_file_path(msginfo);
1194
        g_return_val_if_fail(file != NULL, NULL);
1195

    
1196
        if (!is_file_exist(file)) {
1197
                g_free(file);
1198
                file = procmsg_get_message_file(msginfo);
1199
                if (!file)
1200
                        return NULL;
1201
        }
1202

    
1203
        if ((fp = g_fopen(file, "rb")) == NULL) {
1204
                FILE_OP_ERROR(file, "procmsg_open_message: fopen");
1205
                g_free(file);
1206
                return NULL;
1207
        }
1208

    
1209
        g_free(file);
1210

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

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

    
1218
        return fp;
1219
}
1220

    
1221
static DecryptMessageFunc decrypt_message_func = NULL;
1222
static gboolean auto_decrypt = TRUE;
1223

    
1224
void procmsg_set_decrypt_message_func(DecryptMessageFunc func)
1225
{
1226
        decrypt_message_func = func;
1227
}
1228

    
1229
void procmsg_set_auto_decrypt_message(gboolean enabled)
1230
{
1231
        auto_decrypt = enabled;
1232
}
1233

    
1234
FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
1235
{
1236
        FILE *fp;
1237

    
1238
        if (decrypt_message_func && auto_decrypt)
1239
                return decrypt_message_func(msginfo, mimeinfo);
1240

    
1241
        *mimeinfo = NULL;
1242
        if ((fp = procmsg_open_message(msginfo)) == NULL)
1243
                return NULL;
1244
        *mimeinfo = procmime_scan_mime_header(fp);
1245

    
1246
        return fp;
1247
}
1248

    
1249
gboolean procmsg_msg_exist(MsgInfo *msginfo)
1250
{
1251
        gchar *path;
1252
        gboolean ret;
1253

    
1254
        if (!msginfo) return FALSE;
1255

    
1256
        path = folder_item_get_path(msginfo->folder);
1257
        change_dir(path);
1258
        ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
1259
        g_free(path);
1260

    
1261
        return ret;
1262
}
1263

    
1264
gboolean procmsg_trash_messages_exist(void)
1265
{
1266
        FolderItem *trash;
1267
        GList *cur;
1268

    
1269
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1270
                trash = FOLDER(cur->data)->trash;
1271
                if (trash && trash->total > 0)
1272
                        return TRUE;
1273
        }
1274

    
1275
        return FALSE;
1276
}
1277

    
1278
void procmsg_empty_trash(FolderItem *trash)
1279
{
1280
        if (!trash)
1281
                return;
1282

    
1283
        g_return_if_fail(trash->stype == F_TRASH);
1284

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

    
1288
                folder_item_remove_all_msg(trash);
1289
                procmsg_clear_cache(trash);
1290
                procmsg_clear_mark(trash);
1291
                trash->cache_dirty = FALSE;
1292
                trash->mark_dirty = FALSE;
1293
        }
1294
}
1295

    
1296
void procmsg_empty_all_trash(void)
1297
{
1298
        FolderItem *trash;
1299
        GList *cur;
1300

    
1301
        for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
1302
                trash = FOLDER(cur->data)->trash;
1303
                procmsg_empty_trash(trash);
1304
        }
1305
}
1306

    
1307
static gboolean remove_all_cached_messages_func(GNode *node, gpointer data)
1308
{
1309
        FolderItem *item;
1310
        gchar *dir;
1311

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

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

    
1318
        dir = folder_item_get_path(item);
1319
        if (is_dir_exist(dir)) {
1320
                debug_print("removing all cached messages in '%s' ...\n",
1321
                            item->path);
1322
                remove_all_numbered_files(dir);
1323
        }
1324
        g_free(dir);
1325

    
1326
        return FALSE;
1327
}
1328

    
1329
void procmsg_remove_all_cached_messages(Folder *folder)
1330
{
1331
        g_return_if_fail(folder != NULL);
1332
        g_return_if_fail(FOLDER_IS_REMOTE(folder));
1333

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

    
1337
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1338
                        remove_all_cached_messages_func, NULL);
1339
}
1340

    
1341
gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file)
1342
{
1343
        gint num;
1344
        MsgFlags flag = {0, 0};
1345

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

    
1348
        if (!outbox)
1349
                outbox = folder_get_default_outbox();
1350
        g_return_val_if_fail(outbox != NULL, -1);
1351

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

    
1358
        return 0;
1359
}
1360

    
1361
static guint print_id = 0;
1362

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

    
1368
#ifdef G_OS_WIN32
1369
        if (canonicalize_file_replace(file) < 0)
1370
                return -1;
1371
#endif
1372

    
1373
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
1374
                g_snprintf(buf, sizeof(buf) - 1, cmdline, file);
1375
        else {
1376
                if (cmdline) {
1377
                        g_warning(_("Print command line is invalid: `%s'\n"),
1378
                                  cmdline);
1379
                        return -1;
1380
                }
1381

    
1382
#ifdef G_OS_WIN32
1383
                execute_print_file(file);
1384
                return 0;
1385
#else
1386
                g_snprintf(buf, sizeof(buf) - 1, def_cmd, file);
1387
#endif
1388
        }
1389

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

    
1394
        system(buf);
1395

    
1396
        return 0;
1397
}
1398

    
1399
void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline,
1400
                           gboolean all_headers)
1401
{
1402
        gchar *prtmp;
1403
        FILE *msgfp, *tmpfp, *prfp;
1404
        GPtrArray *headers;
1405
        gint i;
1406
        gchar buf[BUFFSIZE];
1407

    
1408
        g_return_if_fail(msginfo != NULL);
1409

    
1410
        if ((tmpfp = procmime_get_first_text_content
1411
                (msginfo, conv_get_locale_charset_str())) == NULL) {
1412
                g_warning("Can't get text part\n");
1413
                return;
1414
        }
1415

    
1416
        prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
1417
                                get_mime_tmp_dir(), G_DIR_SEPARATOR,
1418
                                print_id++);
1419

    
1420
        if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1421
                FILE_OP_ERROR(prtmp, "procmsg_print_message: fopen");
1422
                g_free(prtmp);
1423
                fclose(tmpfp);
1424
                return;
1425
        }
1426

    
1427
        if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
1428
                fclose(prfp);
1429
                g_free(prtmp);
1430
                fclose(tmpfp);
1431
                return;
1432
        }
1433

    
1434
        if (all_headers)
1435
                headers = procheader_get_header_array_asis(msgfp, NULL);
1436
        else
1437
                headers = procheader_get_header_array_for_display(msgfp, NULL);
1438

    
1439
        fclose(msgfp);
1440

    
1441
        for (i = 0; i < headers->len; i++) {
1442
                Header *hdr;
1443
                gchar *locale_str;
1444
                const gchar *body;
1445

    
1446
                hdr = g_ptr_array_index(headers, i);
1447

    
1448
                if (!g_ascii_strcasecmp(hdr->name, "Subject"))
1449
                        body = msginfo->subject;
1450
                else if (!g_ascii_strcasecmp(hdr->name, "From"))
1451
                        body = msginfo->from;
1452
                else if (!g_ascii_strcasecmp(hdr->name, "To"))
1453
                        body = msginfo->to;
1454
                else if (!g_ascii_strcasecmp(hdr->name, "Cc")) {
1455
                        unfold_line(hdr->body);
1456
                        body = hdr->body;
1457
                        while (g_ascii_isspace(*body))
1458
                                body++;
1459
                } else {
1460
                        body = hdr->body;
1461
                        while (g_ascii_isspace(*body))
1462
                                body++;
1463
                }
1464

    
1465
                if (body && *body != '\0') {
1466
                        locale_str = conv_codeset_strdup
1467
                                (body, CS_INTERNAL,
1468
                                 conv_get_locale_charset_str());
1469
                        fprintf(prfp, "%s: %s\n", hdr->name,
1470
                                locale_str ? locale_str : body);
1471
                        g_free(locale_str);
1472
                } else {
1473
                        fprintf(prfp, "%s: (none)\n", hdr->name);
1474
                }
1475
        }
1476

    
1477
        procheader_header_array_destroy(headers);
1478

    
1479
        fputc('\n', prfp);
1480

    
1481
        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1482
                fputs(buf, prfp);
1483

    
1484
        fclose(prfp);
1485
        fclose(tmpfp);
1486

    
1487
        print_command_exec(prtmp, cmdline);
1488

    
1489
        g_free(prtmp);
1490
}
1491

    
1492
void procmsg_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo,
1493
                                const gchar *cmdline, gboolean all_headers)
1494
{
1495
        FILE *msgfp, *tmpfp, *prfp;
1496
        gchar *prtmp;
1497
        gchar buf[BUFFSIZE];
1498

    
1499
        if ((msgfp = procmsg_open_message(msginfo)) == NULL) {
1500
                return;
1501
        }
1502

    
1503
        if ((tmpfp = procmime_get_text_content
1504
                (partinfo, msgfp, conv_get_locale_charset_str())) == NULL) {
1505
                fclose(msgfp);
1506
                return;
1507
        }
1508
        fclose(msgfp);
1509

    
1510
        prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt",
1511
                                get_mime_tmp_dir(), G_DIR_SEPARATOR,
1512
                                print_id++);
1513
        if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1514
                FILE_OP_ERROR(prtmp, "procmsg_print_message_part: fopen");
1515
                g_free(prtmp);
1516
                fclose(tmpfp);
1517
                return;
1518
        }
1519

    
1520
        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1521
                fputs(buf, prfp);
1522

    
1523
        fclose(prfp);
1524
        fclose(tmpfp);
1525

    
1526
        print_command_exec(prtmp, cmdline);
1527

    
1528
        g_free(prtmp);
1529
}
1530

    
1531
static gboolean procmsg_get_flags(FolderItem *item, gint num,
1532
                                  MsgPermFlags *flags)
1533
{
1534
        FILE *fp;
1535
        guint32 idata;
1536
        gint read_num;
1537
        MsgPermFlags perm_flags;
1538
        gboolean found = FALSE;
1539
        GSList *cur;
1540

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

    
1544
        while (fread(&idata, sizeof(idata), 1, fp) == 1) {
1545
                read_num = idata;
1546
                if (fread(&idata, sizeof(idata), 1, fp) != 1)
1547
                        break;
1548
                perm_flags = idata;
1549
                if (read_num == num) {
1550
                        *flags = perm_flags;
1551
                        found = TRUE;
1552
                        break;
1553
                }
1554
        }
1555

    
1556
        fclose(fp);
1557
        if (found)
1558
                return TRUE;
1559

    
1560
        for (cur = item->mark_queue; cur != NULL; cur = cur->next) {
1561
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1562

    
1563
                if (msginfo->msgnum == num) {
1564
                        *flags = msginfo->flags.perm_flags;
1565
                        found = TRUE;
1566
                        break;
1567
                }
1568
        }
1569

    
1570
        return found;
1571
}
1572

    
1573
MsgInfo *procmsg_get_msginfo(FolderItem *item, gint num)
1574
{
1575
        MsgInfo *msginfo;
1576
        FolderType type;
1577

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

    
1580
        msginfo = folder_item_get_msginfo(item, num);
1581
        if (!msginfo)
1582
                return NULL;
1583

    
1584
        type = FOLDER_TYPE(item->folder);
1585
        if (type == F_MH || type == F_IMAP) {
1586
                if (item->stype == F_QUEUE) {
1587
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1588
                } else if (item->stype == F_DRAFT) {
1589
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1590
                }
1591
        }
1592
        if (type == F_IMAP) {
1593
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
1594
        } else if (type == F_NEWS) {
1595
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
1596
        }
1597

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

    
1604
        return msginfo;
1605
}
1606

    
1607
MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1608
{
1609
        MsgInfo *newmsginfo;
1610

    
1611
        if (msginfo == NULL) return NULL;
1612

    
1613
        newmsginfo = g_new0(MsgInfo, 1);
1614

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

    
1619
        MEMBCOPY(msgnum);
1620
        MEMBCOPY(size);
1621
        MEMBCOPY(mtime);
1622
        MEMBCOPY(date_t);
1623

    
1624
        MEMBCOPY(flags);
1625

    
1626
        MEMBDUP(fromname);
1627

    
1628
        MEMBDUP(date);
1629
        MEMBDUP(from);
1630
        MEMBDUP(to);
1631
        MEMBDUP(cc);
1632
        MEMBDUP(newsgroups);
1633
        MEMBDUP(subject);
1634
        MEMBDUP(msgid);
1635
        MEMBDUP(inreplyto);
1636

    
1637
        MEMBCOPY(folder);
1638
        MEMBCOPY(to_folder);
1639

    
1640
        MEMBDUP(xface);
1641

    
1642
        MEMBDUP(file_path);
1643

    
1644
        if (msginfo->encinfo) {
1645
                newmsginfo->encinfo = g_new0(MsgEncryptInfo, 1);
1646
                MEMBDUP(encinfo->plaintext_file);
1647
                MEMBDUP(encinfo->sigstatus);
1648
                MEMBDUP(encinfo->sigstatus_full);
1649
                MEMBCOPY(encinfo->decryption_failed);
1650
        }
1651

    
1652
        return newmsginfo;
1653
}
1654

    
1655
MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1656
{
1657
        MsgInfo *full_msginfo;
1658
        gchar *file;
1659

    
1660
        if (msginfo == NULL) return NULL;
1661

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

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

    
1672
        full_msginfo->msgnum = msginfo->msgnum;
1673
        full_msginfo->size = msginfo->size;
1674
        full_msginfo->mtime = msginfo->mtime;
1675
        full_msginfo->folder = msginfo->folder;
1676
        full_msginfo->to_folder = msginfo->to_folder;
1677

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

    
1680
        if (msginfo->encinfo) {
1681
                full_msginfo->encinfo = g_new0(MsgEncryptInfo, 1);
1682
                full_msginfo->encinfo->plaintext_file =
1683
                        g_strdup(msginfo->encinfo->plaintext_file);
1684
                full_msginfo->encinfo->sigstatus =
1685
                        g_strdup(msginfo->encinfo->sigstatus);
1686
                full_msginfo->encinfo->sigstatus_full =
1687
                        g_strdup(msginfo->encinfo->sigstatus_full);
1688
                full_msginfo->encinfo->decryption_failed =
1689
                        msginfo->encinfo->decryption_failed;
1690
        }
1691

    
1692
        return full_msginfo;
1693
}
1694

    
1695
gboolean procmsg_msginfo_equal(MsgInfo *msginfo_a, MsgInfo *msginfo_b)
1696
{
1697
        if (!msginfo_a || !msginfo_b)
1698
                return FALSE;
1699

    
1700
        if (msginfo_a == msginfo_b)
1701
                return TRUE;
1702

    
1703
        if (msginfo_a->folder == msginfo_b->folder &&
1704
            msginfo_a->msgnum == msginfo_b->msgnum &&
1705
            msginfo_a->size   == msginfo_b->size   &&
1706
            msginfo_a->mtime  == msginfo_b->mtime)
1707
                return TRUE;
1708

    
1709
        return FALSE;
1710
}
1711

    
1712
void procmsg_msginfo_free(MsgInfo *msginfo)
1713
{
1714
        if (msginfo == NULL) return;
1715

    
1716
        g_free(msginfo->xface);
1717

    
1718
        g_free(msginfo->fromname);
1719

    
1720
        g_free(msginfo->date);
1721
        g_free(msginfo->from);
1722
        g_free(msginfo->to);
1723
        g_free(msginfo->cc);
1724
        g_free(msginfo->newsgroups);
1725
        g_free(msginfo->subject);
1726
        g_free(msginfo->msgid);
1727
        g_free(msginfo->inreplyto);
1728

    
1729
        slist_free_strings(msginfo->references);
1730
        g_slist_free(msginfo->references);
1731

    
1732
        g_free(msginfo->file_path);
1733

    
1734
        if (msginfo->encinfo) {
1735
                g_free(msginfo->encinfo->plaintext_file);
1736
                g_free(msginfo->encinfo->sigstatus);
1737
                g_free(msginfo->encinfo->sigstatus_full);
1738
                g_free(msginfo->encinfo);
1739
        }
1740

    
1741
        g_free(msginfo);
1742
}
1743

    
1744
gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1745
{
1746
        const MsgInfo *msginfo1 = a;
1747
        const MsgInfo *msginfo2 = b;
1748

    
1749
        if (!msginfo1 || !msginfo2)
1750
                return 0;
1751

    
1752
        return msginfo1->msgnum - msginfo2->msgnum;
1753
}
1754

    
1755
#define CMP_FUNC_DEF(func_name, val)                                        \
1756
static gint func_name(gconstpointer a, gconstpointer b)                        \
1757
{                                                                        \
1758
        const MsgInfo *msginfo1 = a;                                        \
1759
        const MsgInfo *msginfo2 = b;                                        \
1760
        gint ret;                                                        \
1761
                                                                        \
1762
        if (!msginfo1 || !msginfo2)                                        \
1763
                return 0;                                                \
1764
                                                                        \
1765
        ret = (val);                                                        \
1766
        if (ret == 0)                                                        \
1767
                ret = msginfo1->date_t - msginfo2->date_t;                \
1768
                                                                        \
1769
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1770
}
1771

    
1772
CMP_FUNC_DEF(procmsg_cmp_by_mark,
1773
             MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
1774
CMP_FUNC_DEF(procmsg_cmp_by_unread,
1775
             MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
1776
CMP_FUNC_DEF(procmsg_cmp_by_mime,
1777
             MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags))
1778
CMP_FUNC_DEF(procmsg_cmp_by_label,
1779
             MSG_GET_COLORLABEL(msginfo1->flags) -
1780
             MSG_GET_COLORLABEL(msginfo2->flags))
1781
CMP_FUNC_DEF(procmsg_cmp_by_size, msginfo1->size - msginfo2->size)
1782

    
1783
#undef CMP_FUNC_DEF
1784
#define CMP_FUNC_DEF(func_name, val)                                        \
1785
static gint func_name(gconstpointer a, gconstpointer b)                        \
1786
{                                                                        \
1787
        const MsgInfo *msginfo1 = a;                                        \
1788
        const MsgInfo *msginfo2 = b;                                        \
1789
                                                                        \
1790
        if (!msginfo1 || !msginfo2)                                        \
1791
                return 0;                                                \
1792
                                                                        \
1793
        return (val) * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1794
}
1795

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

    
1799
#undef CMP_FUNC_DEF
1800
#define CMP_FUNC_DEF(func_name, var_name)                                \
1801
static gint func_name(gconstpointer a, gconstpointer b)                        \
1802
{                                                                        \
1803
        const MsgInfo *msginfo1 = a;                                        \
1804
        const MsgInfo *msginfo2 = b;                                        \
1805
        gint ret;                                                        \
1806
                                                                        \
1807
        if (!msginfo1->var_name)                                        \
1808
                return (msginfo2->var_name != NULL) *                        \
1809
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);\
1810
        if (!msginfo2->var_name)                                        \
1811
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1812
                                                                        \
1813
        ret = g_ascii_strcasecmp                                        \
1814
                (msginfo1->var_name, msginfo2->var_name);                \
1815
        if (ret == 0)                                                        \
1816
                ret = msginfo1->date_t - msginfo2->date_t;                \
1817
                                                                        \
1818
        return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);        \
1819
}
1820

    
1821
CMP_FUNC_DEF(procmsg_cmp_by_from, fromname)
1822
CMP_FUNC_DEF(procmsg_cmp_by_to, to)
1823

    
1824
#undef CMP_FUNC_DEF
1825

    
1826
static gint procmsg_cmp_by_subject(gconstpointer a, gconstpointer b)
1827
{
1828
        const MsgInfo *msginfo1 = a;
1829
        const MsgInfo *msginfo2 = b;
1830
        gint ret;
1831

    
1832
        if (!msginfo1->subject)
1833
                return (msginfo2->subject != NULL) *
1834
                        (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);
1835
        if (!msginfo2->subject)
1836
                return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1);
1837

    
1838
        ret = subject_compare_for_sort(msginfo1->subject, msginfo2->subject);
1839
        if (ret == 0)
1840
                ret = msginfo1->date_t - msginfo2->date_t;
1841

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