Statistics
| Branch: | Tag: | Revision:

root / libsylph / procmime.c @ aebfd4cc

History | View | Annotate | Download (38.1 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2011 Hiroyuki Yamamoto
4
 */
5

    
6
#ifdef HAVE_CONFIG_H
7
#  include "config.h"
8
#endif
9

    
10
#include "defs.h"
11

    
12
#include <glib.h>
13
#include <glib/gi18n.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <locale.h>
17
#include <ctype.h>
18

    
19
#include "procmime.h"
20
#include "procheader.h"
21
#include "base64.h"
22
#include "quoted-printable.h"
23
#include "uuencode.h"
24
#include "html.h"
25
#include "codeconv.h"
26
#include "utils.h"
27

    
28
#define MAX_MIME_LEVEL        64
29

    
30
static GHashTable *procmime_get_mime_type_table        (void);
31
static GList *procmime_get_mime_type_list        (const gchar *file);
32

    
33

    
34
MimeInfo *procmime_mimeinfo_new(void)
35
{
36
        MimeInfo *mimeinfo;
37

    
38
        mimeinfo = g_new0(MimeInfo, 1);
39
        mimeinfo->mime_type     = MIME_UNKNOWN;
40
        mimeinfo->encoding_type = ENC_UNKNOWN;
41

    
42
        return mimeinfo;
43
}
44

    
45
void procmime_mimeinfo_free_all(MimeInfo *mimeinfo)
46
{
47
        while (mimeinfo != NULL) {
48
                MimeInfo *next;
49

    
50
                g_free(mimeinfo->encoding);
51
                g_free(mimeinfo->content_type);
52
                g_free(mimeinfo->charset);
53
                g_free(mimeinfo->name);
54
                g_free(mimeinfo->boundary);
55
                g_free(mimeinfo->content_disposition);
56
                g_free(mimeinfo->filename);
57

    
58
                g_free(mimeinfo->sigstatus);
59
                g_free(mimeinfo->sigstatus_full);
60

    
61
                procmime_mimeinfo_free_all(mimeinfo->sub);
62
                procmime_mimeinfo_free_all(mimeinfo->children);
63
                procmime_mimeinfo_free_all(mimeinfo->plaintext);
64

    
65
                next = mimeinfo->next;
66
                g_free(mimeinfo);
67
                mimeinfo = next;
68
        }
69
}
70

    
71
MimeInfo *procmime_mimeinfo_insert(MimeInfo *parent, MimeInfo *mimeinfo)
72
{
73
        MimeInfo *child = parent->children;
74

    
75
        if (!child)
76
                parent->children = mimeinfo;
77
        else {
78
                while (child->next != NULL)
79
                        child = child->next;
80

    
81
                child->next = mimeinfo;
82
        }
83

    
84
        mimeinfo->parent = parent;
85
        mimeinfo->level = parent->level + 1;
86

    
87
        return mimeinfo;
88
}
89

    
90
#if 0
91
void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new)
92
{
93
        MimeInfo *parent = old->parent;
94
        MimeInfo *child;
95

96
        g_return_if_fail(parent != NULL);
97
        g_return_if_fail(new->next == NULL);
98

99
        for (child = parent->children; child && child != old;
100
             child = child->next)
101
                ;
102
        if (!child) {
103
                g_warning("oops: parent can't find it's own child");
104
                return;
105
        }
106
        procmime_mimeinfo_free_all(old);
107

108
        if (child == parent->children) {
109
                new->next = parent->children->next;
110
                parent->children = new;
111
        } else {
112
                new->next = child->next;
113
                child = new;
114
        }
115
}
116
#endif
117

    
118
MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo)
119
{
120
        if (!mimeinfo) return NULL;
121

    
122
        if (mimeinfo->children)
123
                return mimeinfo->children;
124
        if (mimeinfo->sub)
125
                return mimeinfo->sub;
126
        if (mimeinfo->next)
127
                return mimeinfo->next;
128

    
129
        if (mimeinfo->main) {
130
                mimeinfo = mimeinfo->main;
131
                if (mimeinfo->next)
132
                        return mimeinfo->next;
133
        }
134

    
135
        for (mimeinfo = mimeinfo->parent; mimeinfo != NULL;
136
             mimeinfo = mimeinfo->parent) {
137
                if (mimeinfo->next)
138
                        return mimeinfo->next;
139
                if (mimeinfo->main) {
140
                        mimeinfo = mimeinfo->main;
141
                        if (mimeinfo->next)
142
                                return mimeinfo->next;
143
                }
144
        }
145

    
146
        return NULL;
147
}
148

    
149
#if 0
150
void procmime_dump_mimeinfo(MimeInfo *mimeinfo)
151
{
152
        gint i;
153

154
        g_print("\n");
155

156
        for (; mimeinfo != NULL; mimeinfo = procmime_mimeinfo_next(mimeinfo)) {
157
                for (i = 0; i < mimeinfo->level; i++)
158
                        g_print("  ");
159
                g_print("%s%s\n", mimeinfo->main ? "sub: " : "",
160
                        mimeinfo->content_type);
161
        }
162
}
163
#endif
164

    
165
MimeInfo *procmime_scan_message(MsgInfo *msginfo)
166
{
167
        FILE *fp;
168
        MimeInfo *mimeinfo;
169

    
170
        g_return_val_if_fail(msginfo != NULL, NULL);
171

    
172
        if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
173
                return NULL;
174

    
175
        if (mimeinfo) {
176
                mimeinfo->size = msginfo->size;
177
                mimeinfo->content_size = get_left_file_size(fp);
178
                if (mimeinfo->encoding_type == ENC_BASE64)
179
                        mimeinfo->content_size = mimeinfo->content_size / 4 * 3;
180
                if (mimeinfo->mime_type == MIME_MULTIPART ||
181
                    mimeinfo->mime_type == MIME_MESSAGE_RFC822)
182
                        procmime_scan_multipart_message(mimeinfo, fp);
183
        }
184

    
185
        fclose(fp);
186

    
187
        return mimeinfo;
188
}
189

    
190
void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp)
191
{
192
        gchar *p;
193
        gchar *boundary;
194
        gint boundary_len = 0;
195
        gchar *buf;
196
        glong fpos, prev_fpos;
197

    
198
        g_return_if_fail(mimeinfo != NULL);
199
        g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART ||
200
                         mimeinfo->mime_type == MIME_MESSAGE_RFC822);
201

    
202
        if (mimeinfo->mime_type == MIME_MULTIPART) {
203
                g_return_if_fail(mimeinfo->boundary != NULL);
204
                g_return_if_fail(mimeinfo->sub == NULL);
205
        }
206
        g_return_if_fail(fp != NULL);
207

    
208
        buf = g_malloc(BUFFSIZE);
209

    
210
        boundary = mimeinfo->boundary;
211

    
212
        if (boundary) {
213
                boundary_len = strlen(boundary);
214

    
215
                /* look for first boundary */
216
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL)
217
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
218
                if (!p) {
219
                        g_free(buf);
220
                        return;
221
                }
222
        } else if (mimeinfo->parent && mimeinfo->parent->boundary) {
223
                boundary = mimeinfo->parent->boundary;
224
                boundary_len = strlen(boundary);
225
        }
226

    
227
        if ((fpos = ftell(fp)) < 0) {
228
                perror("ftell");
229
                g_free(buf);
230
                return;
231
        }
232

    
233
        debug_print("level = %d\n", mimeinfo->level);
