Statistics
| Revision:

root / libsylph / procmime.c @ 3281

History | View | Annotate | Download (42.9 KB)

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

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <locale.h>
31
#include <ctype.h>
32

    
33
#include "procmime.h"
34
#include "procheader.h"
35
#include "base64.h"
36
#include "quoted-printable.h"
37
#include "uuencode.h"
38
#include "html.h"
39
#include "codeconv.h"
40
#include "utils.h"
41
#include "prefs_common.h"
42

    
43
//#undef MIME_DEBUG
44
#define MIME_DEBUG
45
#ifdef MIME_DEBUG
46
#  define mime_debug_print        debug_print
47
#else
48
#  define mime_debug_print        1 ? (void)0 : debug_print
49
#endif
50

    
51
#define MAX_MIME_LEVEL        64
52

    
53
static GHashTable *procmime_get_mime_type_table        (void);
54
static GList *procmime_get_mime_type_list        (const gchar *file);
55

    
56

    
57
MimeInfo *procmime_mimeinfo_new(void)
58
{
59
        MimeInfo *mimeinfo;
60

    
61
        mimeinfo = g_new0(MimeInfo, 1);
62
        mimeinfo->mime_type     = MIME_UNKNOWN;
63
        mimeinfo->encoding_type = ENC_UNKNOWN;
64

    
65
        return mimeinfo;
66
}
67

    
68
void procmime_mimeinfo_free_all(MimeInfo *mimeinfo)
69
{
70
        while (mimeinfo != NULL) {
71
                MimeInfo *next;
72

    
73
                g_free(mimeinfo->encoding);
74
                g_free(mimeinfo->content_type);
75
                g_free(mimeinfo->charset);
76
                g_free(mimeinfo->name);
77
                g_free(mimeinfo->boundary);
78
                g_free(mimeinfo->content_disposition);
79
                g_free(mimeinfo->filename);
80

    
81
                g_free(mimeinfo->sigstatus);
82
                g_free(mimeinfo->sigstatus_full);
83

    
84
                procmime_mimeinfo_free_all(mimeinfo->sub);
85
                procmime_mimeinfo_free_all(mimeinfo->children);
86
                procmime_mimeinfo_free_all(mimeinfo->plaintext);
87

    
88
                next = mimeinfo->next;
89
                g_free(mimeinfo);
90
                mimeinfo = next;
91
        }
92
}
93

    
94
MimeInfo *procmime_mimeinfo_insert(MimeInfo *parent, MimeInfo *mimeinfo)
95
{
96
        MimeInfo *child = parent->children;
97

    
98
        if (!child)
99
                parent->children = mimeinfo;
100
        else {
101
                while (child->next != NULL)
102
                        child = child->next;
103

    
104
                child->next = mimeinfo;
105
        }
106

    
107
        mimeinfo->parent = parent;
108
        mimeinfo->level = parent->level + 1;
109

    
110
        return mimeinfo;
111
}
112

    
113
#if 0
114
void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new)
115
{
116
        MimeInfo *parent = old->parent;
117
        MimeInfo *child;
118

119
        g_return_if_fail(parent != NULL);
120
        g_return_if_fail(new->next == NULL);
121

122
        for (child = parent->children; child && child != old;
123
             child = child->next)
124
                ;
125
        if (!child) {
126
                g_warning("oops: parent can't find it's own child");
127
                return;
128
        }
129
        procmime_mimeinfo_free_all(old);
130

131
        if (child == parent->children) {
132
                new->next = parent->children->next;
133
                parent->children = new;
134
        } else {
135
                new->next = child->next;
136
                child = new;
137
        }
138
}
139
#endif
140

    
141
MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo)
142
{
143
        if (!mimeinfo) return NULL;
144

    
145
        if (mimeinfo->children)
146
                return mimeinfo->children;
147
        if (mimeinfo->sub)
148
                return mimeinfo->sub;
149
        if (mimeinfo->next)
150
                return mimeinfo->next;
151

    
152
        if (mimeinfo->main) {
153
                mimeinfo = mimeinfo->main;
154
                if (mimeinfo->next)
155
                        return mimeinfo->next;
156
        }
157

    
158
        for (mimeinfo = mimeinfo->parent; mimeinfo != NULL;
159
             mimeinfo = mimeinfo->parent) {
160
                if (mimeinfo->next)
161
                        return mimeinfo->next;
162
                if (mimeinfo->main) {
163
                        mimeinfo = mimeinfo->main;
164
                        if (mimeinfo->next)
165
                                return mimeinfo->next;
166
                }
167
        }
168

    
169
        return NULL;
170
}
171

    
172
#if 0
173
void procmime_dump_mimeinfo(MimeInfo *mimeinfo)
174
{
175
        gint i;
176

177
        g_print("\n");
178

179
        for (; mimeinfo != NULL; mimeinfo = procmime_mimeinfo_next(mimeinfo)) {
180
                for (i = 0; i < mimeinfo->level; i++)
181
                        g_print("  ");
182
                g_print("%s%s\n", mimeinfo->main ? "sub: " : "",
183
                        mimeinfo->content_type);
184
        }
185
}
186
#endif
187

    
188
MimeInfo *procmime_scan_message(MsgInfo *msginfo)
189
{
190
        FILE *fp;
191
        MimeInfo *mimeinfo;
192

    
193
        g_return_val_if_fail(msginfo != NULL, NULL);
194

    
195
        if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
196
                return NULL;
197

    
198
        if (mimeinfo) {
199
                mimeinfo->size = msginfo->size;
200
                mimeinfo->content_size = get_left_file_size(fp);
201
                if (mimeinfo->encoding_type == ENC_BASE64)
202
                        mimeinfo->content_size = mimeinfo->content_size / 4 * 3;
203
                if (mimeinfo->mime_type == MIME_MULTIPART ||
204
                    mimeinfo->mime_type == MIME_MESSAGE_RFC822)
205
                        procmime_scan_multipart_message(mimeinfo, fp);
206
        }
207

    
208
        fclose(fp);
209

    
210
        return mimeinfo;
211
}
212

    
213
MimeInfo *procmime_scan_message_stream(FILE *fp)
214
{
215
        MimeInfo *mimeinfo;
216
        glong fpos;
217

    
218
        g_return_val_if_fail(fp != NULL, NULL);
219

    
220
        if (fseek(fp, 0L, SEEK_SET) < 0) {
221
                FILE_OP_ERROR("procmime_scan_message_stream()", "fseek");
222
                return NULL;
223
        }
224

    
225
        mimeinfo = procmime_scan_mime_header(fp);
226

    
227
        if (mimeinfo) {
228
                fpos = ftell(fp);
229
                mimeinfo->content_size = get_left_file_size(fp);
230
                mimeinfo->size = fpos + mimeinfo->content_size;
231
                if (mimeinfo->encoding_type == ENC_BASE64)
232
                        mimeinfo->content_size = mimeinfo->content_size / 4 * 3;
233
                if (mimeinfo->mime_type == MIME_MULTIPART ||
234
                    mimeinfo->mime_type == MIME_MESSAGE_RFC822)
235
                        procmime_scan_multipart_message(mimeinfo, fp);
236
        }
237

    
238
        return mimeinfo;
239
}
240

    
241
void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp)
242
{
243
        gchar *p;
244
        gchar *boundary;
245
        gint boundary_len = 0;
246
        gchar *buf;
247
        glong fpos, prev_fpos;
248

    
249
        g_return_if_fail(mimeinfo != NULL);
250
        g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART ||
251
                         mimeinfo->mime_type == MIME_MESSAGE_RFC822);
252

    
253
        if (mimeinfo->mime_type == MIME_MULTIPART) {
254
                g_return_if_fail(mimeinfo->boundary != NULL);
255
                g_return_if_fail(mimeinfo->sub == NULL);
256
        }
257
        g_return_if_fail(fp != NULL);
258

    
259
        buf = g_malloc(BUFFSIZE);
260

    
261
        boundary = mimeinfo->boundary;
262

    
263
        if (boundary) {
264
                boundary_len = strlen(boundary);
265

    
266
                /* look for first boundary */
267
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL)
268
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
269
                if (!p) {
270
                        g_free(buf);
271
                        return;
272
                }
273
        } else if (mimeinfo->parent && mimeinfo->parent->boundary) {
274
                boundary = mimeinfo->parent->boundary;
275
                boundary_len = strlen(boundary);
276
        }
