summaryrefslogtreecommitdiff
path: root/libsylph/procmime.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/procmime.c')
-rw-r--r--libsylph/procmime.c1955
1 files changed, 1955 insertions, 0 deletions
diff --git a/libsylph/procmime.c b/libsylph/procmime.c
new file mode 100644
index 0000000..4c3a90c
--- /dev/null
+++ b/libsylph/procmime.c
@@ -0,0 +1,1955 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2017 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
53static GHashTable *procmime_get_mime_type_table (void);
54static GList *procmime_get_mime_type_list (const gchar *file);
55
56
57MimeInfo *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
68void 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
94MimeInfo *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
114void 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
141MimeInfo *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
173void 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
188MimeInfo *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
213MimeInfo *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
241void 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
397void 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
419void 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
439typedef struct
440{
441 gchar *name;
442 gchar *value;
443} MimeParam;
444
445typedef struct
446{
447 gchar *hvalue;
448 GSList *plist;
449} MimeParams;
450
451static 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
495static 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
508static 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
732static 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
750void 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
782void 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
825void 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
846enum
847{
848 H_CONTENT_TRANSFER_ENCODING = 0,
849 H_CONTENT_TYPE = 1,
850 H_CONTENT_DISPOSITION = 2
851};
852
853MimeInfo *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
905static 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
936FILE *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
1142gint 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
1162gint 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
1198FILE *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
1220gint 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
1273FILE *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. */
1351FILE *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
1384gboolean 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
1421gboolean 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
1455gchar *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
1470gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
1471{
1472 static guint32 id = 0;
1473 gchar *base;
1474 gchar *filename = NULL;
1475 gchar f_prefix[10];
1476
1477 g_return_val_if_fail(mimeinfo != NULL, NULL);
1478
1479 if (MIME_TEXT_HTML == mimeinfo->mime_type)
1480 base = g_strdup("mimetmp.html");
1481 else
1482 base = procmime_get_part_file_name(mimeinfo);
1483
1484 do {
1485 g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
1486 if (filename)
1487 g_free(filename);
1488 filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1489 f_prefix, base, NULL);
1490 } while (is_file_entry_exist(filename));
1491
1492 g_free(base);
1493
1494 debug_print("procmime_get_tmp_file_name: %s\n", filename);
1495
1496 return filename;
1497}
1498
1499gchar *procmime_get_tmp_file_name_for_user(MimeInfo *mimeinfo)
1500{
1501 gchar *base;
1502 gchar *filename;
1503 gint count = 1;
1504
1505 g_return_val_if_fail(mimeinfo != NULL, NULL);
1506
1507 if (MIME_TEXT_HTML == mimeinfo->mime_type)
1508 base = g_strdup("mimetmp.html");
1509 else
1510 base = procmime_get_part_file_name(mimeinfo);
1511
1512 filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1513 base, NULL);
1514 while (is_file_entry_exist(filename)) {
1515 gchar *base_alt;
1516
1517 base_alt = get_alt_filename(base, count++);
1518 g_free(filename);
1519 filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1520 base_alt, NULL);
1521 g_free(base_alt);
1522 }
1523
1524 g_free(base);
1525
1526 debug_print("procmime_get_tmp_file_name_for_user: %s\n", filename);
1527
1528 return filename;
1529}
1530
1531ContentType procmime_scan_mime_type(const gchar *mime_type)
1532{
1533 ContentType type;
1534
1535 if (!g_ascii_strncasecmp(mime_type, "text/html", 9))
1536 type = MIME_TEXT_HTML;
1537 else if (!g_ascii_strncasecmp(mime_type, "text/", 5))
1538 type = MIME_TEXT;
1539 else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14))
1540 type = MIME_MESSAGE_RFC822;
1541 else if (!g_ascii_strncasecmp(mime_type, "message/", 8))
1542 type = MIME_TEXT;
1543 else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream",
1544 24))
1545 type = MIME_APPLICATION_OCTET_STREAM;
1546 else if (!g_ascii_strncasecmp(mime_type, "application/", 12))
1547 type = MIME_APPLICATION;
1548 else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10))
1549 type = MIME_MULTIPART;
1550 else if (!g_ascii_strncasecmp(mime_type, "image/", 6))
1551 type = MIME_IMAGE;
1552 else if (!g_ascii_strncasecmp(mime_type, "audio/", 6))
1553 type = MIME_AUDIO;
1554 else if (!g_ascii_strncasecmp(mime_type, "video/", 6))
1555 type = MIME_VIDEO;
1556 else if (!g_ascii_strcasecmp(mime_type, "text"))
1557 type = MIME_TEXT;
1558 else
1559 type = MIME_UNKNOWN;
1560
1561 return type;
1562}
1563
1564static GList *mime_type_list = NULL;
1565
1566gchar *procmime_get_mime_type(const gchar *filename)
1567{
1568 static GHashTable *mime_type_table = NULL;
1569 MimeType *mime_type;
1570 const gchar *p;
1571 gchar ext[64];
1572 static gboolean no_mime_type_table = FALSE;
1573
1574 if (no_mime_type_table)
1575 return NULL;
1576
1577 if (!mime_type_table) {
1578 mime_type_table = procmime_get_mime_type_table();
1579 if (!mime_type_table) {
1580 no_mime_type_table = TRUE;
1581 return NULL;
1582 }
1583 }
1584
1585 filename = g_basename(filename);
1586 p = strrchr(filename, '.');
1587 if (!p) return NULL;
1588
1589 strncpy2(ext, p + 1, sizeof(ext));
1590 g_strdown(ext);
1591 mime_type = g_hash_table_lookup(mime_type_table, ext);
1592 if (mime_type) {
1593 gchar *str;
1594
1595 str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
1596 NULL);
1597 return str;
1598 }
1599
1600 return NULL;
1601}
1602
1603static GHashTable *procmime_get_mime_type_table(void)
1604{
1605 GHashTable *table = NULL;
1606 GList *cur;
1607 MimeType *mime_type;
1608 gchar **exts;
1609
1610 if (!mime_type_list) {
1611 GList *list;
1612 gchar *dir;
1613
1614#ifdef G_OS_WIN32
1615 dir = g_strconcat(get_startup_dir(),
1616 G_DIR_SEPARATOR_S "etc" G_DIR_SEPARATOR_S
1617 "mime.types", NULL);
1618 mime_type_list = procmime_get_mime_type_list(dir);
1619 g_free(dir);
1620#else
1621 mime_type_list =
1622 procmime_get_mime_type_list(SYSCONFDIR "/mime.types");
1623 if (!mime_type_list)
1624 mime_type_list =
1625 procmime_get_mime_type_list("/etc/mime.types");
1626#endif
1627 dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1628 "mime.types", NULL);
1629 list = procmime_get_mime_type_list(dir);
1630 g_free(dir);
1631 mime_type_list = g_list_concat(mime_type_list, list);
1632
1633 if (!mime_type_list) {
1634 debug_print("mime.types not found\n");
1635 return NULL;
1636 }
1637 }
1638
1639 table = g_hash_table_new(g_str_hash, g_str_equal);
1640
1641 for (cur = mime_type_list; cur != NULL; cur = cur->next) {
1642 gint i;
1643 gchar *key;
1644
1645 mime_type = (MimeType *)cur->data;
1646
1647 if (!mime_type->extension) continue;
1648
1649 exts = g_strsplit(mime_type->extension, " ", 16);
1650 for (i = 0; exts[i] != NULL; i++) {
1651 /* make the key case insensitive */
1652 g_strdown(exts[i]);
1653 /* use previously dup'd key on overwriting */
1654 if (g_hash_table_lookup(table, exts[i]))
1655 key = exts[i];
1656 else
1657 key = g_strdup(exts[i]);
1658 g_hash_table_insert(table, key, mime_type);
1659 }
1660 g_strfreev(exts);
1661 }
1662
1663 return table;
1664}
1665
1666static GList *procmime_get_mime_type_list(const gchar *file)
1667{
1668 GList *list = NULL;
1669 FILE *fp;
1670 gchar buf[BUFFSIZE];
1671 gchar *p;
1672 gchar *delim;
1673 MimeType *mime_type;
1674
1675 if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1676
1677 debug_print("Reading %s ...\n", file);
1678
1679 while (fgets(buf, sizeof(buf), fp) != NULL) {
1680 p = strchr(buf, '#');
1681 if (p) *p = '\0';
1682 g_strstrip(buf);
1683
1684 p = buf;
1685 while (*p && !g_ascii_isspace(*p)) p++;
1686 if (*p) {
1687 *p = '\0';
1688 p++;
1689 }
1690 delim = strchr(buf, '/');
1691 if (delim == NULL) continue;
1692 *delim = '\0';
1693
1694 mime_type = g_new(MimeType, 1);
1695 mime_type->type = g_strdup(buf);
1696 mime_type->sub_type = g_strdup(delim + 1);
1697
1698 while (*p && g_ascii_isspace(*p)) p++;
1699 if (*p)
1700 mime_type->extension = g_strdup(p);
1701 else
1702 mime_type->extension = NULL;
1703
1704 list = g_list_append(list, mime_type);
1705 }
1706
1707 fclose(fp);
1708
1709 if (!list)
1710 g_warning("Can't read mime.types\n");
1711
1712 return list;
1713}
1714
1715static GList *mailcap_list = NULL;
1716
1717static GList *procmime_parse_mailcap(const gchar *file)
1718{
1719 GList *list = NULL;
1720 FILE *fp;
1721 gchar buf[BUFFSIZE];
1722 MailCap *mailcap;
1723
1724 if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
1725
1726 while (fgets(buf, sizeof(buf), fp) != NULL) {
1727 gint i;
1728 gchar *p;
1729 gchar **strv;
1730
1731 p = strchr(buf, '#');
1732 if (p) *p = '\0';
1733 g_strstrip(buf);
1734
1735 strv = strsplit_with_quote(buf, ";", 0);
1736 if (!strv)
1737 continue;
1738
1739 for (i = 0; strv[i] != NULL; ++i)
1740 g_strstrip(strv[i]);
1741
1742 if (!strv[0] || *strv[0] == '\0' ||
1743 !strv[1] || *strv[1] == '\0') {
1744 g_strfreev(strv);
1745 continue;
1746 }
1747
1748 mailcap = g_new(MailCap, 1);
1749 mailcap->mime_type = g_strdup(strv[0]);
1750 mailcap->cmdline_fmt = g_strdup(strv[1]);
1751 mailcap->needs_terminal = FALSE;
1752
1753 for (i = 0; strv[i] != NULL; ++i) {
1754 if (strcmp(strv[i], "needsterminal") == 0)
1755 mailcap->needs_terminal = TRUE;
1756 }
1757
1758 g_strfreev(strv);
1759
1760 list = g_list_append(list, mailcap);
1761 }
1762
1763 fclose(fp);
1764
1765 return list;
1766}
1767
1768gint procmime_execute_open_file(const gchar *file, const gchar *mime_type)
1769{
1770 gchar *mime_type_ = NULL;
1771 GList *cur;
1772 MailCap *mailcap;
1773 gchar *cmdline;
1774 gint ret = -1;
1775 static gboolean mailcap_list_init = FALSE;
1776
1777 g_return_val_if_fail(file != NULL, -1);
1778
1779 if (!mime_type ||
1780 g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
1781 gchar *tmp;
1782 tmp = procmime_get_mime_type(file);
1783 if (!tmp)
1784 return -1;
1785 mime_type_ = g_ascii_strdown(tmp, -1);
1786 g_free(tmp);
1787 } else
1788 mime_type_ = g_ascii_strdown(mime_type, -1);
1789
1790 if (!mailcap_list_init && !mailcap_list) {
1791 GList *list;
1792 gchar *path;
1793
1794 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "mailcap",
1795 NULL);
1796 mailcap_list = procmime_parse_mailcap(path);
1797 g_free(path);
1798#ifdef G_OS_WIN32
1799 path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S "etc"
1800 G_DIR_SEPARATOR_S "mailcap", NULL);
1801 list = procmime_parse_mailcap(path);
1802 g_free(path);
1803#else
1804 if (!mailcap_list) {
1805 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1806 ".mailcap", NULL);
1807 mailcap_list = procmime_parse_mailcap(path);
1808 g_free(path);
1809 }
1810 list = procmime_parse_mailcap(SYSCONFDIR "/mailcap");
1811 if (!list)
1812 list = procmime_parse_mailcap("/etc/mailcap");
1813#endif
1814 mailcap_list = g_list_concat(mailcap_list, list);
1815
1816 mailcap_list_init = TRUE;
1817 }
1818
1819 for (cur = mailcap_list; cur != NULL; cur = cur->next) {
1820 mailcap = (MailCap *)cur->data;
1821
1822 if (!g_pattern_match_simple(mailcap->mime_type, mime_type_))
1823 continue;
1824 if (mailcap->needs_terminal)
1825 continue;
1826
1827 if (str_find_format_times(mailcap->cmdline_fmt, 's') == 1)
1828 cmdline = g_strdup_printf(mailcap->cmdline_fmt, file);
1829 else
1830 cmdline = g_strconcat(mailcap->cmdline_fmt, " \"", file,
1831 "\"", NULL);
1832 ret = execute_command_line(cmdline, TRUE);
1833 g_free(cmdline);
1834 break;
1835 }
1836
1837 g_free(mime_type_);
1838
1839 return ret;
1840}
1841
1842EncodingType procmime_get_encoding_for_charset(const gchar *charset)
1843{
1844 if (!charset)
1845 return ENC_8BIT;
1846 else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
1847 !g_ascii_strcasecmp(charset, "US-ASCII"))
1848 return ENC_7BIT;
1849 else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
1850 !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
1851 !g_ascii_strcasecmp(charset, "Windows-1251"))
1852 return ENC_8BIT;
1853 else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
1854 return ENC_QUOTED_PRINTABLE;
1855 else
1856 return ENC_8BIT;
1857}
1858
1859EncodingType procmime_get_encoding_for_text_file(const gchar *file)
1860{
1861 FILE *fp;
1862 guchar buf[BUFFSIZE];
1863 size_t len;
1864 size_t octet_chars = 0;
1865 size_t total_len = 0;
1866 gfloat octet_percentage;
1867
1868 if ((fp = g_fopen(file, "rb")) == NULL) {
1869 FILE_OP_ERROR(file, "fopen");
1870 return ENC_UNKNOWN;
1871 }
1872
1873 while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1874 guchar *p;
1875 gint i;
1876
1877 for (p = buf, i = 0; i < len; ++p, ++i) {
1878 if ((*p & 0x80) || *p == 0x00 || *p == 0x7f) {
1879 ++octet_chars;
1880 }
1881 }
1882 total_len += len;
1883 }
1884
1885 fclose(fp);
1886
1887 if (total_len > 0)
1888 octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1889 else
1890 octet_percentage = 0.0;
1891
1892 debug_print("procmime_get_encoding_for_text_file(): "
1893 "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1894 100.0 * octet_percentage);
1895
1896 if (octet_percentage > 0.20) {
1897 debug_print("using BASE64\n");
1898 return ENC_BASE64;
1899 } else if (octet_chars > 0) {
1900 debug_print("using quoted-printable\n");
1901 return ENC_QUOTED_PRINTABLE;
1902 } else {
1903 debug_print("using 7bit\n");
1904 return ENC_7BIT;
1905 }
1906}
1907
1908EncodingType procmime_get_encoding_for_str(const gchar *str)
1909{
1910 const guchar *p;
1911 size_t octet_chars = 0;
1912 size_t total_len = 0;
1913 gfloat octet_percentage;
1914
1915 total_len = strlen(str);
1916
1917 for (p = (const guchar *)str; *p != '\0'; ++p) {
1918 if ((*p & 0x80) || *p == 0x7f) {
1919 ++octet_chars;
1920 }
1921 }
1922
1923 if (total_len > 0)
1924 octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1925 else
1926 octet_percentage = 0.0;
1927
1928 debug_print("procmime_get_encoding_for_str(): "
1929 "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
1930 100.0 * octet_percentage);
1931
1932 if (octet_percentage > 0.20) {
1933 debug_print("using BASE64\n");
1934 return ENC_BASE64;
1935 } else if (octet_chars > 0) {
1936 debug_print("using quoted-printable\n");
1937 return ENC_QUOTED_PRINTABLE;
1938 } else {
1939 debug_print("using 7bit\n");
1940 return ENC_7BIT;
1941 }
1942}
1943
1944const gchar *procmime_get_encoding_str(EncodingType encoding)
1945{
1946 static const gchar *encoding_str[] = {
1947 "7bit", "8bit", "quoted-printable", "base64", "x-uuencode",
1948 NULL
1949 };
1950
1951 if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
1952 return encoding_str[encoding];
1953 else
1954 return NULL;
1955}