234

    
235
        for (;;) {
236
                MimeInfo *partinfo;
237
                gboolean eom = FALSE;
238
                glong content_pos;
239
                gboolean is_base64;
240
                gint len;
241
                guint b64_content_len = 0;
242
                gint b64_pad_len = 0;
243

    
244
                prev_fpos = fpos;
245
                debug_print("prev_fpos: %ld\n", fpos);
246

    
247
                /* scan part header */
248
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
249
                        MimeInfo *sub;
250

    
251
                        mimeinfo->sub = sub = procmime_scan_mime_header(fp);
252
                        if (!sub) break;
253

    
254
                        debug_print("message/rfc822 part (content-type: %s)\n",
255
                                    sub->content_type);
256
                        sub->level = mimeinfo->level + 1;
257
                        sub->parent = mimeinfo->parent;
258
                        sub->main = mimeinfo;
259

    
260
                        partinfo = sub;
261
                } else {
262
                        partinfo = procmime_scan_mime_header(fp);
263
                        if (!partinfo) break;
264
                        procmime_mimeinfo_insert(mimeinfo, partinfo);
265
                        debug_print("content-type: %s\n",
266
                                    partinfo->content_type);
267
                }
268

    
269
                /* begin content */
270
                content_pos = ftell(fp);
271
                debug_print("content_pos: %ld\n", content_pos);
272

    
273
                if (partinfo->mime_type == MIME_MULTIPART ||
274
                    partinfo->mime_type == MIME_MESSAGE_RFC822) {
275
                        if (partinfo->level < MAX_MIME_LEVEL)
276
                                procmime_scan_multipart_message(partinfo, fp);
277
                }
278

    
279
                /* look for next boundary */
280
                buf[0] = '\0';
281
                is_base64 = partinfo->encoding_type == ENC_BASE64;
282
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL) {
283
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) {
284
                                if (buf[2 + boundary_len]     == '-' &&
285
                                    buf[2 + boundary_len + 1] == '-')
286
                                        eom = TRUE;
287
                                break;
288
                        } else if (is_base64) {
289
                                const gchar *s;
290
                                for (s = buf; *s && *s != '\r' && *s != '\n';
291
                                     ++s)
292
                                        if (*s == '=')
293
                                                ++b64_pad_len;
294
                                b64_content_len += s - buf;
295
                        }
296
                }
297
                if (p == NULL) {
298
                        /* broken MIME, or single part MIME message */
299
                        buf[0] = '\0';
300
                        eom = TRUE;
301
                }
302
                debug_print("boundary: %s\n", buf);
303

    
304
                fpos = ftell(fp);
305
                debug_print("fpos: %ld\n", fpos);
306

    
307
                len = strlen(buf);
308
                partinfo->size = fpos - prev_fpos - len;
309
                if (is_base64)
310
                        partinfo->content_size =
311
                                b64_content_len / 4 * 3 - b64_pad_len;
312
                else
313
                        partinfo->content_size = fpos - content_pos - len;
314
                debug_print("partinfo->size: %d\n", partinfo->size);
315
                debug_print("partinfo->content_size: %d\n",
316
                            partinfo->content_size);
317
                if (partinfo->sub && !partinfo->sub->sub &&
318
                    !partinfo->sub->children) {
319
                        partinfo->sub->size =
320
                                fpos - partinfo->sub->fpos - strlen(buf);
321
                        debug_print("partinfo->sub->size: %d\n",
322
                                    partinfo->sub->size);
323
                }
324

    
325
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
326
                        if (len > 0 && fseek(fp, fpos - len, SEEK_SET) < 0)
327
                                perror("fseek");
328
                        break;
329
                }
330

    
331
                if (eom) break;
332
        }
333

    
334
        g_free(buf);
335
}
336

    
337
void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding)
338
{
339
        gchar *enc;
340

    
341
        g_free(mimeinfo->encoding);
342
        enc = mimeinfo->encoding = g_strstrip(g_strdup(encoding));
343

    
344
        if (!g_ascii_strncasecmp(enc, "7bit", 4))
345
                mimeinfo->encoding_type = ENC_7BIT;
346
        else if (!g_ascii_strncasecmp(enc, "8bit", 4))
347
                mimeinfo->encoding_type = ENC_8BIT;
348
        else if (!g_ascii_strncasecmp(enc, "quoted-printable", 16))
349
                mimeinfo->encoding_type = ENC_QUOTED_PRINTABLE;
350
        else if (!g_ascii_strncasecmp(enc, "base64", 6))
351
                mimeinfo->encoding_type = ENC_BASE64;
352
        else if (!g_ascii_strncasecmp(enc, "x-uuencode", 10))
353
                mimeinfo->encoding_type = ENC_X_UUENCODE;
354
        else
355
                mimeinfo->encoding_type = ENC_UNKNOWN;
356

    
357
}
358

    
359
void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type)
360
{
361
        g_free(mimeinfo->content_type);
362
        g_free(mimeinfo->charset);
363
        g_free(mimeinfo->name);
364
        g_free(mimeinfo->boundary);
365
        mimeinfo->content_type = NULL;
366
        mimeinfo->charset      = NULL;
367
        mimeinfo->name         = NULL;
368
        mimeinfo->boundary     = NULL;
369

    
370
        procmime_scan_content_type_str(content_type, &mimeinfo->content_type,
371
                                       &mimeinfo->charset, &mimeinfo->name,
372
                                       &mimeinfo->boundary);
373

    
374
        mimeinfo->mime_type = procmime_scan_mime_type(mimeinfo->content_type);
375
        if (mimeinfo->mime_type == MIME_MULTIPART && !mimeinfo->boundary)
376
                mimeinfo->mime_type = MIME_TEXT;
377
}
378

    
379
typedef struct
380
{
381
        gchar *name;
382
        gchar *value;
383
} MimeParam;
384

    
385
typedef struct
386
{
387
        gchar *hvalue;
388
        GSList *plist;
389
} MimeParams;
390

    
391
static gchar *procmime_find_parameter_delimiter(const gchar *param,
392
                                                const gchar **eq)
