Statistics
| Revision:

root / libsylph / procmime.c @ 3307

History | View | Annotate | Download (43 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

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

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

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

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

    
390
                if (eom) break;
391
        }
392

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

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

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

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

    
417
}
418

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

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

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

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

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

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

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

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

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

    
492
        return (gchar *)delim;
493
}
494

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

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

    
505
        return g_strdup(value);
506
}
507

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

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

    
528
        g_strstrip(hvalue);
529

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

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

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

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

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

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

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

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

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

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

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

    
607
                begin = value;
608

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

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

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

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

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

    
663
#undef CONCAT_CONT_VALUE
664

    
665
                if (count == -1 && 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
670
                                (cont_value, charset);
671
                        g_free(cont_value);
672
                        cont_value = NULL;
673
                        plist = g_slist_prepend(plist, mparam);
674
                }
675

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

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

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

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

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

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

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

    
729
        return mparams;
730
}
731

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

    
736
        if (!mparams)
737
                return;
738

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

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

    
757
        mparams = procmime_parse_mime_parameter(content_type);
758

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

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

    
779
        procmime_mime_params_free(mparams);
780
}
781

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

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

    
795
        mparams = procmime_parse_mime_parameter(content_type);
796

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

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

    
814
        procmime_mime_params_free(mparams);
815

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

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

    
831
        mparams = procmime_parse_mime_parameter(content_disposition);
832

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

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

    
843
        procmime_mime_params_free(mparams);
844
}
845

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

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

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

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

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

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

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

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

    
902
        return mimeinfo;
903
}
904

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

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

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

    
933
        return 0;
934
}
935

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1159
        return ret;
1160
}
1161

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

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

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

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

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

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

    
1195
        return 0;
1196
}
1197

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

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

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

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

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

    
1217
        return outfp;
1218
}
1219

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

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

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

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

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

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

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

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

    
1261
                        procmime_get_part_fp(filename, fp, partinfo);
1262

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

    
1268
        fclose(fp);
1269

    
1270
        return 0;
1271
}
1272

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

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

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

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

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

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

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

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

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

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

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

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

    
1346
        return outfp;
1347
}
1348

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

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

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

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

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

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

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

    
1381
        return outfp;
1382
}
1383

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

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

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

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

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

    
1405
        if (!outfp)
1406
                return FALSE;
1407

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

    
1416
        fclose(outfp);
1417

    
1418
        return FALSE;
1419
}
1420

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

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

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

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

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

    
1452
        return found;
1453
}
1454

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

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

    
1467
        return base;
1468
}
1469

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

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

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

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

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

    
1489
        g_free(base);
1490

    
1491
        return filename;
1492
}
1493

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

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

    
1524
        return type;
1525
}
1526

    
1527
static GList *mime_type_list = NULL;
1528

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

    
1537
        if (no_mime_type_table)
1538
                return NULL;
1539

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

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

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

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

    
1563
        return NULL;
1564
}
1565

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

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

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

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

    
1602
        table = g_hash_table_new(g_str_hash, g_str_equal);
1603

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

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

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

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

    
1626
        return table;
1627
}
1628

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

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

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

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

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

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

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

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

    
1670
        fclose(fp);
1671

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

    
1675
        return list;
1676
}
1677

    
1678
static GList *mailcap_list = NULL;
1679

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

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

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

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

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

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

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

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

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

    
1721
                g_strfreev(strv);
1722

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

    
1726
        fclose(fp);
1727

    
1728
        return list;
1729
}
1730

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

    
1740
        g_return_val_if_fail(file != NULL, -1);
1741

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

    
1753
        if (!mailcap_list_init && !mailcap_list) {
1754
                GList *list;
1755
                gchar *path;
1756

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

    
1779
                mailcap_list_init = TRUE;
1780
        }
1781

    
1782
        for (cur = mailcap_list; cur != NULL; cur = cur->next) {
1783
                mailcap = (MailCap *)cur->data;
1784

    
1785
                if (!g_pattern_match_simple(mailcap->mime_type, mime_type_))
1786
                        continue;
1787
                if (mailcap->needs_terminal)
1788
                        continue;
1789

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

    
1800
        g_free(mime_type_);
1801

    
1802
        return ret;
1803
}
1804

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

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

    
1831
        if ((fp = g_fopen(file, "rb")) == NULL) {
1832
                FILE_OP_ERROR(file, "fopen");
1833
                return ENC_UNKNOWN;
1834
        }
1835

    
1836
        while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1837
                guchar *p;
1838
                gint i;
1839

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

    
1847
        fclose(fp);
1848

    
1849
        if (total_len > 0)
1850
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1851
        else
1852
                octet_percentage = 0.0;
1853

    
1854
        debug_print("procmime_get_encoding_for_text_file(): "
1855
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1856
                    100.0 * octet_percentage);
1857

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

    
1870
EncodingType procmime_get_encoding_for_str(const gchar *str)
1871
{
1872
        const guchar *p;
1873
        size_t octet_chars = 0;
1874
        size_t total_len = 0;
1875
        gfloat octet_percentage;
1876

    
1877
        total_len = strlen(str);
1878

    
1879
        for (p = (const guchar *)str; *p != '\0'; ++p) {
1880
                if (*p & 0x80)
1881
                        ++octet_chars;
1882
        }
1883

    
1884
        if (total_len > 0)
1885
                octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1886
        else
1887
                octet_percentage = 0.0;
1888

    
1889
        debug_print("procmime_get_encoding_for_str(): "
1890
                    "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1891
                    100.0 * octet_percentage);
1892

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

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

    
1912
        if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
1913
                return encoding_str[encoding];
1914
        else
1915
                return NULL;
1916
}