Statistics
| Revision:

root / libsylph / procmime.c @ 2090

History | View | Annotate | Download (38.8 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2008 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
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include "defs.h"
25
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <locale.h>
31
#include <ctype.h>
32
33
#include "procmime.h"
34
#include "procheader.h"
35
#include "base64.h"
36
#include "quoted-printable.h"
37
#include "uuencode.h"
38
#include "html.h"
39
#include "codeconv.h"
40
#include "utils.h"
41
#include "prefs_common.h"
42
43
#define MAX_MIME_LEVEL        64
44
45
static GHashTable *procmime_get_mime_type_table        (void);
46
static GList *procmime_get_mime_type_list        (const gchar *file);
47
48
49
MimeInfo *procmime_mimeinfo_new(void)
50
{
51
        MimeInfo *mimeinfo;
52
53
        mimeinfo = g_new0(MimeInfo, 1);
54
        mimeinfo->mime_type     = MIME_UNKNOWN;
55
        mimeinfo->encoding_type = ENC_UNKNOWN;
56
57
        return mimeinfo;
58
}
59
60
void procmime_mimeinfo_free_all(MimeInfo *mimeinfo)
61
{
62
        while (mimeinfo != NULL) {
63
                MimeInfo *next;
64
65
                g_free(mimeinfo->encoding);
66
                g_free(mimeinfo->content_type);
67
                g_free(mimeinfo->charset);
68
                g_free(mimeinfo->name);
69
                g_free(mimeinfo->boundary);
70
                g_free(mimeinfo->content_disposition);
71
                g_free(mimeinfo->filename);
72
73
                g_free(mimeinfo->sigstatus);
74
                g_free(mimeinfo->sigstatus_full);
75
76
                procmime_mimeinfo_free_all(mimeinfo->sub);
77
                procmime_mimeinfo_free_all(mimeinfo->children);
78
                procmime_mimeinfo_free_all(mimeinfo->plaintext);
79
80
                next = mimeinfo->next;
81
                g_free(mimeinfo);
82
                mimeinfo = next;
83
        }
84
}
85
86
MimeInfo *procmime_mimeinfo_insert(MimeInfo *parent, MimeInfo *mimeinfo)
87
{
88
        MimeInfo *child = parent->children;
89
90
        if (!child)
91
                parent->children = mimeinfo;
92
        else {
93
                while (child->next != NULL)
94
                        child = child->next;
95
96
                child->next = mimeinfo;
97
        }
98
99
        mimeinfo->parent = parent;
100
        mimeinfo->level = parent->level + 1;
101
102
        return mimeinfo;
103
}
104
105
#if 0
106
void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new)
107
{
108
        MimeInfo *parent = old->parent;
109
        MimeInfo *child;
110
111
        g_return_if_fail(parent != NULL);
112
        g_return_if_fail(new->next == NULL);
113
114
        for (child = parent->children; child && child != old;
115
             child = child->next)
116
                ;
117
        if (!child) {
118
                g_warning("oops: parent can't find it's own child");
119
                return;
120
        }
121
        procmime_mimeinfo_free_all(old);
122
123
        if (child == parent->children) {
124
                new->next = parent->children->next;
125
                parent->children = new;
126
        } else {
127
                new->next = child->next;
128
                child = new;
129
        }
130
}
131
#endif
132
133
MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo)
134
{
135
        if (!mimeinfo) return NULL;
136
137
        if (mimeinfo->children)
138
                return mimeinfo->children;
139
        if (mimeinfo->sub)
140
                return mimeinfo->sub;
141
        if (mimeinfo->next)
142
                return mimeinfo->next;
143
144
        if (mimeinfo->main) {
145
                mimeinfo = mimeinfo->main;
146
                if (mimeinfo->next)
147
                        return mimeinfo->next;
148
        }
149
150
        for (mimeinfo = mimeinfo->parent; mimeinfo != NULL;
151
             mimeinfo = mimeinfo->parent) {
152
                if (mimeinfo->next)
153
                        return mimeinfo->next;
154
                if (mimeinfo->main) {
155
                        mimeinfo = mimeinfo->main;
156
                        if (mimeinfo->next)
157
                                return mimeinfo->next;
158
                }
159
        }
160
161
        return NULL;
162
}
163
164
#if 0
165
void procmime_dump_mimeinfo(MimeInfo *mimeinfo)
166
{
167
        gint i;
168
169
        g_print("\n");
170
171
        for (; mimeinfo != NULL; mimeinfo = procmime_mimeinfo_next(mimeinfo)) {
172
                for (i = 0; i < mimeinfo->level; i++)
173
                        g_print("  ");
174
                g_print("%s%s\n", mimeinfo->main ? "sub: " : "",
175
                        mimeinfo->content_type);
176
        }
177
}
178
#endif
179
180
MimeInfo *procmime_scan_message(MsgInfo *msginfo)
181
{
182
        FILE *fp;
183
        MimeInfo *mimeinfo;
184
185
        g_return_val_if_fail(msginfo != NULL, NULL);
186
187
        if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
188
                return NULL;
189
190
        if (mimeinfo) {
191
                mimeinfo->size = msginfo->size;
192
                mimeinfo->content_size = get_left_file_size(fp);
193
                if (mimeinfo->encoding_type == ENC_BASE64)
194
                        mimeinfo->content_size = mimeinfo->content_size / 4 * 3;
195
                if (mimeinfo->mime_type == MIME_MULTIPART ||
196
                    mimeinfo->mime_type == MIME_MESSAGE_RFC822)
197
                        procmime_scan_multipart_message(mimeinfo, fp);
198
        }
199
200
        fclose(fp);
201
202
        return mimeinfo;
203
}
204
205
void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp)
206
{
207
        gchar *p;
208
        gchar *boundary;
209
        gint boundary_len = 0;
210
        gchar *buf;
211
        glong fpos, prev_fpos;
212
213
        g_return_if_fail(mimeinfo != NULL);
214
        g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART ||
215
                         mimeinfo->mime_type == MIME_MESSAGE_RFC822);
216
217
        if (mimeinfo->mime_type == MIME_MULTIPART) {
218
                g_return_if_fail(mimeinfo->boundary != NULL);
219
                g_return_if_fail(mimeinfo->sub == NULL);
220
        }
221
        g_return_if_fail(fp != NULL);
222
223
        buf = g_malloc(BUFFSIZE);
224
225
        boundary = mimeinfo->boundary;
226
227
        if (boundary) {
228
                boundary_len = strlen(boundary);
229
230
                /* look for first boundary */
231
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL)
232
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
233
                if (!p) {
234
                        g_free(buf);
235
                        return;
236
                }
237
        } else if (mimeinfo->parent && mimeinfo->parent->boundary) {
238
                boundary = mimeinfo->parent->boundary;
239
                boundary_len = strlen(boundary);
240
        }
241
242
        if ((fpos = ftell(fp)) < 0) {
243
                perror("ftell");
244
                g_free(buf);
245
                return;
246
        }
247
248
        debug_print("level = %d\n", mimeinfo->level);