393
{
394
        register const gchar *p = param;
395
        gboolean quoted = FALSE;
396
        const gchar *delim = NULL;
397

    
398
        while (*p) {
399
                if (*p == '=')
400
                        break;
401
                else if (*p == ';' || *p == '\r' || *p == '\n') {
402
                        delim = p;
403
                        break;
404
                }
405
                ++p;
406
        }
407
        if (*p != '=') {
408
                *eq = NULL;
409
                return (gchar *)delim;
410
        }
411
        *eq = p;
412

    
413
        ++p;
414
        while (g_ascii_isspace(*p))
415
                ++p;
416
        if (*p == '"') {
417
                quoted = TRUE;
418
                ++p;
419
        }
420

    
421
        while (*p) {
422
                if (quoted == TRUE) {
423
                        if (*p == '"')
424
                                quoted = FALSE;
425
                } else if (*p == ';' || *p == '\r' || *p == '\n') {
426
                        delim = p;
427
                        break;
428
                }
429
                ++p;
430
        }
431

    
432
        return (gchar *)delim;
433
}
434

    
435
static gchar *procmime_convert_value(const gchar *value, const gchar *charset)
436
{
437
        if (charset) {
438
                gchar *utf8_value;
439

    
440
                utf8_value = conv_codeset_strdup(value, charset, CS_INTERNAL);
441
                if (utf8_value)
442
                        return utf8_value;
443
        }
444

    
445
        return g_strdup(value);
446
}
447

    
448
static MimeParams *procmime_parse_mime_parameter(const gchar *str)
449
{
450
        ConvADType ad_type;
451
        gchar *tmp_param = NULL;
452
        gchar *hvalue;
453
        gchar *param, *name, *value;
454
        gchar *charset = NULL, *lang = NULL;
455
        const gchar *p, *delim;
456
        gint count, prev_count;
457
        gchar *cont_name;
458
        gchar *cont_value;
459
        MimeParam *mparam;
460
        MimeParams *mparams;
461
        GSList *plist = NULL;
462

    
463
        if ((p = strchr(str, ';')))
464
                hvalue = g_strndup(str, p - str);
465
        else
466
                hvalue = g_strdup(str);
467

    
468
        g_strstrip(hvalue);
469

    
470
        mparams = g_new(MimeParams, 1);
471
        mparams->hvalue = hvalue;
472
        mparams->plist = NULL;
473

    
474
        if (!p)
475
                return mparams;
476
        ++p;
477

    
478
        /* workaround for raw-JIS filename (Eudora etc.) */
479
        ad_type = conv_get_autodetect_type();
480
        if ((ad_type == C_AD_JAPANESE ||
481
             (ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) &&
482
            strstr(p, "\033$") != NULL) {
483
                CodeConvFunc conv_func;
484
                conv_func = conv_get_code_conv_func(NULL, NULL);
485
                tmp_param = conv_func(p, NULL);
486
                p = tmp_param;
487
                debug_print("procmime_parse_mime_parameter(): raw-JIS header body detected: %s\n", str);
488
        }
489

    
490
        count = prev_count = -1;
491
        cont_name = cont_value = NULL;
492

    
493
        for (;;) {
494
                gboolean encoded = FALSE;
495
                gchar *begin;
496
                gchar *dec_value;
497
                const gchar *eq;
498
                gchar *ast = NULL;
499

    
500
                while (*p == ';' || g_ascii_isspace(*p))
501
                        ++p;
502
                if (*p == '\0')
503
                        break;
504

    
505
                delim = procmime_find_parameter_delimiter(p, &eq);
506
                if (!eq)
507
                        break;
508
                if (delim)
509
                        param = g_strndup(p, delim - p);
510
                else
511
                        param = g_strdup(p);
512

    
513
                name = g_strndup(p, eq - p);
514
                g_strchomp(name);
515
                if (*name != '*' && (ast = strchr(name, '*'))) {
516
                        const gchar *next = ast + 1;
517

    
518
                        if (*next == '\0') {
519
                                encoded = TRUE;
520
                        } else if (g_ascii_isdigit(*next)) {
521
                                count = atoi(next);
522
                                while (g_ascii_isdigit(*next))
523
                                        ++next;
524
                                if (*next == '*')
525
                                        encoded = TRUE;
526
                                if (prev_count + 1 != count) {
527
                                        g_warning("procmime_parse_mime_parameter(): invalid count: %s\n", str);
528
                                        g_free(name);
529
                                        g_free(param);
530
                                        break;
531
                                }
532
                        } else {
533
                                g_warning("procmime_parse_mime_parameter(): invalid name: %s\n", str);
534
                                g_free(name);
535
                                g_free(param);
536
                                break;
537
                        }
538

    
539
                        *ast = '\0';
540
                }
541

    
542
                value = g_strdup(param + (eq - p) + 1);
543
                g_strstrip(value);
544
                if (*value == '"')
545
                        extract_quote(value, '"');
546

    
547
                begin = value;
548

    
549
                if (encoded) {
550
                        gchar *sq1, *sq2;
551

    
552
                        if ((sq1 = strchr(value, '\''))) {
553
                                if (sq1 > value) {
554
                                        if (charset)
555
                                                g_free(charset);
556
                                        charset = g_strndup(value, sq1 - value);
557
                                }
558
                                if ((sq2 = strchr(sq1 + 1, '\''))) {
559
                                        if (sq2 > sq1 + 1) {
560
                                                if (lang)
561
                                                        g_free(lang);
562
                                                lang = g_strndup(sq1 + 1,
563
                                                                 sq2 - sq1 - 1);
564
                                        }
565
                                        begin = sq2 + 1;
566
                                }
567
                        }
568
                }
569

    
570
#define CONCAT_CONT_VALUE(s)                                \
571
{                                                        \
572
        if (cont_value) {                                \
573
                gchar *tmp;                                \
574
                tmp = g_strconcat(cont_value, s, NULL);        \
575
                g_free(cont_value);                        \
576
                cont_value = tmp;                        \
577
        } else                                                \
578
                cont_value = g_strdup(s);                \
579
}
580

    
581
                if (count >= 0) {
582
                        if (count > 0 && cont_name) {
583
                                if (strcmp(cont_name, name) != 0) {
584
                                        g_warning("procmime_parse_mime_parameter(): mismatch parameter name: %s\n", str);
585
                                        g_free(name);
586
                                        g_free(value);
587
                                        g_free(param);
588
                                        break;
589
                                }
590
                        } else
591
                                cont_name = g_strdup(name);
592

    
593
                        if (encoded) {
594
                                dec_value = g_malloc(strlen(begin) + 1);
595
                                decode_xdigit_encoded_str(dec_value, begin);
596
                                CONCAT_CONT_VALUE(dec_value);
597
                                g_free(dec_value);
598
                        } else {
599
                                CONCAT_CONT_VALUE(begin);
600
                        }
601
                }
602

    
603
#undef CONCAT_CONT_VALUE
604

    
605
                if (count == -1 && cont_name && cont_value) {
606
                        mparam = g_new(MimeParam, 1);
607
                        mparam->name = cont_name;
608
                        cont_name = NULL;
609
                        mparam->value = procmime_convert_value
610
                                (cont_value, charset);
611
                        g_free(cont_value);
612
                        cont_value = NULL;
613
                        plist = g_slist_prepend(plist, mparam);
614
                }
615

    
616
                if (count == -1) {
617
                        mparam = g_new(MimeParam, 1);
618
                        mparam->name = name;
619
                        if (encoded) {
620
                                dec_value = g_malloc(strlen(begin) + 1);
621
                                decode_xdigit_encoded_str(dec_value, begin);
622
                                mparam->value = procmime_convert_value
623
                                        (dec_value, charset);
624
                                g_free(dec_value);
625
                        } else {
626
                                if (!ast &&
627
                                    (!g_ascii_strcasecmp(name, "name") ||
628
                                     !g_ascii_strcasecmp(name, "filename")))
629
                                        mparam->value =
630
                                                conv_unmime_header(begin, NULL);
631
                                else
632
                                        mparam->value = g_strdup(begin);
633
                        }
634
                        name = NULL;
635
                        plist = g_slist_prepend(plist, mparam);
636
                }
637

    
638
                g_free(name);
639
                g_free(value);
640
                g_free(param);
641

    
642
                prev_count = count;
643
                count = -1;
644

    
645
                if (delim)
646
                        p = delim + 1;
647
                else
648
                        break;
649
        }
650

    
651
        if (cont_name && cont_value) {
652
                mparam = g_new(MimeParam, 1);
653
                mparam->name = cont_name;
654
                cont_name = NULL;
655
                mparam->value = procmime_convert_value(cont_value, charset);
656
                plist = g_slist_prepend(plist, mparam);
657
        }
658

    
659
        g_free(cont_name);
660
        g_free(cont_value);
661
        g_free(lang);
662
        g_free(charset);
663
        if (tmp_param)
664
                g_free(tmp_param);
665

    
666
        plist = g_slist_reverse(plist);
667
        mparams->plist = plist;
668

    
669
        return mparams;
670
}
671

    
672
static void procmime_mime_params_free(MimeParams *mparams)
673
{
674
        GSList *cur;
675

    
676
        if (!mparams)
677
                return;
678

    
679
        g_free(mparams->hvalue);
680
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
681
                MimeParam *mparam = (MimeParam *)cur->data;
682
                g_free(mparam->name);
683
                g_free(mparam->value);
684
                g_free(mparam);
685
        }
686
        g_slist_free(mparams->plist);
687
        g_free(mparams);
688
}
689

    
690
void procmime_scan_content_type_str(const gchar *content_type,
691
                                    gchar **mime_type, gchar **charset,
692
                                    gchar **name, gchar **boundary)