277

    
278
        if ((fpos = ftell(fp)) < 0) {
279
                perror("ftell");
280
                g_free(buf);
281
                return;
282
        }
283

    
284
        mime_debug_print("==== enter part\n");
285
        mime_debug_print("level = %d\n", mimeinfo->level);
286

    
287
        for (;;) {
288
                MimeInfo *partinfo;
289
                gboolean eom = FALSE;
290
                glong content_pos;
291
                gboolean is_base64;
292
                gint len;
293
                guint b64_content_len = 0;
294
                gint b64_pad_len = 0;
295

    
296
                prev_fpos = fpos;
297
                mime_debug_print("prev_fpos: %ld\n", fpos);
298

    
299
                /* scan part header */
300
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
301
                        MimeInfo *sub;
302

    
303
                        mimeinfo->sub = sub = procmime_scan_mime_header(fp);
304
                        if (!sub) break;
305

    
306
                        mime_debug_print("message/rfc822 part (content-type: %s)\n",
307
                                         sub->content_type);
308
                        sub->level = mimeinfo->level + 1;
309
                        sub->parent = mimeinfo->parent;
310
                        sub->main = mimeinfo;
311

    
312
                        partinfo = sub;
313
                } else {
314
                        partinfo = procmime_scan_mime_header(fp);
315
                        if (!partinfo) break;
316
                        procmime_mimeinfo_insert(mimeinfo, partinfo);
317
                        mime_debug_print("content-type: %s\n",
318
                                         partinfo->content_type);
319
                        if (partinfo->filename)
320
                                mime_debug_print("filename: %s\n", partinfo->filename);
321
                        else if (partinfo->name)
322
                                mime_debug_print("name: %s\n", partinfo->name);
323
                }
324

    
325
                /* begin content */
326
                content_pos = ftell(fp);
327
                mime_debug_print("content_pos: %ld\n", content_pos);
328

    
329
                if (partinfo->mime_type == MIME_MULTIPART ||
330
                    partinfo->mime_type == MIME_MESSAGE_RFC822) {
331
                        if (partinfo->level < MAX_MIME_LEVEL)
332
                                mime_debug_print("\n");
333
                                mime_debug_print("enter to child part:\n");
334
                                procmime_scan_multipart_message(partinfo, fp);
335
                }
336

    
337
                /* look for next boundary */
338
                buf[0] = '\0';
339
                is_base64 = partinfo->encoding_type == ENC_BASE64;
340
                while ((p = fgets(buf, BUFFSIZE, fp)) != NULL) {
341
                        if (IS_BOUNDARY(buf, boundary, boundary_len)) {
342
                                if (buf[2 + boundary_len]     == '-' &&
343
                                    buf[2 + boundary_len + 1] == '-')
344
                                        eom = TRUE;
345
                                break;
346
                        } else if (is_base64) {
347
                                const gchar *s;
348
                                for (s = buf; *s && *s != '\r' && *s != '\n';
349
                                     ++s)
350
                                        if (*s == '=')
351
                                                ++b64_pad_len;
352
                                b64_content_len += s - buf;
353
                        }
354
                }
355
                if (p == NULL) {
356
                        /* broken MIME, or single part MIME message */
357
                        buf[0] = '\0';
358
                        eom = TRUE;
359
                }
360
                mime_debug_print("boundary: %s\n", buf);
361

    
362
                fpos = ftell(fp);
363
                mime_debug_print("fpos: %ld\n", fpos);
364

    
365
                len = strlen(buf);
366
                partinfo->size = fpos - prev_fpos - len;
367
                if (is_base64)
368
                        partinfo->content_size =
369
                                b64_content_len / 4 * 3 - b64_pad_len;
370
                else
371
                        partinfo->content_size = fpos - content_pos - len;
372
                mime_debug_print("partinfo->size: %d\n", partinfo->size);
373
                mime_debug_print("partinfo->content_size: %d\n",
374
                                 partinfo->content_size);
375
                if (partinfo->sub && !partinfo->sub->sub &&
376
                    !partinfo->sub->children) {
377
                        partinfo->sub->size =
378
                                fpos - partinfo->sub->fpos - strlen(buf);
379
                        mime_debug_print("partinfo->sub->size: %d\n",
380
                                         partinfo->sub->size);
381
                }
382

    
383
                if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
384
                        if (len > 0 && fseek(fp, fpos - len, SEEK_SET) < 0)
385
                                perror("fseek");
386
                        break;
387
                }
388

    
389
                if (eom) break;
390
        }
391

    
392
        g_free(buf);
393
        mime_debug_print("==== leave part\n");
394
}
395

    
396
void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding)
397
{
398
        gchar *enc;
399

    
400
        g_free(mimeinfo->encoding);
401
        enc = mimeinfo->encoding = g_strstrip(g_strdup(encoding));
402

    
403
        if (!g_ascii_strncasecmp(enc, "7bit", 4))
404
                mimeinfo->encoding_type = ENC_7BIT;
405
        else if (!g_ascii_strncasecmp(enc, "8bit", 4))
406
                mimeinfo->encoding_type = ENC_8BIT;
407
        else if (!g_ascii_strncasecmp(enc, "quoted-printable", 16))
408
                mimeinfo->encoding_type = ENC_QUOTED_PRINTABLE;
409
        else if (!g_ascii_strncasecmp(enc, "base64", 6))
410
                mimeinfo->encoding_type = ENC_BASE64;
411
        else if (!g_ascii_strncasecmp(enc, "x-uuencode", 10))
412
                mimeinfo->encoding_type = ENC_X_UUENCODE;
413
        else
414
                mimeinfo->encoding_type = ENC_UNKNOWN;
415

    
416
}
417

    
418
void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type)
419
{
420
        g_free(mimeinfo->content_type);
421
        g_free(mimeinfo->charset);
422
        g_free(mimeinfo->name);
423
        g_free(mimeinfo->boundary);
424
        mimeinfo->content_type = NULL;
425
        mimeinfo->charset      = NULL;
426
        mimeinfo->name         = NULL;
427
        mimeinfo->boundary     = NULL;
428

    
429
        procmime_scan_content_type_str(content_type, &mimeinfo->content_type,
430
                                       &mimeinfo->charset, &mimeinfo->name,
431
                                       &mimeinfo->boundary);
432

    
433
        mimeinfo->mime_type = procmime_scan_mime_type(mimeinfo->content_type);
434
        if (mimeinfo->mime_type == MIME_MULTIPART && !mimeinfo->boundary)
435
                mimeinfo->mime_type = MIME_TEXT;
436
}
437

    
438
typedef struct
439
{
440
        gchar *name;
441
        gchar *value;
442
} MimeParam;
443

    
444
typedef struct
445
{
446
        gchar *hvalue;
447
        GSList *plist;
448
} MimeParams;
449

    
450
static gchar *procmime_find_parameter_delimiter(const gchar *param,
451
                                                const gchar **eq)