249
250
        for (;;) {
251
                MimeInfo *partinfo;
252
                gboolean eom = FALSE;
253
                glong content_pos;
254
                gboolean is_base64;
255
                gint len;
256
                guint b64_content_len = 0;
257
                gint b64_pad_len = 0;
258
259
                prev_fpos = fpos;
260
                debug_print("prev_fpos: %ld\n", fpos);
261
262
                /* scan part header */
263
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
264
                        MimeInfo *sub;
265
266
                        mimeinfo->sub = sub = procmime_scan_mime_header(fp);
267
                        if (!sub) break;
268
269
                        debug_print("message/rfc822 part (content-type: %s)\n",
270
                                    sub->content_type);
271
                        sub->level = mimeinfo->level + 1;
272
                        sub->parent = mimeinfo->parent;
273
                        sub->main = mimeinfo;
274
275
                        partinfo = sub;
276
                } else {
277
                        partinfo = procmime_scan_mime_header(fp);
278
                        if (!partinfo) break;
279
                        procmime_mimeinfo_insert(mimeinfo, partinfo);
280
                        debug_print("content-type: %s\n",
281
                                    partinfo->content_type);
282
                }
283
284
                /* begin content */
285
                content_pos = ftell(fp);
286
                debug_print("content_pos: %ld\n", content_pos);
287
288
                if (partinfo->mime_type == MIME_MULTIPART ||
289
                    partinfo->mime_type == MIME_MESSAGE_RFC822) {
290
                        if (partinfo->level < MAX_MIME_LEVEL)
291
                                procmime_scan_multipart_message(partinfo, fp);
292
                }
293
294
                /* look for next boundary */
295
                buf[0] = '\0';
296
                is_base64 = partinfo->encoding_type == ENC_BASE64;
297
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL) {
298
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) {
299
                                if (buf[2 + boundary_len]     == '-' &&
300
                                    buf[2 + boundary_len + 1] == '-')
301
                                        eom = TRUE;
302
                                break;
303
                        } else if (is_base64) {
304
                                const gchar *s;
305
                                for (s = buf; *s && *s != '\r' && *s != '\n';
306
                                     ++s)
307
                                        if (*s == '=')
308
                                                ++b64_pad_len;
309
                                b64_content_len += s - buf;
310
                        }
311
                }
312
                if (p == NULL) {
313
                        /* broken MIME, or single part MIME message */
314
                        buf[0] = '\0';
315
                        eom = TRUE;
316
                }
317
                debug_print("boundary: %s\n", buf);
318
319
                fpos = ftell(fp);
320
                debug_print("fpos: %ld\n", fpos);
321
322
                len = strlen(buf);
323
                partinfo->size = fpos - prev_fpos - len;
324
                if (is_base64)
325
                        partinfo->content_size =
326
                                b64_content_len / 4 * 3 - b64_pad_len;
327
                else
328
                        partinfo->content_size = fpos - content_pos - len;
329
                debug_print("partinfo->size: %d\n", partinfo->size);
330
                debug_print("partinfo->content_size: %d\n",
331
                            partinfo->content_size);
332
                if (partinfo->sub && !partinfo->sub->sub &&
333
                    !partinfo->sub->children) {
334
                        partinfo->sub->size =
335
                                fpos - partinfo->sub->fpos - strlen(buf);
336
                        debug_print("partinfo->sub->size: %d\n",
337
                                    partinfo->sub->size);
338
                }
339
340
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
341
                        if (len > 0 && fseek(fp, fpos - len, SEEK_SET) < 0)
342
                                perror("fseek");
343
                        break;
344
                }
345
346
                if (eom) break;
347
        }
348
349
        g_free(buf);
350
}
351
352
void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding)
353
{
354
        gchar *buf;
355
356
        Xstrdup_a(buf, encoding, return);
357
358
        g_free(mimeinfo->encoding);
359
360
        mimeinfo->encoding = g_strdup(g_strstrip(buf));
361
        if (!g_ascii_strncasecmp(buf, "7bit", 4))
362
                mimeinfo->encoding_type = ENC_7BIT;
363
        else if (!g_ascii_strncasecmp(buf, "8bit", 4))
364
                mimeinfo->encoding_type = ENC_8BIT;
365
        else if (!g_ascii_strncasecmp(buf, "quoted-printable", 16))
366
                mimeinfo->encoding_type = ENC_QUOTED_PRINTABLE;
367
        else if (!g_ascii_strncasecmp(buf, "base64", 6))
368
                mimeinfo->encoding_type = ENC_BASE64;
369
        else if (!g_ascii_strncasecmp(buf, "x-uuencode", 10))
370
                mimeinfo->encoding_type = ENC_X_UUENCODE;
371
        else
372
                mimeinfo->encoding_type = ENC_UNKNOWN;
373
374
}
375
376
void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type)
377
{
378
        g_free(mimeinfo->content_type);
379
        g_free(mimeinfo->charset);
380
        g_free(mimeinfo->name);
381
        g_free(mimeinfo->boundary);
382
        mimeinfo->content_type = NULL;
383
        mimeinfo->charset      = NULL;
384
        mimeinfo->name         = NULL;
385
        mimeinfo->boundary     = NULL;
386
387
        procmime_scan_content_type_str(content_type, &mimeinfo->content_type,
388
                                       &mimeinfo->charset, &mimeinfo->name,
389
                                       &mimeinfo->boundary);
390
391
        mimeinfo->mime_type = procmime_scan_mime_type(mimeinfo->content_type);
392
        if (mimeinfo->mime_type == MIME_MULTIPART && !mimeinfo->boundary)
393
                mimeinfo->mime_type = MIME_TEXT;
394
}
395
396
typedef struct
397
{
398
        gchar *name;
399
        gchar *value;
400
} MimeParam;
401
402
typedef struct
403
{
404
        gchar *hvalue;
405
        GSList *plist;
406
} MimeParams;
407
408
static gchar *procmime_find_parameter_delimiter(const gchar *param,
409
                                                const gchar **eq)