693
{
694
        MimeParams *mparams;
695
        GSList *cur;
696

    
697
        mparams = procmime_parse_mime_parameter(content_type);
698

    
699
        if (mime_type)
700
                *mime_type = g_strdup(mparams->hvalue);
701

    
702
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
703
                MimeParam *param = (MimeParam *)cur->data;
704
                if (charset && !g_ascii_strcasecmp(param->name, "charset")) {
705
                        *charset = g_strdup(param->value);
706
                        eliminate_parenthesis(*charset, '(', ')');
707
                        g_strstrip(*charset);
708
                        charset = NULL;
709
                } else if (name && !g_ascii_strcasecmp(param->name, "name")) {
710
                        *name = g_strdup(param->value);
711
                        name = NULL;
712
                } else if (boundary &&
713
                           !g_ascii_strcasecmp(param->name, "boundary")) {
714
                        *boundary = g_strdup(param->value);
715
                        boundary = NULL;
716
                }
717
        }
718

    
719
        procmime_mime_params_free(mparams);
720
}
721

    
722
void procmime_scan_content_type_partial(const gchar *content_type,
723
                                        gint *total, gchar **part_id,
724
                                        gint *number)
725
{
726
        MimeParams *mparams;
727
        GSList *cur;
728
        gchar *id_str = NULL;
729
        gint t = 0, n = 0;
730

    
731
        *total = 0;
732
        *part_id = NULL;
733
        *number = 0;
734

    
735
        mparams = procmime_parse_mime_parameter(content_type);
736

    
737
        if (!mparams->hvalue ||
738
            g_ascii_strcasecmp(mparams->hvalue, "message/partial") != 0) {
739
                procmime_mime_params_free(mparams);
740
                return;
741
        }
742

    
743
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
744
                MimeParam *param = (MimeParam *)cur->data;
745
                if (!g_ascii_strcasecmp(param->name, "total")) {
746
                        t = atoi(param->value);
747
                } else if (!id_str && !g_ascii_strcasecmp(param->name, "id")) {
748
                        id_str = g_strdup(param->value);
749
                } else if (!g_ascii_strcasecmp(param->name, "number")) {
750
                        n = atoi(param->value);
751
                }
752
        }
753

    
754
        procmime_mime_params_free(mparams);
755

    
756
        if (n > 0 && (t == 0 || t >= n) && id_str) {
757
                *total = t;
758
                *part_id = id_str;
759
                *number = n;
760
        } else {
761
                g_free(id_str);
762
        }
763
}
764

    
765
void procmime_scan_content_disposition(MimeInfo *mimeinfo,
766
                                       const gchar *content_disposition)
767
{
768
        MimeParams *mparams;
769
        GSList *cur;
770

    
771
        mparams = procmime_parse_mime_parameter(content_disposition);
772

    
773
        mimeinfo->content_disposition = g_strdup(mparams->hvalue);
774

    
775
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
776
                MimeParam *param = (MimeParam *)cur->data;
777
                if (!g_ascii_strcasecmp(param->name, "filename")) {
778
                        mimeinfo->filename = g_strdup(param->value);
779
                        break;
780
                }
781
        }
782

    
783
        procmime_mime_params_free(mparams);
784
}
785

    
786
enum
787
{
788
        H_CONTENT_TRANSFER_ENCODING = 0,
789
        H_CONTENT_TYPE                    = 1,
790
        H_CONTENT_DISPOSITION            = 2
791
};
792

    
793
MimeInfo *procmime_scan_mime_header(FILE *fp)
794
{
795
        static HeaderEntry hentry[] = {{"Content-Transfer-Encoding:",
796
                                                          NULL, FALSE},
797
                                       {"Content-Type:", NULL, TRUE},
798
                                       {"Content-Disposition:",
799
                                                          NULL, TRUE},
800
                                       {NULL,                  NULL, FALSE}};
801
        gchar buf[BUFFSIZE];
802
        gint hnum;
803
        HeaderEntry *hp;
804
        MimeInfo *mimeinfo;
805

    
806
        g_return_val_if_fail(fp != NULL, NULL);
807

    
808
        mimeinfo = procmime_mimeinfo_new();
809
        mimeinfo->mime_type = MIME_TEXT;
810
        mimeinfo->encoding_type = ENC_7BIT;
811
        mimeinfo->fpos = ftell(fp);
812

    
813
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
814
               != -1) {
815
                hp = hentry + hnum;
816

    
817
                if (H_CONTENT_TRANSFER_ENCODING == hnum) {
818
                        procmime_scan_encoding
819
                                (mimeinfo, buf + strlen(hp->name));
820
                } else if (H_CONTENT_TYPE == hnum) {
821
                        procmime_scan_content_type
822
                                (mimeinfo, buf + strlen(hp->name));
823
                } else if (H_CONTENT_DISPOSITION == hnum) {
824
                        procmime_scan_content_disposition
825
                                (mimeinfo, buf + strlen(hp->name));
826
                }
827
        }
828

    
829
        if (mimeinfo->mime_type == MIME_APPLICATION_OCTET_STREAM &&
830
            (mimeinfo->filename || mimeinfo->name)) {
831
                const gchar *type;
832
                type = procmime_get_mime_type
833
                        (mimeinfo->filename ? mimeinfo->filename
834
                         : mimeinfo->name);
835
                if (type)
836
                        mimeinfo->mime_type = procmime_scan_mime_type(type);
837
        }
838

    
839
        if (!mimeinfo->content_type)
840
                mimeinfo->content_type = g_strdup("text/plain");
841

    
842
        return mimeinfo;
843
}
844

    
845
static gint procmime_normalize_lbreak(FILE *infp, FILE *outfp)
846
{
847
        gchar buf[BUFFSIZE];
848
        gint len;
849

    
850
        g_return_val_if_fail(infp != NULL, -1);
851
        g_return_val_if_fail(outfp != NULL, -1);
852

    
853
        while (fgets(buf, sizeof(buf), infp) != NULL) {
854
                len = strlen(buf);
855
                if (len == sizeof(buf) - 1 && buf[len - 1] != '\n') {
856
                        if (buf[len - 1] == '\r') {
857
                                ungetc('\r', infp);
858
                                buf[len - 1] = '\0';
859
                        }
860
                        fputs(buf, outfp);
861
                        continue;
862
                }
863
#ifdef G_OS_WIN32
864
                strretchomp(buf);
865
                fputs(buf, outfp);
866
                fputs("\r\n", outfp);
867
#else
868
                strcrchomp(buf);
869
                fputs(buf, outfp);
870
#endif
871
        }
872

    
873
        return 0;
874
}
875

    
876
FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
877
{
878
        gchar buf[BUFFSIZE];
879
        gchar *boundary = NULL;
880
        gint boundary_len = 0;
881
        gboolean tmp_file = FALSE;
882
        gboolean normalize_lbreak = FALSE;
883
        ContentType content_type;
884

    
885
        g_return_val_if_fail(infp != NULL, NULL);
886
        g_return_val_if_fail(mimeinfo != NULL, NULL);
887

    
888
        if (!outfp) {
889
                outfp = my_tmpfile();
890
                if (!outfp) {
891
                        perror("tmpfile");
892
                        return NULL;
893
                }
894
                tmp_file = TRUE;
895
        }
896

    
897
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
898
                boundary = mimeinfo->parent->boundary;
899
                boundary_len = strlen(boundary);
900
        }