452
{
453
        register const gchar *p = param;
454
        gboolean quoted = FALSE;
455
        const gchar *delim = NULL;
456

    
457
        while (*p) {
458
                if (*p == '=')
459
                        break;
460
                else if (*p == ';' || *p == '\r' || *p == '\n') {
461
                        delim = p;
462
                        break;
463
                }
464
                ++p;
465
        }
466
        if (*p != '=') {
467
                *eq = NULL;
468
                return (gchar *)delim;
469
        }
470
        *eq = p;
471

    
472
        ++p;
473
        while (g_ascii_isspace(*p))
474
                ++p;
475
        if (*p == '"') {
476
                quoted = TRUE;
477
                ++p;
478
        }
479

    
480
        while (*p) {
481
                if (quoted == TRUE) {
482
                        if (*p == '"')
483
                                quoted = FALSE;
484
                } else if (*p == ';' || *p == '\r' || *p == '\n') {
485
                        delim = p;
486
                        break;
487
                }
488
                ++p;
489
        }
490

    
491
        return (gchar *)delim;
492
}
493

    
494
static gchar *procmime_convert_value(const gchar *value, const gchar *charset)
495
{
496
        if (charset) {
497
                gchar *utf8_value;
498

    
499
                utf8_value = conv_codeset_strdup(value, charset, CS_INTERNAL);
500
                if (utf8_value)
501
                        return utf8_value;
502
        }
503

    
504
        return g_strdup(value);
505
}
506

    
507
static MimeParams *procmime_parse_mime_parameter(const gchar *str)
508
{
509
        ConvADType ad_type;
510
        gchar *tmp_param = NULL;
511
        gchar *hvalue;
512
        gchar *param, *name, *value;
513
        gchar *charset = NULL, *lang = NULL;
514
        const gchar *p, *delim;
515
        gint count, prev_count;
516
        gchar *cont_name;
517
        gchar *cont_value;
518
        MimeParam *mparam;
519
        MimeParams *mparams;
520
        GSList *plist = NULL;
521

    
522
        if ((p = strchr(str, ';')))
523
                hvalue = g_strndup(str, p - str);
524
        else
525
                hvalue = g_strdup(str);
526

    
527
        g_strstrip(hvalue);
528

    
529
        mparams = g_new(MimeParams, 1);
530
        mparams->hvalue = hvalue;
531
        mparams->plist = NULL;
532

    
533
        if (!p)
534
                return mparams;
535
        ++p;
536

    
537
        /* workaround for raw-JIS filename (Eudora etc.) */
538
        ad_type = conv_get_autodetect_type();
539
        if ((ad_type == C_AD_JAPANESE ||
540
             (ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) &&
541
            strstr(p, "\033$") != NULL) {
542
                CodeConvFunc conv_func;
543
                conv_func = conv_get_code_conv_func(NULL, NULL);
544
                tmp_param = conv_func(p, NULL);
545
                p = tmp_param;
546
                debug_print("procmime_parse_mime_parameter(): raw-JIS header body detected: %s\n", str);
547
        }
548

    
549
        count = prev_count = -1;
550
        cont_name = cont_value = NULL;
551

    
552
        for (;;) {
553
                gboolean encoded = FALSE;
554
                gchar *begin;
555
                gchar *dec_value;
556
                const gchar *eq;
557
                gchar *ast = NULL;
558

    
559
                while (*p == ';' || g_ascii_isspace(*p))
560
                        ++p;
561
                if (*p == '\0')
562
                        break;
563

    
564
                delim = procmime_find_parameter_delimiter(p, &eq);
565
                if (!eq)
566
                        break;
567
                if (delim)
568
                        param = g_strndup(p, delim - p);
569
                else
570
                        param = g_strdup(p);
571

    
572
                name = g_strndup(p, eq - p);
573
                g_strchomp(name);
574
                if (*name != '*' && (ast = strchr(name, '*'))) {
575
                        const gchar *next = ast + 1;
576

    
577
                        if (*next == '\0') {
578
                                encoded = TRUE;
579
                        } else if (g_ascii_isdigit(*next)) {
580
                                count = atoi(next);
581
                                while (g_ascii_isdigit(*next))
582
                                        ++next;
583
                                if (*next == '*')
584
                                        encoded = TRUE;
585
                                if (prev_count + 1 != count) {
586
                                        g_warning("procmime_parse_mime_parameter(): invalid count: %s\n", str);
587
                                        g_free(name);
588
                                        g_free(param);
589
                                        break;
590
                                }
591
                        } else {
592
                                g_warning("procmime_parse_mime_parameter(): invalid name: %s\n", str);
593
                                g_free(name);
594
                                g_free(param);
595
                                break;
596
                        }
597

    
598
                        *ast = '\0';
599
                }
600

    
601
                value = g_strdup(param + (eq - p) + 1);
602
                g_strstrip(value);
603
                if (*value == '"')
604
                        extract_quote(value, '"');
605

    
606
                begin = value;
607

    
608
                if (encoded) {
609
                        gchar *sq1, *sq2;
610

    
611
                        if ((sq1 = strchr(value, '\''))) {
612
                                if (sq1 > value) {
613
                                        if (charset)
614
                                                g_free(charset);
615
                                        charset = g_strndup(value, sq1 - value);
616
                                }
617
                                if ((sq2 = strchr(sq1 + 1, '\''))) {
618
                                        if (sq2 > sq1 + 1) {
619
                                                if (lang)
620
                                                        g_free(lang);
621
                                                lang = g_strndup(sq1 + 1,
622
                                                                 sq2 - sq1 - 1);
623
                                        }
624
                                        begin = sq2 + 1;
625
                                }
626
                        }
627
                }
628

    
629
#define CONCAT_CONT_VALUE(s)                                \
630
{                                                        \
631
        if (cont_value) {                                \
632
                gchar *tmp;                                \
633
                tmp = g_strconcat(cont_value, s, NULL);        \
634
                g_free(cont_value);                        \
635
                cont_value = tmp;                        \
636
        } else                                                \
637
                cont_value = g_strdup(s);                \
638
}
639

    
640
                if (count >= 0) {
641
                        if (count > 0 && cont_name) {
642
                                if (strcmp(cont_name, name) != 0) {
643
                                        g_warning("procmime_parse_mime_parameter(): mismatch parameter name: %s\n", str);
644
                                        g_free(name);
645
                                        g_free(value);
646
                                        g_free(param);
647
                                        break;
648
                                }
649
                        } else
650
                                cont_name = g_strdup(name);
651

    
652
                        if (encoded) {
653
                                dec_value = g_malloc(strlen(begin) + 1);
654
                                decode_xdigit_encoded_str(dec_value, begin);
655
                                CONCAT_CONT_VALUE(dec_value);
656
                                g_free(dec_value);
657
                        } else {
658
                                CONCAT_CONT_VALUE(begin);
659
                        }
660
                }
661

    
662
#undef CONCAT_CONT_VALUE
663

    
664
                if (count == -1 && cont_name && cont_value) {
665
                        mparam = g_new(MimeParam, 1);
666
                        mparam->name = cont_name;
667
                        cont_name = NULL;
668
                        mparam->value = procmime_convert_value
669
                                (cont_value, charset);
670
                        g_free(cont_value);
671
                        cont_value = NULL;
672
                        plist = g_slist_prepend(plist, mparam);
673
                }
674

    
675
                if (count == -1) {
676
                        mparam = g_new(MimeParam, 1);
677
                        mparam->name = name;
678
                        if (encoded) {
679
                                dec_value = g_malloc(strlen(begin) + 1);
680
                                decode_xdigit_encoded_str(dec_value, begin);
681
                                mparam->value = procmime_convert_value
682
                                        (dec_value, charset);
683
                                g_free(dec_value);
684
                        } else {
685
                                if (!ast &&
686
                                    (!g_ascii_strcasecmp(name, "name") ||
687
                                     !g_ascii_strcasecmp(name, "filename")))
688
                                        mparam->value =
689
                                                conv_unmime_header(begin, NULL);
690
                                else
691
                                        mparam->value = g_strdup(begin);
692
                        }
693
                        name = NULL;
694
                        plist = g_slist_prepend(plist, mparam);
695
                }
696

    
697
                g_free(name);
698
                g_free(value);
699
                g_free(param);
700

    
701
                prev_count = count;
702
                count = -1;
703

    
704
                if (delim)
705
                        p = delim + 1;
706
                else
707
                        break;
708
        }
709

    
710
        if (cont_name && cont_value) {
711
                mparam = g_new(MimeParam, 1);
712
                mparam->name = cont_name;
713
                cont_name = NULL;
714
                mparam->value = procmime_convert_value(cont_value, charset);
715
                plist = g_slist_prepend(plist, mparam);
716
        }
717

    
718
        g_free(cont_name);
719
        g_free(cont_value);
720
        g_free(lang);
721
        g_free(charset);
722
        if (tmp_param)
723
                g_free(tmp_param);
724

    
725
        plist = g_slist_reverse(plist);
726
        mparams->plist = plist;
727

    
728
        return mparams;
729
}
730

    
731
static void procmime_mime_params_free(MimeParams *mparams)
732
{
733
        GSList *cur;
734

    
735
        if (!mparams)
736
                return;
737

    
738
        g_free(mparams->hvalue);
739
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
740
                MimeParam *mparam = (MimeParam *)cur->data;
741
                g_free(mparam->name);
742
                g_free(mparam->value);
743
                g_free(mparam);
744
        }
745
        g_slist_free(mparams->plist);
746
        g_free(mparams);
747
}
748

    
749
void procmime_scan_content_type_str(const gchar *content_type,
750
                                    gchar **mime_type, gchar **charset,
751
                                    gchar **name, gchar **boundary)