410
{
411
        register const gchar *p = param;
412
        gboolean quoted = FALSE;
413
        const gchar *delim = NULL;
414
415
        while (*p) {
416
                if (*p == '=')
417
                        break;
418
                else if (*p == ';' || *p == '\r' || *p == '\n') {
419
                        delim = p;
420
                        break;
421
                }
422
                ++p;
423
        }
424
        if (*p != '=') {
425
                *eq = NULL;
426
                return (gchar *)delim;
427
        }
428
        *eq = p;
429
430
        ++p;
431
        while (g_ascii_isspace(*p))
432
                ++p;
433
        if (*p == '"') {
434
                quoted = TRUE;
435
                ++p;
436
        }
437
438
        while (*p) {
439
                if (quoted == TRUE) {
440
                        if (*p == '"')
441
                                quoted = FALSE;
442
                } else if (*p == ';' || *p == '\r' || *p == '\n') {
443
                        delim = p;
444
                        break;
445
                }
446
                ++p;
447
        }
448
449
        return (gchar *)delim;
450
}
451
452
static gchar *procmime_convert_value(const gchar *value, const gchar *charset)
453
{
454
        if (charset) {
455
                gchar *utf8_value;
456
457
                utf8_value = conv_codeset_strdup(value, charset, CS_INTERNAL);
458
                if (utf8_value)
459
                        return utf8_value;
460
        }
461
462
        return g_strdup(value);
463
}
464
465
static MimeParams *procmime_parse_mime_parameter(const gchar *str)
466
{
467
        ConvADType ad_type;
468
        gchar *tmp = NULL;
469
        gchar *hvalue;
470
        gchar *param, *name, *value;
471
        gchar *charset = NULL, *lang = NULL;
472
        const gchar *p, *delim;
473
        gint count, prev_count;
474
        gchar *cont_name;
475
        gchar *cont_value;
476
        MimeParam *mparam;
477
        MimeParams *mparams;
478
        GSList *plist = NULL;
479
480
        if ((p = strchr(str, ';')))
481
                hvalue = g_strndup(str, p - str);
482
        else
483
                hvalue = g_strdup(str);
484
485
        g_strstrip(hvalue);
486
487
        mparams = g_new(MimeParams, 1);
488
        mparams->hvalue = hvalue;
489
        mparams->plist = NULL;
490
491
        if (!p)
492
                return mparams;
493
        ++p;
494
495
        /* workaround for raw-JIS filename (Eudora etc.) */
496
        ad_type = conv_get_autodetect_type();
497
        if ((ad_type == C_AD_JAPANESE ||
498
             (ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) &&
499
            strstr(p, "\033$") != NULL) {
500
                CodeConvFunc conv_func;
501
                conv_func = conv_get_code_conv_func(NULL, NULL);
502
                tmp = conv_func(p, NULL);
503
                p = tmp;
504
                debug_print("procmime_parse_mime_parameter(): raw-JIS header body detected: %s\n", str);
505
        }
506
507
        count = prev_count = -1;
508
        cont_name = cont_value = NULL;
509
510
        for (;;) {
511
                gboolean encoded = FALSE;
512
                gchar *begin;
513
                gchar *dec_value;
514
                const gchar *eq;
515
                gchar *ast = NULL;
516
517
                while (*p == ';' || g_ascii_isspace(*p))
518
                        ++p;
519
                if (*p == '\0')
520
                        break;
521
522
                delim = procmime_find_parameter_delimiter(p, &eq);
523
                if (!eq)
524
                        break;
525
                if (delim)
526
                        param = g_strndup(p, delim - p);
527
                else
528
                        param = g_strdup(p);
529
530
                name = g_strndup(p, eq - p);
531
                g_strchomp(name);
532
                if (*name != '*' && (ast = strchr(name, '*'))) {
533
                        const gchar *next = ast + 1;
534
535
                        if (*next == '\0') {
536
                                encoded = TRUE;
537
                        } else if (g_ascii_isdigit(*next)) {
538
                                count = atoi(next);
539
                                while (g_ascii_isdigit(*next))
540
                                        ++next;
541
                                if (*next == '*')
542
                                        encoded = TRUE;
543
                                if (prev_count + 1 != count) {
544
                                        g_warning("procmime_parse_mime_parameter(): invalid count: %s\n", str);
545
                                        g_free(name);
546
                                        g_free(param);
547
                                        break;
548
                                }
549
                        } else {
550
                                g_warning("procmime_parse_mime_parameter(): invalid name: %s\n", str);
551
                                g_free(name);
552
                                g_free(param);
553
                                break;
554
                        }
555
556
                        *ast = '\0';
557
                }
558
559
                value = g_strdup(param + (eq - p) + 1);
560
                g_strstrip(value);
561
                if (*value == '"')
562
                        extract_quote(value, '"');
563
564
                begin = value;
565
566
                if (encoded) {
567
                        gchar *sq1, *sq2;
568
569
                        if ((sq1 = strchr(value, '\''))) {
570
                                if (sq1 > value) {
571
                                        if (charset)
572
                                                g_free(charset);
573
                                        charset = g_strndup(value, sq1 - value);
574
                                }
575
                                if ((sq2 = strchr(sq1 + 1, '\''))) {
576
                                        if (sq2 > sq1 + 1) {
577
                                                if (lang)
578
                                                        g_free(lang);
579
                                                lang = g_strndup(sq1 + 1,
580
                                                                 sq2 - sq1 - 1);
581
                                        }
582
                                        begin = sq2 + 1;
583
                                }
584
                        }
585
                }
586
587
#define CONCAT_CONT_VALUE(s)                                \
588
{                                                        \
589
        if (cont_value) {                                \
590
                gchar *tmp;                                \
591
                tmp = g_strconcat(cont_value, s, NULL);        \
592
                g_free(cont_value);                        \
593
                cont_value = tmp;                        \
594
        } else                                                \
595
                cont_value = g_strdup(s);                \
596
}
597
598
                if (count >= 0) {
599
                        if (count > 0 && cont_name) {
600
                                if (strcmp(cont_name, name) != 0) {
601
                                        g_warning("procmime_parse_mime_parameter(): mismatch parameter name: %s\n", str);
602
                                        g_free(name);
603
                                        g_free(value);
604
                                        g_free(param);
605
                                        break;
606
                                }
607
                        } else
608
                                cont_name = g_strdup(name);
609
610
                        if (encoded) {
611
                                dec_value = g_malloc(strlen(begin) + 1);
612
                                decode_xdigit_encoded_str(dec_value, begin);
613
                                CONCAT_CONT_VALUE(dec_value);
614
                                g_free(dec_value);
615
                        } else {
616
                                CONCAT_CONT_VALUE(begin);
617
                        }
618
                }
619
620
#undef CONCAT_CONT_VALUE
621
622
                if (count == -1 && cont_name && cont_value) {
623
                        mparam = g_new(MimeParam, 1);
624
                        mparam->name = cont_name;
625
                        cont_name = NULL;
626
                        mparam->value = procmime_convert_value
627
                                (cont_value, charset);
628
                        g_free(cont_value);
629
                        cont_value = NULL;
630
                        plist = g_slist_prepend(plist, mparam);
631
                }
632
633
                if (count == -1) {
634
                        mparam = g_new(MimeParam, 1);
635
                        mparam->name = name;
636
                        if (encoded) {
637
                                dec_value = g_malloc(strlen(begin) + 1);
638
                                decode_xdigit_encoded_str(dec_value, begin);
639
                                mparam->value = procmime_convert_value
640
                                        (dec_value, charset);
641
                                g_free(dec_value);
642
                        } else {
643
                                if (!ast &&
644
                                    (!g_ascii_strcasecmp(name, "name") ||
645
                                     !g_ascii_strcasecmp(name, "filename")))
646
                                        mparam->value =
647
                                                conv_unmime_header(begin, NULL);
648
                                else
649
                                        mparam->value = g_strdup(begin);
650
                        }
651
                        name = NULL;
652
                        plist = g_slist_prepend(plist, mparam);
653
                }
654
655
                g_free(name);
656
                g_free(value);
657
                g_free(param);
658
659
                prev_count = count;
660
                count = -1;
661
662
                if (delim)
663
                        p = delim + 1;
664
                else
665
                        break;
666
        }
667
668
        if (cont_name && cont_value) {
669
                mparam = g_new(MimeParam, 1);
670
                mparam->name = cont_name;
671
                cont_name = NULL;
672
                mparam->value = procmime_convert_value(cont_value, charset);
673
                plist = g_slist_prepend(plist, mparam);
674
        }
675
676
        g_free(cont_name);
677
        g_free(cont_value);
678
        g_free(lang);
679
        g_free(charset);
680
        if (tmp)
681
                g_free(tmp);
682
683
        plist = g_slist_reverse(plist);
684
        mparams->plist = plist;
685
686
        return mparams;
687
}
688
689
static void procmime_mime_params_free(MimeParams *mparams)
690
{
691
        GSList *cur;
692
693
        if (!mparams)
694
                return;
695
696
        g_free(mparams->hvalue);
697
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
698
                MimeParam *mparam = (MimeParam *)cur->data;
699
                g_free(mparam->name);
700
                g_free(mparam->value);
701
                g_free(mparam);
702
        }
703
        g_slist_free(mparams->plist);
704
        g_free(mparams);
705
}
706
707
void procmime_scan_content_type_str(const gchar *content_type,
708
                                    gchar **mime_type, gchar **charset,
709
                                    gchar **name, gchar **boundary)