901

    
902
        content_type = procmime_scan_mime_type(mimeinfo->content_type);
903
        if (content_type == MIME_TEXT ||
904
            content_type == MIME_TEXT_HTML) {
905
                normalize_lbreak = TRUE;
906
        }
907

    
908
        if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) {
909
                FILE *tmpfp = outfp;
910
                gchar prev_empty_line[3] = "";
911

    
912
                if (normalize_lbreak) {
913
                        tmpfp = my_tmpfile();
914
                        if (!tmpfp) {
915
                                perror("tmpfile");
916
                                if (tmp_file) fclose(outfp);
917
                                return NULL;
918
                        }
919
                }
920

    
921
                while (fgets(buf, sizeof(buf), infp) != NULL &&
922
                       (!boundary ||
923
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
924
                        gint len;
925

    
926
                        if (prev_empty_line[0]) {
927
                                fputs(prev_empty_line, tmpfp);
928
                                prev_empty_line[0] = '\0';
929
                        }
930

    
931
                        if (buf[0] == '\n' ||
932
                            (buf[0] == '\r' && buf[1] == '\n'))
933
                                strcpy(prev_empty_line, buf);
934
                        else {
935
                                len = qp_decode_line(buf);
936
                                fwrite(buf, len, 1, tmpfp);
937
                        }
938
                }
939
                if (!boundary && prev_empty_line[0])
940
                        fputs(prev_empty_line, tmpfp);
941

    
942
                if (normalize_lbreak) {
943
                        if (fflush(tmpfp) == EOF) {
944
                                perror("fflush");
945
                                fclose(tmpfp);
946
                                if (tmp_file) fclose(outfp);
947
                                return NULL;
948
                        }
949
                        rewind(tmpfp);
950
                        procmime_normalize_lbreak(tmpfp, outfp);
951
                        fclose(tmpfp);
952
                }
953
        } else if (mimeinfo->encoding_type == ENC_BASE64) {
954
                gchar outbuf[BUFFSIZE];
955
                gint len;
956
                Base64Decoder *decoder;
957
                FILE *tmpfp = outfp;
958

    
959
                if (normalize_lbreak) {
960
                        tmpfp = my_tmpfile();
961
                        if (!tmpfp) {
962
                                perror("tmpfile");
963
                                if (tmp_file) fclose(outfp);
964
                                return NULL;
965
                        }
966
                }
967

    
968
                decoder = base64_decoder_new();
969
                while (fgets(buf, sizeof(buf), infp) != NULL &&
970
                       (!boundary ||
971
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
972
                        len = base64_decoder_decode(decoder, buf,
973
                                                    (guchar *)outbuf);
974
                        if (len < 0) {
975
                                g_warning("Bad BASE64 content\n");
976
                                break;
977
                        }
978
                        fwrite(outbuf, sizeof(gchar), len, tmpfp);
979
                }
980
                base64_decoder_free(decoder);
981

    
982
                if (normalize_lbreak) {
983
                        if (fflush(tmpfp) == EOF) {
984
                                perror("fflush");
985
                                fclose(tmpfp);
986
                                if (tmp_file) fclose(outfp);
987
                                return NULL;
988
                        }
989
                        rewind(tmpfp);
990
                        procmime_normalize_lbreak(tmpfp, outfp);
991
                        fclose(tmpfp);
992
                }
993
        } else if (mimeinfo->encoding_type == ENC_X_UUENCODE) {
994
                gchar outbuf[BUFFSIZE];
995
                gint len;
996
                gboolean flag = FALSE;
997

    
998
                while (fgets(buf, sizeof(buf), infp) != NULL &&
999
                       (!boundary ||
1000
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
1001
                        if(!flag && strncmp(buf,"begin ", 6)) continue;
1002

    
1003
                        if (flag) {
1004
                                len = fromuutobits(outbuf, buf);
1005
                                if (len <= 0) {
1006
                                        if (len < 0) 
1007
                                                g_warning("Bad UUENCODE content(%d)\n", len);
1008
                                        break;
1009
                                }
1010
                                fwrite(outbuf, sizeof(gchar), len, outfp);
1011
                        } else
1012
                                flag = TRUE;
1013
                }
1014
        } else {
1015
                gchar prev_empty_line[3] = "";
1016
                gint len;
1017
                gboolean cont_line = FALSE;
1018

    
1019
                while (fgets(buf, sizeof(buf), infp) != NULL &&
1020
                       (!boundary ||
1021
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
1022
                        if (prev_empty_line[0]) {
1023
                                fputs(prev_empty_line, outfp);
1024
                                prev_empty_line[0] = '\0';
1025
                        }
1026

    
1027
                        len = strlen(buf);
1028
                        if (len == sizeof(buf) - 1 &&
1029
                            buf[len - 1] != '\n') {
1030
                                if (buf[len - 1] == '\r') {
1031
                                        ungetc('\r', infp);
1032
                                        buf[len - 1] = '\0';
1033
                                }
1034
                                fputs(buf, outfp);
1035
                                cont_line = TRUE;
1036
                                continue;
1037
                        }
1038

    
1039
                        if (normalize_lbreak) {
1040
#ifdef G_OS_WIN32
1041
                                strretchomp(buf);
1042
                                if (!cont_line && buf[0] == '\0')
1043
                                        strcpy(prev_empty_line, "\r\n");
1044
                                else {
1045
                                        fputs(buf, outfp);
1046
                                        fputs("\r\n", outfp);
1047
                                }
1048
#else
1049
                                strcrchomp(buf);
1050
                                if (!cont_line && buf[0] == '\n')
1051
                                        strcpy(prev_empty_line, "\n");
1052
                                else
1053
                                        fputs(buf, outfp);
1054
#endif
1055
                        } else {
1056
                                if (!cont_line &&
1057
                                    (buf[0] == '\n' ||
1058
                                     (buf[0] == '\r' && buf[1] == '\n')))
1059
                                        strcpy(prev_empty_line, buf);
1060
                                else
1061
                                        fputs(buf, outfp);
1062
                        }
1063

    
1064
                        cont_line = FALSE;
1065
                }
1066
                if (!boundary && prev_empty_line[0])
1067
                        fputs(prev_empty_line, outfp);
1068
        }
1069

    
1070
        if (fflush(outfp) == EOF)
1071
                perror("fflush");
1072
        if (ferror(outfp) != 0) {
1073
                g_warning("procmime_decode_content(): Can't write to temporary file\n");
1074
                if (tmp_file) fclose(outfp);
1075
                return NULL;
1076
        }
1077

    
1078
        if (tmp_file) rewind(outfp);
1079
        return outfp;
1080
}
1081

    
1082
gint procmime_get_part(const gchar *outfile, const gchar *infile,
1083
                       MimeInfo *mimeinfo)
1084
{
1085
        FILE *infp;
1086
        gint ret;
1087

    
1088
        g_return_val_if_fail(outfile != NULL, -1);
1089
        g_return_val_if_fail(infile != NULL, -1);
1090
        g_return_val_if_fail(mimeinfo != NULL, -1);
1091

    
1092
        if ((infp = g_fopen(infile, "rb")) == NULL) {
1093
                FILE_OP_ERROR(infile, "fopen");
1094
                return -1;
1095
        }
1096
        ret = procmime_get_part_fp(outfile, infp, mimeinfo);
1097
        fclose(infp);
1098

    
1099
        return ret;
1100
}
1101

    
1102
gint procmime_get_part_fp(const gchar *outfile, FILE *infp, MimeInfo *mimeinfo)
1103
{
1104
        FILE *outfp;
1105
        gchar buf[BUFFSIZE];
1106

    
1107
        g_return_val_if_fail(outfile != NULL, -1);
1108
        g_return_val_if_fail(infp != NULL, -1);
1109
        g_return_val_if_fail(mimeinfo != NULL, -1);
1110

    
1111
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1112
                FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
1113
                return -1;
1114
        }
1115
        if ((outfp = g_fopen(outfile, "wb")) == NULL) {
1116
                FILE_OP_ERROR(outfile, "fopen");
1117
                return -1;
1118
        }
1119

    
1120
        while (fgets(buf, sizeof(buf), infp) != NULL)
1121
                if (buf[0] == '\r' || buf[0] == '\n') break;
1122

    
1123
        if (procmime_decode_content(outfp, infp, mimeinfo) == NULL) {
1124
                fclose(outfp);
1125
                g_unlink(outfile);
1126
                return -1;
1127
        }
1128

    
1129
        if (fclose(outfp) == EOF) {
1130
                FILE_OP_ERROR(outfile, "fclose");
1131
                g_unlink(outfile);
1132
                return -1;
1133
        }
1134

    
1135
        return 0;
1136
}
1137

    
1138
FILE *procmime_get_part_fp_fp(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
1139
{
1140
        gchar buf[BUFFSIZE];
1141

    
1142
        g_return_val_if_fail(infp != NULL, NULL);
1143
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1144

    
1145
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1146
                FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
1147
                return NULL;
1148
        }
1149

    
1150
        while (fgets(buf, sizeof(buf), infp) != NULL)
1151
                if (buf[0] == '\r' || buf[0] == '\n') break;
1152

    
1153
        if ((outfp = procmime_decode_content(outfp, infp, mimeinfo)) == NULL) {
1154
                return NULL;
1155
        }
1156

    
1157
        return outfp;
1158
}
1159

    
1160
gint procmime_get_all_parts(const gchar *dir, const gchar *infile,
1161
                            MimeInfo *mimeinfo)
1162
{
1163
        FILE *fp;
1164
        MimeInfo *partinfo;
1165
        gchar *base, *filename;
1166

    
1167
        g_return_val_if_fail(dir != NULL, -1);
1168
        g_return_val_if_fail(infile != NULL, -1);
1169
        g_return_val_if_fail(mimeinfo != NULL, -1);
1170

    
1171
        if (!is_dir_exist(dir)) {
1172
                g_warning("%s: directory not exist.\n", dir);
1173
                return -1;
1174
        }
1175

    
1176
        if ((fp = g_fopen(infile, "rb")) == NULL) {
1177
                FILE_OP_ERROR(infile, "fopen");
1178
                return -1;
1179
        }
1180

    
1181
        for (partinfo = mimeinfo; partinfo != NULL;
1182
             partinfo = procmime_mimeinfo_next(partinfo)) {
1183
                if (partinfo->filename || partinfo->name) {
1184
                        gint count = 1;
1185

    
1186
                        base = procmime_get_part_file_name(partinfo);
1187
                        filename = g_strconcat(dir, G_DIR_SEPARATOR_S, base,
1188
                                               NULL);
1189

    
1190
                        while (is_file_entry_exist(filename)) {
1191
                                gchar *base_alt;
1192

    
1193
                                base_alt = get_alt_filename(base, count++);
1194
                                g_free(filename);
1195
                                filename = g_strconcat
1196
                                        (dir, G_DIR_SEPARATOR_S, base_alt,
1197
                                         NULL);
1198
                                g_free(base_alt);
1199
                        }
1200

    
1201
                        procmime_get_part_fp(filename, fp, partinfo);
1202

    
1203
                        g_free(filename);
1204
                        g_free(base);
1205
                }
1206
        }
1207

    
1208
        fclose(fp);
1209

    
1210
        return 0;
1211
}
1212

    
1213
FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp,
1214
                                const gchar *encoding)
1215
{
1216
        FILE *tmpfp, *outfp;
1217
        const gchar *src_encoding;
1218
        gboolean conv_fail = FALSE;
1219
        gchar buf[BUFFSIZE];
1220

    
1221
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1222
        g_return_val_if_fail(infp != NULL, NULL);
1223
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1224
                             mimeinfo->mime_type == MIME_TEXT_HTML, NULL);
1225

    
1226
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1227
                perror("fseek");
1228
                return NULL;
1229
        }