752
{
753
        MimeParams *mparams;
754
        GSList *cur;
755

    
756
        mparams = procmime_parse_mime_parameter(content_type);
757

    
758
        if (mime_type)
759
                *mime_type = g_strdup(mparams->hvalue);
760

    
761
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
762
                MimeParam *param = (MimeParam *)cur->data;
763
                if (charset && !g_ascii_strcasecmp(param->name, "charset")) {
764
                        *charset = g_strdup(param->value);
765
                        eliminate_parenthesis(*charset, '(', ')');
766
                        g_strstrip(*charset);
767
                        charset = NULL;
768
                } else if (name && !g_ascii_strcasecmp(param->name, "name")) {
769
                        *name = g_strdup(param->value);
770
                        name = NULL;
771
                } else if (boundary &&
772
                           !g_ascii_strcasecmp(param->name, "boundary")) {
773
                        *boundary = g_strdup(param->value);
774
                        boundary = NULL;
775
                }
776
        }
777

    
778
        procmime_mime_params_free(mparams);
779
}
780

    
781
void procmime_scan_content_type_partial(const gchar *content_type,
782
                                        gint *total, gchar **part_id,
783
                                        gint *number)
784
{
785
        MimeParams *mparams;
786
        GSList *cur;
787
        gchar *id_str = NULL;
788
        gint t = 0, n = 0;
789

    
790
        *total = 0;
791
        *part_id = NULL;
792
        *number = 0;
793

    
794
        mparams = procmime_parse_mime_parameter(content_type);
795

    
796
        if (!mparams->hvalue ||
797
            g_ascii_strcasecmp(mparams->hvalue, "message/partial") != 0) {
798
                procmime_mime_params_free(mparams);
799
                return;
800
        }
801

    
802
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
803
                MimeParam *param = (MimeParam *)cur->data;
804
                if (!g_ascii_strcasecmp(param->name, "total")) {
805
                        t = atoi(param->value);
806
                } else if (!id_str && !g_ascii_strcasecmp(param->name, "id")) {
807
                        id_str = g_strdup(param->value);
808
                } else if (!g_ascii_strcasecmp(param->name, "number")) {
809
                        n = atoi(param->value);
810
                }
811
        }
812

    
813
        procmime_mime_params_free(mparams);
814

    
815
        if (n > 0 && (t == 0 || t >= n) && id_str) {
816
                *total = t;
817
                *part_id = id_str;
818
                *number = n;
819
        } else {
820
                g_free(id_str);
821
        }
822
}
823

    
824
void procmime_scan_content_disposition(MimeInfo *mimeinfo,
825
                                       const gchar *content_disposition)
826
{
827
        MimeParams *mparams;
828
        GSList *cur;
829

    
830
        mparams = procmime_parse_mime_parameter(content_disposition);
831

    
832
        mimeinfo->content_disposition = g_strdup(mparams->hvalue);
833

    
834
        for (cur = mparams->plist; cur != NULL; cur = cur->next) {
835
                MimeParam *param = (MimeParam *)cur->data;
836
                if (!g_ascii_strcasecmp(param->name, "filename")) {
837
                        mimeinfo->filename = g_strdup(param->value);
838
                        break;
839
                }
840
        }
841

    
842
        procmime_mime_params_free(mparams);
843
}
844

    
845
enum
846
{
847
        H_CONTENT_TRANSFER_ENCODING = 0,
848
        H_CONTENT_TYPE                    = 1,
849
        H_CONTENT_DISPOSITION            = 2
850
};
851

    
852
MimeInfo *procmime_scan_mime_header(FILE *fp)
853
{
854
        static HeaderEntry hentry[] = {{"Content-Transfer-Encoding:",
855
                                                          NULL, FALSE},
856
                                       {"Content-Type:", NULL, TRUE},
857
                                       {"Content-Disposition:",
858
                                                          NULL, TRUE},
859
                                       {NULL,                  NULL, FALSE}};
860
        gchar buf[BUFFSIZE];
861
        gint hnum;
862
        HeaderEntry *hp;
863
        MimeInfo *mimeinfo;
864

    
865
        g_return_val_if_fail(fp != NULL, NULL);
866

    
867
        mimeinfo = procmime_mimeinfo_new();
868
        mimeinfo->mime_type = MIME_TEXT;
869
        mimeinfo->encoding_type = ENC_7BIT;
870
        mimeinfo->fpos = ftell(fp);
871

    
872
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
873
               != -1) {
874
                hp = hentry + hnum;
875

    
876
                if (H_CONTENT_TRANSFER_ENCODING == hnum) {
877
                        procmime_scan_encoding
878
                                (mimeinfo, buf + strlen(hp->name));
879
                } else if (H_CONTENT_TYPE == hnum) {
880
                        procmime_scan_content_type
881
                                (mimeinfo, buf + strlen(hp->name));
882
                } else if (H_CONTENT_DISPOSITION == hnum) {
883
                        procmime_scan_content_disposition
884
                                (mimeinfo, buf + strlen(hp->name));
885
                }
886
        }
887

    
888
        if (mimeinfo->mime_type == MIME_APPLICATION_OCTET_STREAM &&
889
            (mimeinfo->filename || mimeinfo->name)) {
890
                const gchar *type;
891
                type = procmime_get_mime_type
892
                        (mimeinfo->filename ? mimeinfo->filename
893
                         : mimeinfo->name);
894
                if (type)
895
                        mimeinfo->mime_type = procmime_scan_mime_type(type);
896
        }
897

    
898
        if (!mimeinfo->content_type)
899
                mimeinfo->content_type = g_strdup("text/plain");
900

    
901
        return mimeinfo;
902
}
903

    
904
static gint procmime_normalize_lbreak(FILE *infp, FILE *outfp)
905
{
906
        gchar buf[BUFFSIZE];
907
        gint len;
908

    
909
        g_return_val_if_fail(infp != NULL, -1);
910
        g_return_val_if_fail(outfp != NULL, -1);
911

    
912
        while (fgets(buf, sizeof(buf), infp) != NULL) {
913
                len = strlen(buf);
914
                if (len == sizeof(buf) - 1 && buf[len - 1] != '\n') {
915
                        if (buf[len - 1] == '\r') {
916
                                ungetc('\r', infp);
917
                                buf[len - 1] = '\0';
918
                        }
919
                        fputs(buf, outfp);
920
                        continue;
921
                }
922
#ifdef G_OS_WIN32
923
                strretchomp(buf);
924
                fputs(buf, outfp);
925
                fputs("\r\n", outfp);
926
#else
927
                strcrchomp(buf);
928
                fputs(buf, outfp);
929
#endif
930
        }
931

    
932
        return 0;
933
}
934

    
935
FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
936
{
937
        gchar buf[BUFFSIZE];
938
        gchar *boundary = NULL;
939
        gint boundary_len = 0;
940
        gboolean tmp_file = FALSE;
941
        gboolean normalize_lbreak = FALSE;
942
        ContentType content_type;
943

    
944
        g_return_val_if_fail(infp != NULL, NULL);
945
        g_return_val_if_fail(mimeinfo != NULL, NULL);
946

    
947
        if (!outfp) {
948
                outfp = my_tmpfile();
949
                if (!outfp) {
950
                        perror("tmpfile");
951
                        return NULL;
952
                }
953
                tmp_file = TRUE;
954
        }
955

    
956
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
957
                boundary = mimeinfo->parent->boundary;
958
                boundary_len = strlen(boundary);
959
        }
960

    
961
        content_type = procmime_scan_mime_type(mimeinfo->content_type);
962
        if (content_type == MIME_TEXT ||
963
            content_type == MIME_TEXT_HTML) {
964
                normalize_lbreak = TRUE;
965
        }
