Statistics
| Branch: | Tag: | Revision:

root / libsylph / procmime.c @ 8d7dcace

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