1230

    
1231
        while (fgets(buf, sizeof(buf), infp) != NULL)
1232
                if (buf[0] == '\r' || buf[0] == '\n') break;
1233

    
1234
        tmpfp = procmime_decode_content(NULL, infp, mimeinfo);
1235
        if (!tmpfp)
1236
                return NULL;
1237

    
1238
        if ((outfp = my_tmpfile()) == NULL) {
1239
                perror("tmpfile");
1240
                fclose(tmpfp);
1241
                return NULL;
1242
        }
1243

    
1244
        src_encoding = mimeinfo->charset ? mimeinfo->charset : NULL;
1245

    
1246
        if (mimeinfo->mime_type == MIME_TEXT) {
1247
                while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1248
                        gchar *str;
1249

    
1250
                        str = conv_codeset_strdup(buf, src_encoding, encoding);
1251
                        if (str) {
1252
                                fputs(str, outfp);
1253
                                g_free(str);
1254
                        } else {
1255
                                conv_fail = TRUE;
1256
                                fputs(buf, outfp);
1257
                        }
1258
                }
1259
        } else if (mimeinfo->mime_type == MIME_TEXT_HTML) {
1260
                HTMLParser *parser;
1261
                CodeConverter *conv;
1262
                const gchar *str;
1263

    
1264
                conv = conv_code_converter_new(src_encoding, encoding);
1265
                parser = html_parser_new(tmpfp, conv);
1266
                while ((str = html_parse(parser)) != NULL) {
1267
                        fputs(str, outfp);
1268
                }
1269
                html_parser_destroy(parser);
1270
                conv_code_converter_destroy(conv);
1271
        }
1272

    
1273
        if (conv_fail)
1274
                g_warning(_("procmime_get_text_content(): Code conversion failed.\n"));
1275

    
1276
        fclose(tmpfp);
1277
        if (fflush(outfp) == EOF) {
1278
                perror("fflush");
1279
                fclose(outfp);
1280
                return NULL;
1281
        }
1282
        rewind(outfp);
1283

    
1284
        return outfp;
1285
}
1286

    
1287
/* search the first text part of (multipart) MIME message,
1288
   decode, convert it and output to outfp. */