966

    
967
        if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) {
968
                FILE *tmpfp = outfp;
969
                gchar prev_empty_line[3] = "";
970

    
971
                if (normalize_lbreak) {
972
                        tmpfp = my_tmpfile();
973
                        if (!tmpfp) {
974
                                perror("tmpfile");
975
                                if (tmp_file) fclose(outfp);
976
                                return NULL;
977
                        }
978
                }
979

    
980
                while (fgets(buf, sizeof(buf), infp) != NULL &&
981
                       (!boundary ||
982
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
983
                        gint len;
984

    
985
                        if (prev_empty_line[0]) {
986
                                fputs(prev_empty_line, tmpfp);
987
                                prev_empty_line[0] = '\0';
988
                        }
989

    
990
                        if (buf[0] == '\n' ||
991
                            (buf[0] == '\r' && buf[1] == '\n'))
992
                                strcpy(prev_empty_line, buf);
993
                        else {
994
                                len = qp_decode_line(buf);
995
                                fwrite(buf, len, 1, tmpfp);
996
                        }
997
                }
998
                if (!boundary && prev_empty_line[0])
999
                        fputs(prev_empty_line, tmpfp);
1000

    
1001
                if (normalize_lbreak) {
1002
                        if (fflush(tmpfp) == EOF) {
1003
                                perror("fflush");
1004
                                fclose(tmpfp);
1005
                                if (tmp_file) fclose(outfp);
1006
                                return NULL;
1007
                        }
1008
                        rewind(tmpfp);
1009
                        procmime_normalize_lbreak(tmpfp, outfp);
1010
                        fclose(tmpfp);
1011
                }
1012
        } else if (mimeinfo->encoding_type == ENC_BASE64) {
1013
                gchar outbuf[BUFFSIZE];
1014
                gint len;
1015
                Base64Decoder *decoder;
1016
                FILE *tmpfp = outfp;
1017

    
1018
                if (normalize_lbreak) {
1019
                        tmpfp = my_tmpfile();
1020
                        if (!tmpfp) {
1021
                                perror("tmpfile");
1022
                                if (tmp_file) fclose(outfp);
1023
                                return NULL;
1024
                        }
1025
                }
1026

    
1027
                decoder = base64_decoder_new();
1028
                while (fgets(buf, sizeof(buf), infp) != NULL &&
1029
                       (!boundary ||
1030
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
1031
                        len = base64_decoder_decode(decoder, buf,
1032
                                                    (guchar *)outbuf);
1033
                        if (len < 0) {
1034
                                g_warning("Bad BASE64 content\n");
1035
                                break;
1036
                        }
1037
                        fwrite(outbuf, sizeof(gchar), len, tmpfp);
1038
                }
1039
                base64_decoder_free(decoder);
1040

    
1041
                if (normalize_lbreak) {
1042
                        if (fflush(tmpfp) == EOF) {
1043
                                perror("fflush");
1044
                                fclose(tmpfp);
1045
                                if (tmp_file) fclose(outfp);
1046
                                return NULL;
1047
                        }
1048
                        rewind(tmpfp);
1049
                        procmime_normalize_lbreak(tmpfp, outfp);
1050
                        fclose(tmpfp);
1051
                }
1052
        } else if (mimeinfo->encoding_type == ENC_X_UUENCODE) {
1053
                gchar outbuf[BUFFSIZE];
1054
                gint len;
1055
                gboolean flag = FALSE;
1056

    
1057
                while (fgets(buf, sizeof(buf), infp) != NULL &&
1058
                       (!boundary ||
1059
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
1060
                        if(!flag && strncmp(buf,"begin ", 6)) continue;
1061

    
1062
                        if (flag) {
1063
                                len = fromuutobits(outbuf, buf);
1064
                                if (len <= 0) {
1065
                                        if (len < 0) 
1066
                                                g_warning("Bad UUENCODE content(%d)\n", len);
1067
                                        break;
1068
                                }
1069
                                fwrite(outbuf, sizeof(gchar), len, outfp);
1070
                        } else
1071
                                flag = TRUE;
1072
                }
1073
        } else {
1074
                gchar prev_empty_line[3] = "";
1075
                gint len;
1076
                gboolean cont_line = FALSE;
1077

    
1078
                while (fgets(buf, sizeof(buf), infp) != NULL &&
1079
                       (!boundary ||
1080
                        !IS_BOUNDARY(buf, boundary, boundary_len))) {
1081
                        if (prev_empty_line[0]) {
1082
                                fputs(prev_empty_line, outfp);
1083
                                prev_empty_line[0] = '\0';
1084
                        }
1085

    
1086
                        len = strlen(buf);
1087
                        if (len == sizeof(buf) - 1 &&
1088
                            buf[len - 1] != '\n') {
1089
                                if (buf[len - 1] == '\r') {
1090
                                        ungetc('\r', infp);
1091
                                        buf[len - 1] = '\0';
1092
                                }
1093
                                fputs(buf, outfp);
1094
                                cont_line = TRUE;
1095
                                continue;
1096
                        }
1097

    
1098
                        if (normalize_lbreak) {
1099
#ifdef G_OS_WIN32
1100
                                strretchomp(buf);
1101
                                if (!cont_line && buf[0] == '\0')
1102
                                        strcpy(prev_empty_line, "\r\n");
1103
                                else {
1104
                                        fputs(buf, outfp);
1105
                                        fputs("\r\n", outfp);
1106
                                }
1107
#else
1108
                                strcrchomp(buf);
1109
                                if (!cont_line && buf[0] == '\n')
1110
                                        strcpy(prev_empty_line, "\n");
1111
                                else
1112
                                        fputs(buf, outfp);
1113
#endif
1114
                        } else {
1115
                                if (!cont_line &&
1116
                                    (buf[0] == '\n' ||
1117
                                     (buf[0] == '\r' && buf[1] == '\n')))
1118
                                        strcpy(prev_empty_line, buf);
1119
                                else
1120
                                        fputs(buf, outfp);
1121
                        }
1122

    
1123
                        cont_line = FALSE;
1124
                }
1125
                if (!boundary && prev_empty_line[0])
1126
                        fputs(prev_empty_line, outfp);
1127
        }
1128

    
1129
        if (fflush(outfp) == EOF)
1130
                perror("fflush");
1131
        if (ferror(outfp) != 0) {
1132
                g_warning("procmime_decode_content(): Can't write to temporary file\n");
1133
                if (tmp_file) fclose(outfp);
1134
                return NULL;
1135
        }
1136

    
1137
        if (tmp_file) rewind(outfp);
1138
        return outfp;
1139
}
1140

    
1141
gint procmime_get_part(const gchar *outfile, const gchar *infile,
1142
                       MimeInfo *mimeinfo)
1143
{
1144
        FILE *infp;
1145
        gint ret;
1146

    
1147
        g_return_val_if_fail(outfile != NULL, -1);
1148
        g_return_val_if_fail(infile != NULL, -1);
1149
        g_return_val_if_fail(mimeinfo != NULL, -1);
1150

    
1151
        if ((infp = g_fopen(infile, "rb")) == NULL) {
1152
                FILE_OP_ERROR(infile, "fopen");
1153
                return -1;
1154
        }
1155
        ret = procmime_get_part_fp(outfile, infp, mimeinfo);
1156
        fclose(infp);
1157

    
1158
        return ret;
1159
}
1160

    
1161
gint procmime_get_part_fp(const gchar *outfile, FILE *infp, MimeInfo *mimeinfo)
1162
{
1163
        FILE *outfp;
1164
        gchar buf[BUFFSIZE];
1165

    
1166
        g_return_val_if_fail(outfile != NULL, -1);
1167
        g_return_val_if_fail(infp != NULL, -1);
1168
        g_return_val_if_fail(mimeinfo != NULL, -1);
1169

    
1170
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1171
                FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
1172
                return -1;
1173
        }
1174
        if ((outfp = g_fopen(outfile, "wb")) == NULL) {
1175
                FILE_OP_ERROR(outfile, "fopen");
1176
                return -1;
1177
        }
1178

    
1179
        while (fgets(buf, sizeof(buf), infp) != NULL)
1180
                if (buf[0] == '\r' || buf[0] == '\n') break;
1181

    
1182
        if (procmime_decode_content(outfp, infp, mimeinfo) == NULL) {
1183
                fclose(outfp);
1184
                g_unlink(outfile);
1185
                return -1;
1186
        }
1187

    
1188
        if (fclose(outfp) == EOF) {
1189
                FILE_OP_ERROR(outfile, "fclose");
1190
                g_unlink(outfile);
1191
                return -1;
1192
        }
1193

    
1194
        return 0;
1195
}
1196

    
1197
FILE *procmime_get_part_fp_fp(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
1198
{
1199
        gchar buf[BUFFSIZE];
1200

    
1201
        g_return_val_if_fail(infp != NULL, NULL);
1202
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1203

    
1204
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1205
                FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
1206
                return NULL;
1207
        }
1208

    
1209
        while (fgets(buf, sizeof(buf), infp) != NULL)
1210
                if (buf[0] == '\r' || buf[0] == '\n') break;
1211

    
1212
        if ((outfp = procmime_decode_content(outfp, infp, mimeinfo)) == NULL) {
1213
                return NULL;
1214
        }
1215

    
1216
        return outfp;
1217
}
1218

    
1219
gint procmime_get_all_parts(const gchar *dir, const gchar *infile,
1220
                            MimeInfo *mimeinfo)
1221
{
1222
        FILE *fp;
1223
        MimeInfo *partinfo;
1224
        gchar *base, *filename;
1225

    
1226
        g_return_val_if_fail(dir != NULL, -1);
1227
        g_return_val_if_fail(infile != NULL, -1);
1228
        g_return_val_if_fail(mimeinfo != NULL, -1);
1229

    
1230
        if (!is_dir_exist(dir)) {
1231
                g_warning("%s: directory not exist.\n", dir);
1232
                return -1;
1233
        }
1234

    
1235
        if ((fp = g_fopen(infile, "rb")) == NULL) {
1236
                FILE_OP_ERROR(infile, "fopen");
1237
                return -1;
1238
        }
1239

    
1240
        for (partinfo = mimeinfo; partinfo != NULL;
1241
             partinfo = procmime_mimeinfo_next(partinfo)) {
1242
                if (partinfo->filename || partinfo->name) {
1243
                        gint count = 1;
1244

    
1245
                        base = procmime_get_part_file_name(partinfo);
1246
                        filename = g_strconcat(dir, G_DIR_SEPARATOR_S, base,
1247
                                               NULL);
1248

    
1249
                        while (is_file_entry_exist(filename)) {
1250
                                gchar *base_alt;
1251

    
1252
                                base_alt = get_alt_filename(base, count++);
1253
                                g_free(filename);
1254
                                filename = g_strconcat
1255
                                        (dir, G_DIR_SEPARATOR_S, base_alt,
1256
                                         NULL);
1257
                                g_free(base_alt);
1258
                        }
1259

    
1260
                        procmime_get_part_fp(filename, fp, partinfo);
1261

    
1262
                        g_free(filename);
1263
                        g_free(base);
1264
                }
1265
        }
1266

    
1267
        fclose(fp);
1268

    
1269
        return 0;
1270
}
1271

    
1272
FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp,
1273
                                const gchar *encoding)