710
{
711
        MimeParams *mparams;
712
        GSList *cur;
713
714
        mparams = procmime_parse_mime_parameter(content_type);
715
716
        if (mime_type)
717
                *mime_type = g_strdup(mparams->hvalue);
718
719
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
720
                MimeParam *param = (MimeParam *)cur->data;
721
                if (charset && !g_ascii_strcasecmp(param->name, "charset")) {
722
                        *charset = g_strdup(param->value);
723
                        charset = NULL;
724
                } else if (name && !g_ascii_strcasecmp(param->name, "name")) {
725
                        *name = g_strdup(param->value);
726
                        name = NULL;
727
                } else if (boundary &&
728
                           !g_ascii_strcasecmp(param->name, "boundary")) {
729
                        *boundary = g_strdup(param->value);
730
                        boundary = NULL;
731
                }
732
        }
733
734
        procmime_mime_params_free(mparams);
735
}
736
737
void procmime_scan_content_disposition(MimeInfo *mimeinfo,
738
                                       const gchar *content_disposition)
739
{
740
        MimeParams *mparams;
741
        GSList *cur;
742
743
        mparams = procmime_parse_mime_parameter(content_disposition);
744
745
        mimeinfo->content_disposition = g_strdup(mparams->hvalue);
746
747
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
748
                MimeParam *param = (MimeParam *)cur->data;
749
                if (!g_ascii_strcasecmp(param->name, "filename")) {
750
                        mimeinfo->filename = g_strdup(param->value);
751
                        break;
752
                }
753
        }
754
755
        procmime_mime_params_free(mparams);
756
}
757
758
enum
759
{
760
        H_CONTENT_TRANSFER_ENCODING = 0,
761
        H_CONTENT_TYPE                    = 1,
762
        H_CONTENT_DISPOSITION            = 2
763
};
764
765
MimeInfo *procmime_scan_mime_header(FILE *fp)
766
{
767
        static HeaderEntry hentry[] = {{"Content-Transfer-Encoding:",
768
                                                          NULL, FALSE},
769
                                       {"Content-Type:", NULL, TRUE},
770
                                       {"Content-Disposition:",
771
                                                          NULL, TRUE},
772
                                       {NULL,                  NULL, FALSE}};
773
        gchar buf[BUFFSIZE];
774
        gint hnum;
775
        HeaderEntry *hp;
776
        MimeInfo *mimeinfo;
777
778
        g_return_val_if_fail(fp != NULL, NULL);
779
780
        mimeinfo = procmime_mimeinfo_new();
781
        mimeinfo->mime_type = MIME_TEXT;
782
        mimeinfo->encoding_type = ENC_7BIT;
783
        mimeinfo->fpos = ftell(fp);
784
785
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
786
               != -1) {
787
                hp = hentry + hnum;
788
789
                if (H_CONTENT_TRANSFER_ENCODING == hnum) {
790
                        procmime_scan_encoding
791
                                (mimeinfo, buf + strlen(hp->name));
792
                } else if (H_CONTENT_TYPE == hnum) {
793
                        procmime_scan_content_type
794
                                (mimeinfo, buf + strlen(hp->name));
795
                } else if (H_CONTENT_DISPOSITION == hnum) {
796
                        procmime_scan_content_disposition
797
                                (mimeinfo, buf + strlen(hp->name));
798
                }
799
        }
800
801
        if (mimeinfo->mime_type == MIME_APPLICATION_OCTET_STREAM &&
802
            (mimeinfo->filename || mimeinfo->name)) {
803
                const gchar *type;
804
                type = procmime_get_mime_type
805
                        (mimeinfo->filename ? mimeinfo->filename
806
                         : mimeinfo->name);
807
                if (type)
808
                        mimeinfo->mime_type = procmime_scan_mime_type(type);
809
        }
810
811
        if (!mimeinfo->content_type)
812
                mimeinfo->content_type = g_strdup("text/plain");
813
814
        return mimeinfo;
815
}
816
817
FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
818
{
819
        gchar buf[BUFFSIZE];
820
        gchar *boundary = NULL;
821
        gint boundary_len = 0;
822
        gboolean tmp_file = FALSE;
823
        gboolean normalize_lbreak = FALSE;
824
        ContentType content_type;
825
826
        g_return_val_if_fail(infp != NULL, NULL);
827
        g_return_val_if_fail(mimeinfo != NULL, NULL);
828
829
        if (!outfp) {
830
                outfp = my_tmpfile();
831
                if (!outfp) {
832
                        perror("tmpfile");
833
                        return NULL;
834
                }
835
                tmp_file = TRUE;
836
        }
837
838
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
839
                boundary = mimeinfo->parent->boundary;
840
                boundary_len = strlen(boundary);
841
        }
842
843
        content_type = procmime_scan_mime_type(mimeinfo->content_type);
844
        if (content_type == MIME_TEXT ||
845
            content_type == MIME_TEXT_HTML) {
846
                normalize_lbreak = TRUE;
847
        }