1289
FILE *procmime_get_first_text_content(MsgInfo *msginfo, const gchar *encoding)
1290
{
1291
        FILE *infp, *outfp = NULL;
1292
        MimeInfo *mimeinfo, *partinfo;
1293

    
1294
        g_return_val_if_fail(msginfo != NULL, NULL);
1295

    
1296
        mimeinfo = procmime_scan_message(msginfo);
1297
        if (!mimeinfo) return NULL;
1298

    
1299
        if ((infp = procmsg_open_message(msginfo)) == NULL) {
1300
                procmime_mimeinfo_free_all(mimeinfo);
1301
                return NULL;
1302
        }
1303

    
1304
        partinfo = mimeinfo;
1305
        while (partinfo && partinfo->mime_type != MIME_TEXT)
1306
                partinfo = procmime_mimeinfo_next(partinfo);
1307
        if (!partinfo) {
1308
                partinfo = mimeinfo;
1309
                while (partinfo && partinfo->mime_type != MIME_TEXT_HTML)
1310
                        partinfo = procmime_mimeinfo_next(partinfo);
1311
        }
1312

    
1313
        if (partinfo)
1314
                outfp = procmime_get_text_content(partinfo, infp, encoding);
1315

    
1316
        fclose(infp);
1317
        procmime_mimeinfo_free_all(mimeinfo);
1318

    
1319
        return outfp;
1320
}
1321

    
1322
gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename,
1323
                                   const gchar *str, StrFindFunc find_func)
1324
{
1325

    
1326
        FILE *infp, *outfp;
1327
        gchar buf[BUFFSIZE];
1328

    
1329
        g_return_val_if_fail(mimeinfo != NULL, FALSE);
1330
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1331
                             mimeinfo->mime_type == MIME_TEXT_HTML, FALSE);
1332
        g_return_val_if_fail(str != NULL, FALSE);
1333
        g_return_val_if_fail(find_func != NULL, FALSE);
1334

    
1335
        if ((infp = g_fopen(filename, "rb")) == NULL) {
1336
                FILE_OP_ERROR(filename, "fopen");
1337
                return FALSE;
1338
        }
1339

    
1340
        outfp = procmime_get_text_content(mimeinfo, infp, NULL);
1341
        fclose(infp);
1342

    
1343
        if (!outfp)
1344
                return FALSE;
1345

    
1346
        while (fgets(buf, sizeof(buf), outfp) != NULL) {
1347
                strretchomp(buf);
1348
                if (find_func(buf, str)) {
1349
                        fclose(outfp);
1350
                        return TRUE;
1351
                }
1352
        }
1353

    
1354
        fclose(outfp);
1355

    
1356
        return FALSE;
1357
}
1358

    
1359
gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str,
1360
                              StrFindFunc find_func)
1361
{
1362
        MimeInfo *mimeinfo;
1363
        MimeInfo *partinfo;
1364
        gchar *filename;
1365
        gboolean found = FALSE;
1366

    
1367
        g_return_val_if_fail(msginfo != NULL, FALSE);
1368
        g_return_val_if_fail(str != NULL, FALSE);
1369
        g_return_val_if_fail(find_func != NULL, FALSE);
1370

    
1371
        filename = procmsg_get_message_file(msginfo);
1372
        if (!filename) return FALSE;
1373
        mimeinfo = procmime_scan_message(msginfo);
1374

    
1375
        for (partinfo = mimeinfo; partinfo != NULL;
1376
             partinfo = procmime_mimeinfo_next(partinfo)) {
1377
                if (partinfo->mime_type == MIME_TEXT ||
1378
                    partinfo->mime_type == MIME_TEXT_HTML) {
1379
                        if (procmime_find_string_part
1380
                                (partinfo, filename, str, find_func) == TRUE) {
1381
                                found = TRUE;
1382
                                break;
1383
                        }
1384
                }
1385
        }
1386

    
1387
        procmime_mimeinfo_free_all(mimeinfo);
1388
        g_free(filename);
1389

    
1390
        return found;
1391
}
1392

    
1393
gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
1394
{
1395
        gchar *base;
1396
        const gchar *base_;
1397

    
1398
        base_ = mimeinfo->filename ? mimeinfo->filename
1399
                : mimeinfo->name ? mimeinfo->name : "mimetmp";
1400
        base_ = g_basename(base_);
1401
        if (*base_ == '\0') base_ = "mimetmp";
1402
        base = conv_filename_from_utf8(base_);
1403
        subst_for_filename(base);
1404

    
1405
        return base;
1406
}
1407

    
1408
gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
1409
{
1410
        static guint32 id = 0;
1411
        gchar *base;
1412
        gchar *filename;
1413
        gchar f_prefix[10];
1414

    
1415
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1416

    
1417
        g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
1418

    
1419
        if (MIME_TEXT_HTML == mimeinfo->mime_type)
1420
                base = g_strdup("mimetmp.html");
1421
        else
1422
                base = procmime_get_part_file_name(mimeinfo);
1423

    
1424
        filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1425
                               f_prefix, base, NULL);
1426

    
1427
        g_free(base);
1428

    
1429
        return filename;
1430
}
1431

    
1432
ContentType procmime_scan_mime_type(const gchar *mime_type)
1433
{
1434
        ContentType type;
1435

    
1436
        if (!g_ascii_strncasecmp(mime_type, "text/html", 9))
1437
                type = MIME_TEXT_HTML;
1438
        else if (!g_ascii_strncasecmp(mime_type, "text/", 5))
1439
                type = MIME_TEXT;
1440
        else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14))
1441
                type = MIME_MESSAGE_RFC822;
1442
        else if (!g_ascii_strncasecmp(mime_type, "message/", 8))
1443
                type = MIME_TEXT;
1444
        else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream",
1445
                                      24))
1446
                type = MIME_APPLICATION_OCTET_STREAM;
1447
        else if (!g_ascii_strncasecmp(mime_type, "application/", 12))
1448
                type = MIME_APPLICATION;
1449
        else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10))
1450
                type = MIME_MULTIPART;
1451
        else if (!g_ascii_strncasecmp(mime_type, "image/", 6))
1452
                type = MIME_IMAGE;
1453
        else if (!g_ascii_strncasecmp(mime_type, "audio/", 6))
1454
                type = MIME_AUDIO;
1455
        else if (!g_ascii_strncasecmp(mime_type, "video/", 6))
1456
                type = MIME_VIDEO;
1457
        else if (!g_ascii_strcasecmp(mime_type, "text"))
1458
                type = MIME_TEXT;
1459
        else
1460
                type = MIME_UNKNOWN;
1461

    
1462
        return type;
1463
}
1464

    
1465
static GList *mime_type_list = NULL;
1466

    
1467
gchar *procmime_get_mime_type(const gchar *filename)
1468
{
1469
        static GHashTable *mime_type_table = NULL;
1470
        MimeType *mime_type;
1471
        const gchar *p;
1472
        gchar ext[64];
1473
        static gboolean no_mime_type_table = FALSE;
1474

    
1475
        if (no_mime_type_table)
1476
                return NULL;
1477

    
1478
        if (!mime_type_table) {
1479
                mime_type_table = procmime_get_mime_type_table();
1480
                if (!mime_type_table) {
1481
                        no_mime_type_table = TRUE;
1482
                        return NULL;
1483
                }
1484
        }
1485

    
1486
        filename = g_basename(filename);
1487
        p = strrchr(filename, '.');
1488
        if (!p) return NULL;
1489

    
1490
        strncpy2(ext, p + 1, sizeof(ext));
1491
        g_strdown(ext);
1492
        mime_type = g_hash_table_lookup(mime_type_table, ext);
1493
        if (mime_type) {
1494
                gchar *str;
1495

    
1496
                str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
1497
                                  NULL);
1498
                return str;
1499
        }
1500

    
1501
        return NULL;