1274
{
1275
        FILE *tmpfp, *outfp;
1276
        const gchar *src_encoding;
1277
        gboolean conv_fail = FALSE;
1278
        gchar buf[BUFFSIZE];
1279

    
1280
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1281
        g_return_val_if_fail(infp != NULL, NULL);
1282
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1283
                             mimeinfo->mime_type == MIME_TEXT_HTML, NULL);
1284

    
1285
        if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
1286
                perror("fseek");
1287
                return NULL;
1288
        }
1289

    
1290
        while (fgets(buf, sizeof(buf), infp) != NULL)
1291
                if (buf[0] == '\r' || buf[0] == '\n') break;
1292

    
1293
        tmpfp = procmime_decode_content(NULL, infp, mimeinfo);
1294
        if (!tmpfp)
1295
                return NULL;
1296

    
1297
        if ((outfp = my_tmpfile()) == NULL) {
1298
                perror("tmpfile");
1299
                fclose(tmpfp);
1300
                return NULL;
1301
        }
1302

    
1303
        src_encoding = prefs_common.force_charset ? prefs_common.force_charset
1304
                : mimeinfo->charset ? mimeinfo->charset
1305
                : prefs_common.default_encoding;
1306

    
1307
        if (mimeinfo->mime_type == MIME_TEXT) {
1308
                while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1309
                        gchar *str;
1310

    
1311
                        str = conv_codeset_strdup(buf, src_encoding, encoding);
1312
                        if (str) {
1313
                                fputs(str, outfp);
1314
                                g_free(str);
1315
                        } else {
1316
                                conv_fail = TRUE;
1317
                                fputs(buf, outfp);
1318
                        }
1319
                }
1320
        } else if (mimeinfo->mime_type == MIME_TEXT_HTML) {
1321
                HTMLParser *parser;
1322
                CodeConverter *conv;
1323
                const gchar *str;
1324

    
1325
                conv = conv_code_converter_new(src_encoding, encoding);
1326
                parser = html_parser_new(tmpfp, conv);
1327
                while ((str = html_parse(parser)) != NULL) {
1328
                        fputs(str, outfp);
1329
                }
1330
                html_parser_destroy(parser);
1331
                conv_code_converter_destroy(conv);
1332
        }
1333

    
1334
        if (conv_fail)
1335
                g_warning(_("procmime_get_text_content(): Code conversion failed.\n"));
1336

    
1337
        fclose(tmpfp);
1338
        if (fflush(outfp) == EOF) {
1339
                perror("fflush");
1340
                fclose(outfp);
1341
                return NULL;
1342
        }
1343
        rewind(outfp);
1344

    
1345
        return outfp;
1346
}
1347

    
1348
/* search the first text part of (multipart) MIME message,
1349
   decode, convert it and output to outfp. */
1350
FILE *procmime_get_first_text_content(MsgInfo *msginfo, const gchar *encoding)
1351
{
1352
        FILE *infp, *outfp = NULL;
1353
        MimeInfo *mimeinfo, *partinfo;
1354

    
1355
        g_return_val_if_fail(msginfo != NULL, NULL);
1356

    
1357
        mimeinfo = procmime_scan_message(msginfo);
1358
        if (!mimeinfo) return NULL;
1359

    
1360
        if ((infp = procmsg_open_message(msginfo)) == NULL) {
1361
                procmime_mimeinfo_free_all(mimeinfo);
1362
                return NULL;
1363
        }
1364

    
1365
        partinfo = mimeinfo;
1366
        while (partinfo && partinfo->mime_type != MIME_TEXT)
1367
                partinfo = procmime_mimeinfo_next(partinfo);
1368
        if (!partinfo) {
1369
                partinfo = mimeinfo;
1370
                while (partinfo && partinfo->mime_type != MIME_TEXT_HTML)
1371
                        partinfo = procmime_mimeinfo_next(partinfo);
1372
        }
1373

    
1374
        if (partinfo)
1375
                outfp = procmime_get_text_content(partinfo, infp, encoding);
1376

    
1377
        fclose(infp);
1378
        procmime_mimeinfo_free_all(mimeinfo);
1379

    
1380
        return outfp;
1381
}
1382

    
1383
gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename,
1384
                                   const gchar *str, StrFindFunc find_func)
1385
{
1386

    
1387
        FILE *infp, *outfp;
1388
        gchar buf[BUFFSIZE];
1389

    
1390
        g_return_val_if_fail(mimeinfo != NULL, FALSE);
1391
        g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT ||
1392
                             mimeinfo->mime_type == MIME_TEXT_HTML, FALSE);
1393
        g_return_val_if_fail(str != NULL, FALSE);
1394
        g_return_val_if_fail(find_func != NULL, FALSE);
1395

    
1396
        if ((infp = g_fopen(filename, "rb")) == NULL) {
1397
                FILE_OP_ERROR(filename, "fopen");
1398
                return FALSE;
1399
        }
1400

    
1401
        outfp = procmime_get_text_content(mimeinfo, infp, NULL);
1402
        fclose(infp);
1403

    
1404
        if (!outfp)
1405
                return FALSE;
1406

    
1407
        while (fgets(buf, sizeof(buf), outfp) != NULL) {
1408
                strretchomp(buf);
1409
                if (find_func(buf, str)) {
1410
                        fclose(outfp);
1411
                        return TRUE;
1412
                }
1413
        }
1414

    
1415
        fclose(outfp);
1416

    
1417
        return FALSE;
1418
}
1419

    
1420
gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str,
1421
                              StrFindFunc find_func)