848
849
        if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) {
850
                FILE *tmpfp = outfp;
851
852
                if (normalize_lbreak) {
853
                        tmpfp = my_tmpfile();
854
                        if (!tmpfp) {
855
                                perror("tmpfile");
856
                                if (tmp_file) fclose(outfp);
857
                                return NULL;
858
                        }
859
                }
860
861
                while (fgets(buf, sizeof(buf), infp) != NULL &&
862
                       (!boundary ||
863
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
864
                        gint len;
865
                        len = qp_decode_line(buf);
866
                        fwrite(buf, len, 1, tmpfp);
867
                }
868
869
                if (normalize_lbreak) {
870
                        if (fflush(tmpfp) == EOF) {
871
                                perror("fflush");
872
                                fclose(tmpfp);
873
                                if (tmp_file) fclose(outfp);
874
                                return NULL;
875
                        }
876
                        rewind(tmpfp);
877
                        while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
878
#ifdef G_OS_WIN32
879
                                strretchomp(buf);
880
                                fputs(buf, outfp);
881
                                fputs("\r\n", outfp);
882
#else
883
                                strcrchomp(buf);
884
                                fputs(buf, outfp);
885
#endif
886
                        }
887
                        fclose(tmpfp);
888
                }
889
        } else if (mimeinfo->encoding_type == ENC_BASE64) {
890
                gchar outbuf[BUFFSIZE];
891
                gint len;
892
                Base64Decoder *decoder;
893
                FILE *tmpfp = outfp;
894
895
                if (normalize_lbreak) {
896
                        tmpfp = my_tmpfile();
897
                        if (!tmpfp) {
898
                                perror("tmpfile");
899
                                if (tmp_file) fclose(outfp);
900
                                return NULL;
901
                        }
902
                }
903
904
                decoder = base64_decoder_new();
905
                while (fgets(buf, sizeof(buf), infp) != NULL &&
906
                       (!boundary ||
907
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
908
                        len = base64_decoder_decode(decoder, buf,
909
                                                    (guchar *)outbuf);
910
                        if (len < 0) {
911
                                g_warning("Bad BASE64 content\n");
912
                                break;
913
                        }
914
                        fwrite(outbuf, sizeof(gchar), len, tmpfp);
915
                }
916
                base64_decoder_free(decoder);
917
918
                if (normalize_lbreak) {
919
                        if (fflush(tmpfp) == EOF) {
920
                                perror("fflush");
921
                                fclose(tmpfp);
922
                                if (tmp_file) fclose(outfp);
923
                                return NULL;
924
                        }
925
                        rewind(tmpfp);
926
                        while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
927
#ifdef G_OS_WIN32
928
                                strretchomp(buf);
929
                                fputs(buf, outfp);
930
                                fputs("\r\n", outfp);
931
#else
932
                                strcrchomp(buf);
933
                                fputs(buf, outfp);
934
#endif
935
                        }
936
                        fclose(tmpfp);
937
                }
938
        } else if (mimeinfo->encoding_type == ENC_X_UUENCODE) {
939
                gchar outbuf[BUFFSIZE];
940
                gint len;
941
                gboolean flag = FALSE;
942
943
                while (fgets(buf, sizeof(buf), infp) != NULL &&
944
                       (!boundary ||
945
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
946
                        if(!flag && strncmp(buf,"begin ", 6)) continue;
947
948
                        if (flag) {
949
                                len = fromuutobits(outbuf, buf);
950
                                if (len <= 0) {
951
                                        if (len < 0) 
952
                                                g_warning("Bad UUENCODE content(%d)\n", len);
953
                                        break;
954
                                }
955
                                fwrite(outbuf, sizeof(gchar), len, outfp);
956
                        } else
957
                                flag = TRUE;
958
                }
959
        } else {
960
                while (fgets(buf, sizeof(buf), infp) != NULL &&
961
                       (!boundary ||
962
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
963
                        if (normalize_lbreak) {
964
#ifdef G_OS_WIN32
965
                                strretchomp(buf);
966
                                fputs(buf, outfp);
967
                                fputs("\r\n", outfp);
968
#else
969
                                strcrchomp(buf);
970
                                fputs(buf, outfp);
971
#endif
972
                        } else
973
                                fputs(buf, outfp);
974
                }
975
        }
976
977
        if (fflush(outfp) == EOF)
978
                perror("fflush");
979
        if (ferror(outfp) != 0) {
980
                g_warning("procmime_decode_content(): Can't write to temporary file\n");
981
                if (tmp_file) fclose(outfp);
982
                return NULL;
983
        }
984
985
        if (tmp_file) rewind(outfp);
986
        return outfp;
987
}
988
989
gint procmime_get_part(const gchar *outfile, const gchar *infile,
990
                       MimeInfo *mimeinfo)
991
{
992
        FILE *infp;
993
        gint ret;
994
995
        g_return_val_if_fail(outfile != NULL, -1);
996
        g_return_val_if_fail(infile != NULL, -1);
997
        g_return_val_if_fail(mimeinfo != NULL, -1);
998
999
        if ((infp = g_fopen(infile, "rb")) == NULL) {
1000
                FILE_OP_ERROR(infile, "fopen");
1001
                return -1;
1002
        }
1003
        ret = procmime_get_part_fp(outfile, infp, mimeinfo);
1004
        fclose(infp);
1005
1006
        return ret;
1007
}
1008
1009
gint procmime_get_part_fp(const gchar *outfile, FILE *infp, MimeInfo *mimeinfo)
1010
{
1011
        FILE *outfp;
1012
        gchar buf[BUFFSIZE];
1013
1014
        g_return_val_if_fail(outfile != NULL, -1);
1015
        g_return_val_if_fail(infp != NULL, -1);
1016
        g_return_val_if_fail(mimeinfo != NULL, -1);
1017
1018
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1019
                FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
1020
                return -1;
1021
        }
1022
        if ((outfp = g_fopen(outfile, "wb")) == NULL) {
1023
                FILE_OP_ERROR(outfile, "fopen");
1024
                return -1;
1025
        }
1026
1027
        while (fgets(buf, sizeof(buf), infp) != NULL)
1028
                if (buf[0] == '\r' || buf[0] == '\n') break;
1029
1030
        if (procmime_decode_content(outfp, infp, mimeinfo) == NULL) {
1031
                fclose(outfp);
1032
                g_unlink(outfile);
1033
                return -1;
1034
        }
1035
1036
        if (fclose(outfp) == EOF) {
1037
                FILE_OP_ERROR(outfile, "fclose");
1038
                g_unlink(outfile);
1039
                return -1;
1040
        }
1041
1042
        return 0;
1043
}
1044
1045
gint procmime_get_all_parts(const gchar *dir, const gchar *infile,
1046
                            MimeInfo *mimeinfo)
1047
{
1048
        FILE *fp;
1049
        MimeInfo *partinfo;
1050
        gchar *base, *filename;
1051
1052
        g_return_val_if_fail(dir != NULL, -1);
1053
        g_return_val_if_fail(infile != NULL, -1);
1054
        g_return_val_if_fail(mimeinfo != NULL, -1);
1055
1056
        if (!is_dir_exist(dir)) {
1057
                g_warning("%s: directory not exist.\n", dir);
1058
                return -1;
1059
        }
1060
1061
        if ((fp = g_fopen(infile, "rb")) == NULL) {
1062
                FILE_OP_ERROR(infile, "fopen");
1063
                return -1;
1064
        }
1065
1066
        for (partinfo = mimeinfo; partinfo != NULL;
1067
             partinfo = procmime_mimeinfo_next(partinfo)) {
1068
                if (partinfo->filename || partinfo->name) {
1069
                        gint count = 1;
1070
1071
                        base = procmime_get_part_file_name(partinfo);
1072
                        filename = g_strconcat(dir, G_DIR_SEPARATOR_S, base,
1073
                                               NULL);
1074
1075
                        while (is_file_entry_exist(filename)) {
1076
                                gchar *base_alt;
1077
1078
                                base_alt = get_alt_filename(base, count++);
1079
                                g_free(filename);
1080
                                filename = g_strconcat
1081
                                        (dir, G_DIR_SEPARATOR_S, base_alt,
1082
                                         NULL);
1083
                                g_free(base_alt);
1084
                        }
1085
1086
                        procmime_get_part_fp(filename, fp, partinfo);
1087
1088
                        g_free(filename);
1089
                        g_free(base);
1090
                }
1091
        }
1092
1093
        fclose(fp);
1094
1095
        return 0;
1096
}
1097
1098
FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp,
1099
                                const gchar *encoding)
1100
{
1101
        FILE *tmpfp, *outfp;
1102
        const gchar *src_encoding;
1103
        gboolean conv_fail = FALSE;
1104
        gchar buf[BUFFSIZE];
1105
1106
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1107
        g_return_val_if_fail(infp != NULL, NULL);
1108
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1109
                             mimeinfo->mime_type == MIME_TEXT_HTML, NULL);
