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
}