1422
{
1423
        MimeInfo *mimeinfo;
1424
        MimeInfo *partinfo;
1425
        gchar *filename;
1426
        gboolean found = FALSE;
1427

    
1428
        g_return_val_if_fail(msginfo != NULL, FALSE);
1429
        g_return_val_if_fail(str != NULL, FALSE);
1430
        g_return_val_if_fail(find_func != NULL, FALSE);
1431

    
1432
        filename = procmsg_get_message_file(msginfo);
1433
        if (!filename) return FALSE;
1434
        mimeinfo = procmime_scan_message(msginfo);
1435

    
1436
        for (partinfo = mimeinfo; partinfo != NULL;
1437
             partinfo = procmime_mimeinfo_next(partinfo)) {
1438
                if (partinfo->mime_type == MIME_TEXT ||
1439
                    partinfo->mime_type == MIME_TEXT_HTML) {
1440
                        if (procmime_find_string_part
1441
                                (partinfo, filename, str, find_func) == TRUE) {
1442
                                found = TRUE;
1443
                                break;
1444
                        }
1445
                }
1446
        }
1447

    
1448
        procmime_mimeinfo_free_all(mimeinfo);
1449
        g_free(filename);
1450

    
1451
        return found;
1452
}
1453

    
1454
gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
1455
{
1456
        gchar *base;
1457
        const gchar *base_;
1458

    
1459
        base_ = mimeinfo->filename ? mimeinfo->filename
1460
                : mimeinfo->name ? mimeinfo->name : "mimetmp";
1461
        base_ = g_basename(base_);
1462
        if (*base_ == '\0') base_ = "mimetmp";
1463
        base = conv_filename_from_utf8(base_);
1464
        subst_for_filename(base);
1465

    
1466
        return base;
1467
}
1468

    
1469
gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
1470
{
1471
        static guint32 id = 0;
1472
        gchar *base;
1473
        gchar *filename;
1474
        gchar f_prefix[10];
1475

    
1476
        g_return_val_if_fail(mimeinfo != NULL, NULL);
1477

    
1478
        g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
1479

    
1480
        if (MIME_TEXT_HTML == mimeinfo->mime_type)
1481
                base = g_strdup("mimetmp.html");
1482
        else
1483
                base = procmime_get_part_file_name(mimeinfo);
1484

    
1485
        filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1486
                               f_prefix, base, NULL);
1487

    
1488
        g_free(base);
1489

    
1490
        return filename;
1491
}
1492

    
1493
ContentType procmime_scan_mime_type(const gchar *mime_type)
1494
{
1495
        ContentType type;
1496

    
1497
        if (!g_ascii_strncasecmp(mime_type, "text/html", 9))
1498
                type = MIME_TEXT_HTML;
1499
        else if (!g_ascii_strncasecmp(mime_type, "text/", 5))
1500
                type = MIME_TEXT;
1501
        else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14))
1502
                type = MIME_MESSAGE_RFC822;
1503
        else if (!g_ascii_strncasecmp(mime_type, "message/", 8))
1504
                type = MIME_TEXT;
1505
        else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream",
1506
                                      24))
1507
                type = MIME_APPLICATION_OCTET_STREAM;
1508
        else if (!g_ascii_strncasecmp(mime_type, "application/", 12))
1509
                type = MIME_APPLICATION;
1510
        else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10))
1511
                type = MIME_MULTIPART;
1512
        else if (!g_ascii_strncasecmp(mime_type, "image/", 6))
1513
                type = MIME_IMAGE;
1514
        else if (!g_ascii_strncasecmp(mime_type, "audio/", 6))
1515
                type = MIME_AUDIO;
1516
        else if (!g_ascii_strncasecmp(mime_type, "video/", 6))
1517
                type = MIME_VIDEO;
1518
        else if (!g_ascii_strcasecmp(mime_type, "text"))
1519
                type = MIME_TEXT;
1520
        else
1521
                type = MIME_UNKNOWN;
1522

    
1523
        return type;
1524
}
1525

    
1526
static GList *mime_type_list = NULL;
1527

    
1528
gchar *procmime_get_mime_type(const gchar *filename)
1529
{
1530
        static GHashTable *mime_type_table = NULL;
1531
        MimeType *mime_type;
1532
        const gchar *p;
1533
        gchar ext[64];
1534
        static gboolean no_mime_type_table = FALSE;
1535

    
1536
        if (no_mime_type_table)
1537
                return NULL;
1538

    
1539
        if (!mime_type_table) {
1540
                mime_type_table = procmime_get_mime_type_table();
1541
                if (!mime_type_table) {
1542
                        no_mime_type_table = TRUE;
1543
                        return NULL;
1544
                }
1545
        }
1546

    
1547
        filename = g_basename(filename);
1548
        p = strrchr(filename, '.');
1549
        if (!p) return NULL;
1550

    
1551
        strncpy2(ext, p + 1, sizeof(ext));
1552
        g_strdown(ext);
1553
        mime_type = g_hash_table_lookup(mime_type_table, ext);
1554
        if (mime_type) {
1555
                gchar *str;
1556

    
1557
                str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
1558
                                  NULL);
1559
                return str;
1560
        }
1561

    
1562
        return NULL;
1563
}
1564

    
1565
static GHashTable *procmime_get_mime_type_table(void)
1566
{
1567
        GHashTable *table = NULL;
1568
        GList *cur;
1569
        MimeType *mime_type;
1570
        gchar **exts;
1571

    
1572
        if (!mime_type_list) {
1573
                GList *list;
1574
                gchar *dir;
1575

    
1576
#ifdef G_OS_WIN32
1577
                dir = g_strconcat(get_startup_dir(),
1578
                                  G_DIR_SEPARATOR_S "etc" G_DIR_SEPARATOR_S
1579
                                  "mime.types", NULL);
1580
                mime_type_list = procmime_get_mime_type_list(dir);
1581
                g_free(dir);
1582
#else
1583
                mime_type_list =
1584
                        procmime_get_mime_type_list(SYSCONFDIR "/mime.types");
1585
                if (!mime_type_list)
1586
                        mime_type_list =
1587
                                procmime_get_mime_type_list("/etc/mime.types");
1588
#endif
1589
                dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1590
                                  "mime.types", NULL);
1591
                list = procmime_get_mime_type_list(dir);
1592
                g_free(dir);
1593
                mime_type_list = g_list_concat(mime_type_list, list);
1594

    
1595
                if (!mime_type_list) {
1596
                        debug_print("mime.types not found\n");
1597
                        return NULL;
1598
                }
1599
        }
1600

    
1601
        table = g_hash_table_new(g_str_hash, g_str_equal);
1602

    
1603
        for (cur = mime_type_list; cur != NULL; cur = cur->next) {
1604
                gint i;
1605
                gchar *key;
1606

    
1607
                mime_type = (MimeType *)cur->data;
1608

    
1609
                if (!mime_type->extension) continue;
1610

    
1611
                exts = g_strsplit(mime_type->extension, " ", 16);
1612
                for (i = 0; exts[i] != NULL; i++) {
1613
                        /* make the key case insensitive */
1614
                        g_strdown(exts[i]);
1615
                        /* use previously dup'd key on overwriting */
1616
                        if (g_hash_table_lookup(table, exts[i]))
1617
                                key = exts[i];
1618
                        else
1619
                                key = g_strdup(exts[i]);
1620
                        g_hash_table_insert(table, key, mime_type);
1621
                }
1622
                g_strfreev(exts);
1623
        }
1624

    
1625
        return table;
1626
}
1627

    
1628
static GList *procmime_get_mime_type_list(const gchar *file)
1629
{
1630
        GList *list = NULL;
1631
        FILE *fp;
1632
        gchar buf[BUFFSIZE];
1633
        gchar *p;
1634
        gchar *delim;
1635
        MimeType *mime_type;
1636

    
1637
        if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1638

    
1639
        debug_print("Reading %s ...\n", file);
1640

    
1641
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1642
                p = strchr(buf, '#');
1643
                if (p) *p = '\0';
1644
                g_strstrip(buf);
1645

    
1646
                p = buf;
1647
                while (*p && !g_ascii_isspace(*p)) p++;
1648
                if (*p) {
1649
                        *p = '\0';
1650
                        p++;
1651
                }
1652
                delim = strchr(buf, '/');
1653
                if (delim == NULL) continue;
1654
                *delim = '\0';
1655

    
1656
                mime_type = g_new(MimeType, 1);
1657
                mime_type->type = g_strdup(buf);
1658
                mime_type->sub_type = g_strdup(delim + 1);
1659

    
1660
                while (*p && g_ascii_isspace(*p)) p++;
1661
                if (*p)
1662
                        mime_type->extension = g_strdup(p);
1663
                else
1664
                        mime_type->extension = NULL;
1665

    
1666
                list = g_list_append(list, mime_type);
1667
        }
1668

    
1669
        fclose(fp);