1110
1111
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1112
                perror("fseek");
1113
                return NULL;
1114
        }
1115
1116
        while (fgets(buf, sizeof(buf), infp) != NULL)
1117
                if (buf[0] == '\r' || buf[0] == '\n') break;
1118
1119
        tmpfp = procmime_decode_content(NULL, infp, mimeinfo);
1120
        if (!tmpfp)
1121
                return NULL;
1122
1123
        if ((outfp = my_tmpfile()) == NULL) {
1124
                perror("tmpfile");
1125
                fclose(tmpfp);
1126
                return NULL;
1127
        }
1128
1129
        src_encoding = prefs_common.force_charset ? prefs_common.force_charset
1130
                : mimeinfo->charset ? mimeinfo->charset
1131
                : prefs_common.default_encoding;
1132
1133
        if (mimeinfo->mime_type == MIME_TEXT) {
1134
                while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1135
                        gchar *str;
1136
1137
                        str = conv_codeset_strdup(buf, src_encoding, encoding);
1138
                        if (str) {
1139
                                fputs(str, outfp);
1140
                                g_free(str);
1141
                        } else {
1142
                                conv_fail = TRUE;
1143
                                fputs(buf, outfp);
1144
                        }
1145
                }
1146
        } else if (mimeinfo->mime_type == MIME_TEXT_HTML) {
1147
                HTMLParser *parser;
1148
                CodeConverter *conv;
1149
                const gchar *str;
1150
1151
                conv = conv_code_converter_new(src_encoding, encoding);
1152
                parser = html_parser_new(tmpfp, conv);
1153
                while ((str = html_parse(parser)) != NULL) {
1154
                        fputs(str, outfp);
1155
                }
1156
                html_parser_destroy(parser);
1157
                conv_code_converter_destroy(conv);
1158
        }
1159
1160
        if (conv_fail)
1161
                g_warning(_("procmime_get_text_content(): Code conversion failed.\n"));
1162
1163
        fclose(tmpfp);
1164
        if (fflush(outfp) == EOF) {
1165
                perror("fflush");
1166
                fclose(outfp);
1167
                return NULL;
1168
        }
1169
        rewind(outfp);
1170
1171
        return outfp;
1172
}
1173
1174
/* search the first text part of (multipart) MIME message,
1175
   decode, convert it and output to outfp. */
1176
FILE *procmime_get_first_text_content(MsgInfo *msginfo, const gchar *encoding)
1177
{
1178
        FILE *infp, *outfp = NULL;
1179
        MimeInfo *mimeinfo, *partinfo;
1180
1181
        g_return_val_if_fail(msginfo != NULL, NULL);
1182
1183
        mimeinfo = procmime_scan_message(msginfo);
1184
        if (!mimeinfo) return NULL;
1185
1186
        if ((infp = procmsg_open_message(msginfo)) == NULL) {
1187
                procmime_mimeinfo_free_all(mimeinfo);
1188
                return NULL;
1189
        }
1190
1191
        partinfo = mimeinfo;
1192
        while (partinfo && partinfo->mime_type != MIME_TEXT)
1193
                partinfo = procmime_mimeinfo_next(partinfo);
1194
        if (!partinfo) {
1195
                partinfo = mimeinfo;
1196
                while (partinfo && partinfo->mime_type != MIME_TEXT_HTML)
1197
                        partinfo = procmime_mimeinfo_next(partinfo);
1198
        }
1199
1200
        if (partinfo)
1201
                outfp = procmime_get_text_content(partinfo, infp, encoding);
1202
1203
        fclose(infp);
1204
        procmime_mimeinfo_free_all(mimeinfo);
1205
1206
        return outfp;
1207
}
1208
1209
gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename,
1210
                                   const gchar *str, StrFindFunc find_func)
1211
{
1212
1213
        FILE *infp, *outfp;
1214
        gchar buf[BUFFSIZE];
1215
1216
        g_return_val_if_fail(mimeinfo != NULL, FALSE);
1217
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1218
                             mimeinfo->mime_type == MIME_TEXT_HTML, FALSE);
1219
        g_return_val_if_fail(str != NULL, FALSE);
1220
        g_return_val_if_fail(find_func != NULL, FALSE);
1221
1222
        if ((infp = g_fopen(filename, "rb")) == NULL) {
1223
                FILE_OP_ERROR(filename, "fopen");
1224
                return FALSE;
1225
        }
1226
1227
        outfp = procmime_get_text_content(mimeinfo, infp, NULL);
1228
        fclose(infp);
1229
1230
        if (!outfp)
1231
                return FALSE;
1232
1233
        while (fgets(buf, sizeof(buf), outfp) != NULL) {
1234
                strretchomp(buf);
1235
                if (find_func(buf, str)) {
1236
                        fclose(outfp);
1237
                        return TRUE;
1238
                }
1239
        }
1240
1241
        fclose(outfp);
1242
1243
        return FALSE;
1244
}
1245
1246
gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str,
1247
                              StrFindFunc find_func)
1248
{
1249
        MimeInfo *mimeinfo;
1250
        MimeInfo *partinfo;
1251
        gchar *filename;
1252
        gboolean found = FALSE;
1253
1254
        g_return_val_if_fail(msginfo != NULL, FALSE);
1255
        g_return_val_if_fail(str != NULL, FALSE);
1256
        g_return_val_if_fail(find_func != NULL, FALSE);
1257
1258
        filename = procmsg_get_message_file(msginfo);
1259
        if (!filename) return FALSE;
1260
        mimeinfo = procmime_scan_message(msginfo);
1261
1262
        for (partinfo = mimeinfo; partinfo != NULL;
1263
             partinfo = procmime_mimeinfo_next(partinfo)) {
1264
                if (partinfo->mime_type == MIME_TEXT ||
1265
                    partinfo->mime_type == MIME_TEXT_HTML) {
1266
                        if (procmime_find_string_part
1267
                                (partinfo, filename, str, find_func) == TRUE) {
1268
                                found = TRUE;
1269
                                break;
1270
                        }
1271
                }
1272
        }
1273
1274
        procmime_mimeinfo_free_all(mimeinfo);
1275
        g_free(filename);
1276
1277
        return found;
1278
}
1279
1280
gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
1281
{
1282
        gchar *base;
1283
        const gchar *base_;
1284
1285
        base_ = mimeinfo->filename ? mimeinfo->filename
1286
                : mimeinfo->name ? mimeinfo->name : "mimetmp";
1287
        base_ = g_basename(base_);
1288
        if (*base_ == '\0') base_ = "mimetmp";
1289
        base = conv_filename_from_utf8(base_);
1290
        subst_for_filename(base);
1291
1292
        return base;
1293
}
1294
1295
gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
1296
{
1297
        static guint32 id = 0;
1298
        gchar *base;
1299
        gchar *filename;
1300
        gchar f_prefix[10];
1301
1302
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1303
1304
        g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
1305
1306
        if (MIME_TEXT_HTML == mimeinfo->mime_type)
1307
                base = g_strdup("mimetmp.html");
1308
        else
1309
                base = procmime_get_part_file_name(mimeinfo);
1310
1311
        filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1312
                               f_prefix, base, NULL);