1502
}
1503

    
1504
static GHashTable *procmime_get_mime_type_table(void)
1505
{
1506
        GHashTable *table = NULL;
1507
        GList *cur;
1508
        MimeType *mime_type;
1509
        gchar **exts;
1510

    
1511
        if (!mime_type_list) {
1512
                GList *list;
1513
                gchar *dir;
1514

    
1515
#ifdef G_OS_WIN32
1516
                dir = g_strconcat(get_startup_dir(),
1517
                                  G_DIR_SEPARATOR_S "etc" G_DIR_SEPARATOR_S
1518
                                  "mime.types", NULL);
1519
                mime_type_list = procmime_get_mime_type_list(dir);
1520
                g_free(dir);
1521
#else
1522
                mime_type_list =
1523
                        procmime_get_mime_type_list(SYSCONFDIR "/mime.types");
1524
                if (!mime_type_list)
1525
                        mime_type_list =
1526
                                procmime_get_mime_type_list("/etc/mime.types");
1527
#endif
1528
                dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1529
                                  "mime.types", NULL);
1530
                list = procmime_get_mime_type_list(dir);
1531
                g_free(dir);
1532
                mime_type_list = g_list_concat(mime_type_list, list);
1533

    
1534
                if (!mime_type_list) {
1535
                        debug_print("mime.types not found\n");
1536
                        return NULL;
1537
                }
1538
        }
1539

    
1540
        table = g_hash_table_new(g_str_hash, g_str_equal);
1541

    
1542
        for (cur = mime_type_list; cur != NULL; cur = cur->next) {
1543
                gint i;
1544
                gchar *key;
1545

    
1546
                mime_type = (MimeType *)cur->data;
1547

    
1548
                if (!mime_type->extension) continue;
1549

    
1550
                exts = g_strsplit(mime_type->extension, " ", 16);
1551
                for (i = 0; exts[i] != NULL; i++) {
1552
                        /* make the key case insensitive */
1553
                        g_strdown(exts[i]);
1554
                        /* use previously dup'd key on overwriting */
1555
                        if (g_hash_table_lookup(table, exts[i]))
1556
                                key = exts[i];
1557
                        else
1558
                                key = g_strdup(exts[i]);
1559
                        g_hash_table_insert(table, key, mime_type);
1560
                }
1561
                g_strfreev(exts);
1562
        }
1563

    
1564
        return table;
1565
}
1566

    
1567
static GList *procmime_get_mime_type_list(const gchar *file)
1568
{
1569
        GList *list = NULL;
1570
        FILE *fp;
1571
        gchar buf[BUFFSIZE];
1572
        gchar *p;
1573
        gchar *delim;
1574
        MimeType *mime_type;
1575

    
1576
        if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1577

    
1578
        debug_print("Reading %s ...\n", file);
1579

    
1580
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1581
                p = strchr(buf, '#');
1582
                if (p) *p = '\0';
1583
                g_strstrip(buf);
1584

    
1585
                p = buf;
1586
                while (*p && !g_ascii_isspace(*p)) p++;
1587
                if (*p) {
1588
                        *p = '\0';
1589
                        p++;
1590
                }
1591
                delim = strchr(buf, '/');
1592
                if (delim == NULL) continue;
1593
                *delim = '\0';
1594

    
1595
                mime_type = g_new(MimeType, 1);
1596
                mime_type->type = g_strdup(buf);
1597
                mime_type->sub_type = g_strdup(delim + 1);
1598

    
1599
                while (*p && g_ascii_isspace(*p)) p++;
1600
                if (*p)
1601
                        mime_type->extension = g_strdup(p);
1602
                else
1603
                        mime_type->extension = NULL;
1604

    
1605
                list = g_list_append(list, mime_type);
1606
        }
1607

    
1608
        fclose(fp);
1609

    
1610
        if (!list)
1611
                g_warning("Can't read mime.types\n");
1612

    
1613
        return list;
1614
}
1615

    
1616
EncodingType procmime_get_encoding_for_charset(const gchar *charset)
1617
{
1618
        if (!charset)
1619
                return ENC_8BIT;
1620
        else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
1621
                 !g_ascii_strcasecmp(charset, "US-ASCII"))
1622
                return ENC_7BIT;
1623
        else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
1624
                 !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
1625
                 !g_ascii_strcasecmp(charset, "Windows-1251"))
1626
                return ENC_8BIT;
1627
        else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
1628
                return ENC_QUOTED_PRINTABLE;
1629
        else
1630
                return ENC_8BIT;
1631
}
1632

    
1633
EncodingType procmime_get_encoding_for_text_file(const gchar *file)
1634
{
1635
        FILE *fp;
1636
        guchar buf[BUFFSIZE];
1637
        size_t len;
1638
        size_t octet_chars = 0;
1639
        size_t total_len = 0;
1640
        gfloat octet_percentage;
1641

    
1642
        if ((fp = g_fopen(file, "rb")) == NULL) {
1643
                FILE_OP_ERROR(file, "fopen");
1644
                return ENC_UNKNOWN;
1645
        }
1646

    
1647
        while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1648
                guchar *p;
1649
                gint i;
1650

    
1651
                for (p = buf, i = 0; i < len; ++p, ++i) {
1652
                        if (*p & 0x80)
1653
                                ++octet_chars;
1654
                }
1655
                total_len += len;
1656
        }
1657

    
1658
        fclose(fp);
1659

    
1660
        if (total_len > 0)
1661
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1662
        else
1663
                octet_percentage = 0.0;
1664

    
1665
        debug_print("procmime_get_encoding_for_text_file(): "
1666
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1667
                    100.0 * octet_percentage);
1668

    
1669
        if (octet_percentage > 0.20) {
1670
                debug_print("using BASE64\n");
1671
                return ENC_BASE64;
1672
        } else if (octet_chars > 0) {
1673
                debug_print("using quoted-printable\n");
1674
                return ENC_QUOTED_PRINTABLE;
1675
        } else {
1676
                debug_print("using 7bit\n");
1677
                return ENC_7BIT;
1678
        }
1679
}
1680

    
1681
EncodingType procmime_get_encoding_for_str(const gchar *str)
1682
{
1683
        const guchar *p;
1684
        size_t octet_chars = 0;
1685
        size_t total_len = 0;
1686
        gfloat octet_percentage;
1687

    
1688
        total_len = strlen(str);
1689

    
1690
        for (p = (const guchar *)str; *p != '\0'; ++p) {
1691
                if (*p & 0x80)
1692
                        ++octet_chars;
1693
        }
1694

    
1695
        if (total_len > 0)
1696
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1697
        else
1698
                octet_percentage = 0.0;
1699

    
1700
        debug_print("procmime_get_encoding_for_str(): "
1701
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1702
                    100.0 * octet_percentage);
1703

    
1704
        if (octet_percentage > 0.20) {
1705
                debug_print("using BASE64\n");
1706
                return ENC_BASE64;
1707
        } else if (octet_chars > 0) {
1708
                debug_print("using quoted-printable\n");
1709
                return ENC_QUOTED_PRINTABLE;
1710
        } else {
1711
                debug_print("using 7bit\n");
1712
                return ENC_7BIT;
1713
        }
1714
}
1715

    
1716
const gchar *procmime_get_encoding_str(EncodingType encoding)
1717
{
1718
        static const gchar *encoding_str[] = {
1719
                "7bit", "8bit", "quoted-printable", "base64", "x-uuencode",
1720
                NULL
1721
        };
1722

    
1723
        if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
1724
                return encoding_str[encoding];
1725
        else
1726
                return NULL;
1727
}