1670

    
1671
        if (!list)
1672
                g_warning("Can't read mime.types\n");
1673

    
1674
        return list;
1675
}
1676

    
1677
static GList *mailcap_list = NULL;
1678

    
1679
static GList *procmime_parse_mailcap(const gchar *file)
1680
{
1681
        GList *list = NULL;
1682
        FILE *fp;
1683
        gchar buf[BUFFSIZE];
1684
        MailCap *mailcap;
1685

    
1686
        if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1687

    
1688
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1689
                gint i;
1690
                gchar *p;
1691
                gchar **strv;
1692

    
1693
                p = strchr(buf, '#');
1694
                if (p) *p = '\0';
1695
                g_strstrip(buf);
1696

    
1697
                strv = strsplit_with_quote(buf, ";", 0);
1698
                if (!strv)
1699
                        continue;
1700

    
1701
                for (i = 0; strv[i] != NULL; ++i)
1702
                        g_strstrip(strv[i]);
1703

    
1704
                if (!strv[0] || *strv[0] == '\0' ||
1705
                    !strv[1] || *strv[1] == '\0') {
1706
                        g_strfreev(strv);
1707
                        continue;
1708
                }
1709

    
1710
                mailcap = g_new(MailCap, 1);
1711
                mailcap->mime_type = g_strdup(strv[0]);
1712
                mailcap->cmdline_fmt = g_strdup(strv[1]);
1713
                mailcap->needs_terminal = FALSE;
1714

    
1715
                for (i = 0; strv[i] != NULL; ++i) {
1716
                        if (strcmp(strv[i], "needsterminal") == 0)
1717
                                mailcap->needs_terminal = TRUE;
1718
                }
1719

    
1720
                g_strfreev(strv);
1721

    
1722
                list = g_list_append(list, mailcap);
1723
        }
1724

    
1725
        return list;
1726
}
1727

    
1728
gint procmime_execute_open_file(const gchar *file, const gchar *mime_type)
1729
{
1730
        gchar *mime_type_ = NULL;
1731
        GList *cur;
1732
        MailCap *mailcap;
1733
        gchar *cmdline;
1734
        gint ret = -1;
1735
        static gboolean mailcap_list_init = FALSE;
1736

    
1737
        g_return_val_if_fail(file != NULL, -1);
1738

    
1739
        if (!mime_type ||
1740
            g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
1741
                gchar *tmp;
1742
                tmp = procmime_get_mime_type(file);
1743
                if (!tmp)
1744
                        return -1;
1745
                mime_type_ = g_ascii_strdown(tmp, -1);
1746
                g_free(tmp);
1747
        } else
1748
                mime_type_ = g_ascii_strdown(mime_type, -1);
1749

    
1750
        if (!mailcap_list_init && !mailcap_list) {
1751
                GList *list;
1752
                gchar *path;
1753

    
1754
                path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "mailcap",
1755
                                  NULL);
1756
                mailcap_list = procmime_parse_mailcap(path);
1757
                g_free(path);
1758
#ifdef G_OS_WIN32
1759
                path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S "etc"
1760
                                   G_DIR_SEPARATOR_S "mailcap", NULL);
1761
                list = procmime_parse_mailcap(path);
1762
                g_free(path);
1763
#else
1764
                if (!mailcap_list) {
1765
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1766
                                           ".mailcap", NULL);
1767
                        mailcap_list = procmime_parse_mailcap(path);
1768
                        g_free(path);
1769
                }
1770
                list = procmime_parse_mailcap(SYSCONFDIR "/mailcap");
1771
                if (!list)
1772
                        list = procmime_parse_mailcap("/etc/mailcap");
1773
#endif
1774
                mailcap_list = g_list_concat(mailcap_list, list);
1775

    
1776
                mailcap_list_init = TRUE;
1777
        }
1778

    
1779
        for (cur = mailcap_list; cur != NULL; cur = cur->next) {
1780
                mailcap = (MailCap *)cur->data;
1781

    
1782
                if (!g_pattern_match_simple(mailcap->mime_type, mime_type_))
1783
                        continue;
1784
                if (mailcap->needs_terminal)
1785
                        continue;
1786

    
1787
                if (str_find_format_times(mailcap->cmdline_fmt, 's') == 1)
1788
                        cmdline = g_strdup_printf(mailcap->cmdline_fmt, file);
1789
                else
1790
                        cmdline = g_strconcat(mailcap->cmdline_fmt, " \"", file,
1791
                                              "\"", NULL);
1792
                ret = execute_command_line(cmdline, TRUE);
1793
                g_free(cmdline);
1794
                break;
1795
        }
1796

    
1797
        g_free(mime_type_);
1798

    
1799
        return ret;
1800
}
1801

    
1802
EncodingType procmime_get_encoding_for_charset(const gchar *charset)
1803
{
1804
        if (!charset)
1805
                return ENC_8BIT;
1806
        else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
1807
                 !g_ascii_strcasecmp(charset, "US-ASCII"))
1808
                return ENC_7BIT;
1809
        else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
1810
                 !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
1811
                 !g_ascii_strcasecmp(charset, "Windows-1251"))
1812
                return ENC_8BIT;
1813
        else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
1814
                return ENC_QUOTED_PRINTABLE;
1815
        else
1816
                return ENC_8BIT;
1817
}
1818

    
1819
EncodingType procmime_get_encoding_for_text_file(const gchar *file)
1820
{
1821
        FILE *fp;
1822
        guchar buf[BUFFSIZE];
1823
        size_t len;
1824
        size_t octet_chars = 0;
1825
        size_t total_len = 0;
1826
        gfloat octet_percentage;
1827

    
1828
        if ((fp = g_fopen(file, "rb")) == NULL) {
1829
                FILE_OP_ERROR(file, "fopen");
1830
                return ENC_UNKNOWN;
1831
        }
1832

    
1833
        while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1834
                guchar *p;
1835
                gint i;
1836

    
1837
                for (p = buf, i = 0; i < len; ++p, ++i) {
1838
                        if (*p & 0x80)
1839
                                ++octet_chars;
1840
                }
1841
                total_len += len;
1842
        }
1843

    
1844
        fclose(fp);
1845

    
1846
        if (total_len > 0)
1847
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1848
        else
1849
                octet_percentage = 0.0;
1850

    
1851
        debug_print("procmime_get_encoding_for_text_file(): "
1852
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1853
                    100.0 * octet_percentage);
1854

    
1855
        if (octet_percentage > 0.20) {
1856
                debug_print("using BASE64\n");
1857
                return ENC_BASE64;
1858
        } else if (octet_chars > 0) {
1859
                debug_print("using quoted-printable\n");
1860
                return ENC_QUOTED_PRINTABLE;
1861
        } else {
1862
                debug_print("using 7bit\n");
1863
                return ENC_7BIT;
1864
        }
1865
}
1866

    
1867
EncodingType procmime_get_encoding_for_str(const gchar *str)
1868
{
1869
        const guchar *p;
1870
        size_t octet_chars = 0;
1871
        size_t total_len = 0;
1872
        gfloat octet_percentage;
1873

    
1874
        total_len = strlen(str);
1875

    
1876
        for (p = (const guchar *)str; *p != '\0'; ++p) {
1877
                if (*p & 0x80)
1878
                        ++octet_chars;
1879
        }
1880

    
1881
        if (total_len > 0)
1882
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1883
        else
1884
                octet_percentage = 0.0;
1885

    
1886
        debug_print("procmime_get_encoding_for_str(): "
1887
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1888
                    100.0 * octet_percentage);
1889

    
1890
        if (octet_percentage > 0.20) {
1891
                debug_print("using BASE64\n");
1892
                return ENC_BASE64;
1893
        } else if (octet_chars > 0) {
1894
                debug_print("using quoted-printable\n");
1895
                return ENC_QUOTED_PRINTABLE;
1896
        } else {
1897
                debug_print("using 7bit\n");
1898
                return ENC_7BIT;
1899
        }
1900
}
1901

    
1902
const gchar *procmime_get_encoding_str(EncodingType encoding)
1903
{
1904
        static const gchar *encoding_str[] = {
1905
                "7bit", "8bit", "quoted-printable", "base64", "x-uuencode",
1906
                NULL
1907
        };
1908

    
1909
        if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
1910
                return encoding_str[encoding];
1911
        else
1912
                return NULL;
1913
}