1313
1314
        g_free(base);
1315
1316
        return filename;
1317
}
1318
1319
ContentType procmime_scan_mime_type(const gchar *mime_type)
1320
{
1321
        ContentType type;
1322
1323
        if (!g_ascii_strncasecmp(mime_type, "text/html", 9))
1324
                type = MIME_TEXT_HTML;
1325
        else if (!g_ascii_strncasecmp(mime_type, "text/", 5))
1326
                type = MIME_TEXT;
1327
        else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14))
1328
                type = MIME_MESSAGE_RFC822;
1329
        else if (!g_ascii_strncasecmp(mime_type, "message/", 8))
1330
                type = MIME_TEXT;
1331
        else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream",
1332
                                      24))
1333
                type = MIME_APPLICATION_OCTET_STREAM;
1334
        else if (!g_ascii_strncasecmp(mime_type, "application/", 12))
1335
                type = MIME_APPLICATION;
1336
        else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10))
1337
                type = MIME_MULTIPART;
1338
        else if (!g_ascii_strncasecmp(mime_type, "image/", 6))
1339
                type = MIME_IMAGE;
1340
        else if (!g_ascii_strncasecmp(mime_type, "audio/", 6))
1341
                type = MIME_AUDIO;
1342
        else if (!g_ascii_strncasecmp(mime_type, "video/", 6))
1343
                type = MIME_VIDEO;
1344
        else if (!g_ascii_strcasecmp(mime_type, "text"))
1345
                type = MIME_TEXT;
1346
        else
1347
                type = MIME_UNKNOWN;
1348
1349
        return type;
1350
}
1351
1352
static GList *mime_type_list = NULL;
1353
1354
gchar *procmime_get_mime_type(const gchar *filename)
1355
{
1356
        static GHashTable *mime_type_table = NULL;
1357
        MimeType *mime_type;
1358
        const gchar *p;
1359
        gchar *ext;
1360
        static gboolean no_mime_type_table = FALSE;
1361
1362
        if (no_mime_type_table)
1363
                return NULL;
1364
1365
        if (!mime_type_table) {
1366
                mime_type_table = procmime_get_mime_type_table();
1367
                if (!mime_type_table) {
1368
                        no_mime_type_table = TRUE;
1369
                        return NULL;
1370
                }
1371
        }
1372
1373
        filename = g_basename(filename);
1374
        p = strrchr(filename, '.');
1375
        if (!p) return NULL;
1376
1377
        Xstrdup_a(ext, p + 1, return NULL);
1378
        g_strdown(ext);
1379
        mime_type = g_hash_table_lookup(mime_type_table, ext);
1380
        if (mime_type) {
1381
                gchar *str;
1382
1383
                str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
1384
                                  NULL);
1385
                return str;
1386
        }
1387
1388
        return NULL;
1389
}
1390
1391
static GHashTable *procmime_get_mime_type_table(void)
1392
{
1393
        GHashTable *table = NULL;
1394
        GList *cur;
1395
        MimeType *mime_type;
1396
        gchar **exts;
1397
1398
        if (!mime_type_list) {
1399
                GList *list;
1400
                gchar *dir;
1401
1402
#ifdef G_OS_WIN32
1403
                dir = g_strconcat(get_startup_dir(),
1404
                                  G_DIR_SEPARATOR_S "etc" G_DIR_SEPARATOR_S
1405
                                  "mime.types", NULL);
1406
                mime_type_list = procmime_get_mime_type_list(dir);
1407
                g_free(dir);
1408
#else
1409
                mime_type_list =
1410
                        procmime_get_mime_type_list(SYSCONFDIR "/mime.types");
1411
                if (!mime_type_list)
1412
                        mime_type_list =
1413
                                procmime_get_mime_type_list("/etc/mime.types");
1414
#endif
1415
                dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1416
                                  "mime.types", NULL);
1417
                list = procmime_get_mime_type_list(dir);
1418
                g_free(dir);
1419
                mime_type_list = g_list_concat(mime_type_list, list);
1420
1421
                if (!mime_type_list) {
1422
                        debug_print("mime.types not found\n");
1423
                        return NULL;
1424
                }
1425
        }
1426
1427
        table = g_hash_table_new(g_str_hash, g_str_equal);
1428
1429
        for (cur = mime_type_list; cur != NULL; cur = cur->next) {
1430
                gint i;
1431
                gchar *key;
1432
1433
                mime_type = (MimeType *)cur->data;
1434
1435
                if (!mime_type->extension) continue;
1436
1437
                exts = g_strsplit(mime_type->extension, " ", 16);
1438
                for (i = 0; exts[i] != NULL; i++) {
1439
                        /* make the key case insensitive */
1440
                        g_strdown(exts[i]);
1441
                        /* use previously dup'd key on overwriting */
1442
                        if (g_hash_table_lookup(table, exts[i]))
1443
                                key = exts[i];
1444
                        else
1445
                                key = g_strdup(exts[i]);
1446
                        g_hash_table_insert(table, key, mime_type);
1447
                }
1448
                g_strfreev(exts);
1449
        }
1450
1451
        return table;
1452
}
1453
1454
static GList *procmime_get_mime_type_list(const gchar *file)
1455
{
1456
        GList *list = NULL;
1457
        FILE *fp;
1458
        gchar buf[BUFFSIZE];
1459
        gchar *p;
1460
        gchar *delim;
1461
        MimeType *mime_type;
1462
1463
        if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1464
1465
        debug_print("Reading %s ...\n", file);
1466
1467
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1468
                p = strchr(buf, '#');
1469
                if (p) *p = '\0';
1470
                g_strstrip(buf);
1471
1472
                p = buf;
1473
                while (*p && !g_ascii_isspace(*p)) p++;
1474
                if (*p) {
1475
                        *p = '\0';
1476
                        p++;
1477
                }
1478
                delim = strchr(buf, '/');
1479
                if (delim == NULL) continue;
1480
                *delim = '\0';
1481
1482
                mime_type = g_new(MimeType, 1);
1483
                mime_type->type = g_strdup(buf);
1484
                mime_type->sub_type = g_strdup(delim + 1);
1485
1486
                while (*p && g_ascii_isspace(*p)) p++;
1487
                if (*p)
1488
                        mime_type->extension = g_strdup(p);
1489
                else
1490
                        mime_type->extension = NULL;
1491
1492
                list = g_list_append(list, mime_type);
1493
        }
1494
1495
        fclose(fp);
1496
1497
        if (!list)
1498
                g_warning("Can't read mime.types\n");
1499
1500
        return list;
1501
}
1502
1503
static GList *mailcap_list = NULL;
1504
1505
static GList *procmime_parse_mailcap(const gchar *file)
1506
{
1507
        GList *list = NULL;
1508
        FILE *fp;
1509
        gchar buf[BUFFSIZE];
1510
        MailCap *mailcap;
1511
1512
        if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1513
1514
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1515
                gint i;
1516
                gchar *p;
1517
                gchar **strv;
1518
1519
                p = strchr(buf, '#');
1520
                if (p) *p = '\0';
1521
                g_strstrip(buf);
1522
1523
                strv = strsplit_with_quote(buf, ";", 0);
1524
                if (!strv)
1525
                        continue;
1526
1527
                for (i = 0; strv[i] != NULL; ++i)
1528
                        g_strstrip(strv[i]);
1529
1530
                if (!strv[0] || *strv[0] == '\0' ||
1531
                    !strv[1] || *strv[1] == '\0') {
1532
                        g_strfreev(strv);
1533
                        continue;
1534
                }
1535
1536
                mailcap = g_new(MailCap, 1);
1537
                mailcap->mime_type = g_strdup(strv[0]);
1538
                mailcap->cmdline_fmt = g_strdup(strv[1]);
1539
                mailcap->needs_terminal = FALSE;
1540
1541
                for (i = 0; strv[i] != NULL; ++i) {
1542
                        if (strcmp(strv[i], "needsterminal") == 0)
1543
                                mailcap->needs_terminal = TRUE;
1544
                }
1545
1546
                g_strfreev(strv);
1547
1548
                list = g_list_append(list, mailcap);
1549
        }
1550
1551
        return list;
1552
}
1553
1554
gint procmime_execute_open_file(const gchar *file, const gchar *mime_type)
1555
{
1556
        gchar *mime_type_ = NULL;
1557
        GList *cur;
1558
        MailCap *mailcap;
1559
        gchar *cmdline;
1560
        gint ret = -1;
1561
        static gboolean mailcap_list_init = FALSE;
1562
1563
        g_return_val_if_fail(file != NULL, -1);
1564
1565
        if (!mime_type ||
1566
            g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
1567
                gchar *tmp;
1568
                tmp = procmime_get_mime_type(file);
1569
                if (!tmp)
1570
                        return -1;
1571
                mime_type_ = g_ascii_strdown(tmp, -1);
1572
                g_free(tmp);
1573
        } else
1574
                mime_type_ = g_ascii_strdown(mime_type, -1);
1575
1576
        if (!mailcap_list_init && !mailcap_list) {
1577
                GList *list;
1578
                gchar *path;
1579
1580
                path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "mailcap",
1581
                                  NULL);
1582
                mailcap_list = procmime_parse_mailcap(path);
1583
                g_free(path);
1584
#ifdef G_OS_WIN32
1585
                path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S "etc"
1586
                                   G_DIR_SEPARATOR_S "mailcap", NULL);
1587
                list = procmime_parse_mailcap(path);
1588
                g_free(path);
1589
#else
1590
                if (!mailcap_list) {
1591
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1592
                                           ".mailcap", NULL);
1593
                        mailcap_list = procmime_parse_mailcap(path);
1594
                        g_free(path);
1595
                }
1596
                list = procmime_parse_mailcap(SYSCONFDIR "/mailcap");
1597
                if (!list)
1598
                        list = procmime_parse_mailcap("/etc/mailcap");
1599
#endif
1600
                mailcap_list = g_list_concat(mailcap_list, list);
1601
1602
                mailcap_list_init = TRUE;
1603
        }
1604
1605
        for (cur = mailcap_list; cur != NULL; cur = cur->next) {
1606
                mailcap = (MailCap *)cur->data;
1607
1608
                if (!g_pattern_match_simple(mailcap->mime_type, mime_type_))
1609
                        continue;
1610
                if (mailcap->needs_terminal)
1611
                        continue;
1612
1613
                if (str_find_format_times(mailcap->cmdline_fmt, 's') == 1)
1614
                        cmdline = g_strdup_printf(mailcap->cmdline_fmt, file);
1615
                else
1616
                        cmdline = g_strconcat(mailcap->cmdline_fmt, " \"", file,
1617
                                              "\"", NULL);
1618
                ret = execute_command_line(cmdline, TRUE);
1619
                g_free(cmdline);
1620
                break;
1621
        }
1622
1623
        g_free(mime_type_);
1624
1625
        return ret;
1626
}
1627
1628
EncodingType procmime_get_encoding_for_charset(const gchar *charset)
1629
{
1630
        if (!charset)
1631
                return ENC_8BIT;
1632
        else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
1633
                 !g_ascii_strcasecmp(charset, "US-ASCII"))
1634
                return ENC_7BIT;
1635
        else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
1636
                 !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
1637
                 !g_ascii_strcasecmp(charset, "Windows-1251"))
1638
                return ENC_8BIT;
1639
        else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
1640
                return ENC_QUOTED_PRINTABLE;
1641
        else
1642
                return ENC_8BIT;
1643
}
1644
1645
EncodingType procmime_get_encoding_for_text_file(const gchar *file)
1646
{
1647
        FILE *fp;
1648
        guchar buf[BUFFSIZE];
1649
        size_t len;
1650
        size_t octet_chars = 0;
1651
        size_t total_len = 0;
1652
        gfloat octet_percentage;
1653
1654
        if ((fp = g_fopen(file, "rb")) == NULL) {
1655
                FILE_OP_ERROR(file, "fopen");
1656
                return ENC_UNKNOWN;
1657
        }
1658
1659
        while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1660
                guchar *p;
1661
                gint i;
1662
1663
                for (p = buf, i = 0; i < len; ++p, ++i) {
1664
                        if (*p & 0x80)
1665
                                ++octet_chars;
1666
                }
1667
                total_len += len;
1668
        }
1669
1670
        fclose(fp);
1671
1672
        if (total_len > 0)
1673
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1674
        else
1675
                octet_percentage = 0.0;
1676
1677
        debug_print("procmime_get_encoding_for_text_file(): "
1678
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1679
                    100.0 * octet_percentage);
1680
1681
        if (octet_percentage > 0.20) {
1682
                debug_print("using BASE64\n");
1683
                return ENC_BASE64;
1684
        } else if (octet_chars > 0) {
1685
                debug_print("using quoted-printable\n");
1686
                return ENC_QUOTED_PRINTABLE;
1687
        } else {
1688
                debug_print("using 7bit\n");
1689
                return ENC_7BIT;
1690
        }
1691
}
1692
1693
EncodingType procmime_get_encoding_for_str(const gchar *str)
1694
{
1695
        const guchar *p;
1696
        size_t octet_chars = 0;
1697
        size_t total_len = 0;
1698
        gfloat octet_percentage;
1699
1700
        total_len = strlen(str);
1701
1702
        for (p = (const guchar *)str; *p != '\0'; ++p) {
1703
                if (*p & 0x80)
1704
                        ++octet_chars;
1705
        }
1706
1707
        if (total_len > 0)
1708
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1709
        else
1710
                octet_percentage = 0.0;
1711
1712
        debug_print("procmime_get_encoding_for_str(): "
1713
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1714
                    100.0 * octet_percentage);
1715
1716
        if (octet_percentage > 0.20) {
1717
                debug_print("using BASE64\n");
1718
                return ENC_BASE64;
1719
        } else if (octet_chars > 0) {
1720
                debug_print("using quoted-printable\n");
1721
                return ENC_QUOTED_PRINTABLE;
1722
        } else {
1723
                debug_print("using 7bit\n");
1724
                return ENC_7BIT;
1725
        }
1726
}
1727
1728
const gchar *procmime_get_encoding_str(EncodingType encoding)
1729
{
1730
        static const gchar *encoding_str[] = {
1731
                "7bit", "8bit", "quoted-printable", "base64", "x-uuencode",
1732
                NULL
1733
        };
1734
1735
        if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
1736
                return encoding_str[encoding];
1737
        else
1738
                return NULL;
1739
}