2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
35 #include <sys/types.h>
41 #include "procheader.h"
43 #include "quoted-printable.h"
50 #include "prefs_common.h"
51 #include "prefs_gtk.h"
52 #include "alertpanel.h"
55 static GHashTable *procmime_get_mime_type_table (void);
56 static MimeInfo *procmime_scan_file_short(const gchar *filename);
57 static MimeInfo *procmime_scan_queue_file_short(const gchar *filename);
58 static MimeInfo *procmime_scan_queue_file_full(const gchar *filename, gboolean short_scan);
60 MimeInfo *procmime_mimeinfo_new(void)
64 mimeinfo = g_new0(MimeInfo, 1);
65 mimeinfo->content = MIMECONTENT_EMPTY;
66 mimeinfo->data.filename = NULL;
68 mimeinfo->type = MIMETYPE_UNKNOWN;
69 mimeinfo->encoding_type = ENC_UNKNOWN;
70 mimeinfo->typeparameters = g_hash_table_new(g_str_hash, g_str_equal);
72 mimeinfo->disposition = DISPOSITIONTYPE_UNKNOWN;
73 mimeinfo->dispositionparameters
74 = g_hash_table_new(g_str_hash, g_str_equal);
76 mimeinfo->node = g_node_new(mimeinfo);
81 static gboolean procmime_mimeinfo_parameters_destroy(gpointer key, gpointer value, gpointer user_data)
89 static gchar *forced_charset = NULL;
91 void procmime_force_charset(const gchar *str)
93 g_free(forced_charset);
94 forced_charset = NULL;
96 forced_charset = g_strdup(str);
99 static EncodingType forced_encoding = 0;
101 void procmime_force_encoding(EncodingType encoding)
103 forced_encoding = encoding;
106 static gboolean free_func(GNode *node, gpointer data)
108 MimeInfo *mimeinfo = (MimeInfo *) node->data;
110 switch (mimeinfo->content) {
111 case MIMECONTENT_FILE:
113 g_unlink(mimeinfo->data.filename);
114 g_free(mimeinfo->data.filename);
117 case MIMECONTENT_MEM:
119 g_free(mimeinfo->data.mem);
124 g_free(mimeinfo->subtype);
125 g_free(mimeinfo->description);
126 g_free(mimeinfo->id);
127 g_free(mimeinfo->location);
129 g_hash_table_foreach_remove(mimeinfo->typeparameters,
130 procmime_mimeinfo_parameters_destroy, NULL);
131 g_hash_table_destroy(mimeinfo->typeparameters);
132 g_hash_table_foreach_remove(mimeinfo->dispositionparameters,
133 procmime_mimeinfo_parameters_destroy, NULL);
134 g_hash_table_destroy(mimeinfo->dispositionparameters);
136 if (mimeinfo->privacy)
137 privacy_free_privacydata(mimeinfo->privacy);
144 void procmime_mimeinfo_free_all(MimeInfo *mimeinfo)
151 node = mimeinfo->node;
152 g_node_traverse(node, G_IN_ORDER, G_TRAVERSE_ALL, -1, free_func, NULL);
154 g_node_destroy(node);
157 MimeInfo *procmime_mimeinfo_parent(MimeInfo *mimeinfo)
159 g_return_val_if_fail(mimeinfo != NULL, NULL);
160 g_return_val_if_fail(mimeinfo->node != NULL, NULL);
162 if (mimeinfo->node->parent == NULL)
164 return (MimeInfo *) mimeinfo->node->parent->data;
167 MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo)
169 g_return_val_if_fail(mimeinfo != NULL, NULL);
170 g_return_val_if_fail(mimeinfo->node != NULL, NULL);
172 if (mimeinfo->node->children)
173 return (MimeInfo *) mimeinfo->node->children->data;
174 if (mimeinfo->node->next)
175 return (MimeInfo *) mimeinfo->node->next->data;
177 if (mimeinfo->node->parent == NULL)
180 while (mimeinfo->node->parent != NULL) {
181 mimeinfo = (MimeInfo *) mimeinfo->node->parent->data;
182 if (mimeinfo->node->next)
183 return (MimeInfo *) mimeinfo->node->next->data;
189 MimeInfo *procmime_scan_message(MsgInfo *msginfo)
194 filename = procmsg_get_message_file_path(msginfo);
195 if (!filename || !is_file_exist(filename)) {
200 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
201 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
202 mimeinfo = procmime_scan_file(filename);
204 mimeinfo = procmime_scan_queue_file(filename);
211 MimeInfo *procmime_scan_message_short(MsgInfo *msginfo)
216 filename = procmsg_get_message_file_path(msginfo);
217 if (!filename || !is_file_exist(filename)) {
222 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
223 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
224 mimeinfo = procmime_scan_file_short(filename);
226 mimeinfo = procmime_scan_queue_file_short(filename);
234 H_CONTENT_TRANSFER_ENCODING = 0,
236 H_CONTENT_DISPOSITION = 2,
237 H_CONTENT_DESCRIPTION = 3,
241 const gchar *procmime_mimeinfo_get_parameter(MimeInfo *mimeinfo, const gchar *name)
245 g_return_val_if_fail(mimeinfo != NULL, NULL);
246 g_return_val_if_fail(name != NULL, NULL);
248 value = g_hash_table_lookup(mimeinfo->dispositionparameters, name);
250 value = g_hash_table_lookup(mimeinfo->typeparameters, name);
255 #define FLUSH_LASTLINE() { \
256 if (*lastline != '\0') { \
258 strretchomp(lastline); \
259 llen = strlen(lastline); \
260 if (lastline[llen-1] == ' ' && strcmp(lastline,"-- ")) { \
261 /* this is flowed */ \
263 lastline[llen-1] = '\0'; \
264 fputs(lastline, outfp); \
266 fputs(lastline, outfp); \
267 fputs("\n", outfp); \
270 strcpy(lastline, buf); \
273 gboolean procmime_decode_content(MimeInfo *mimeinfo)
280 gboolean tmp_file = FALSE;
281 gboolean flowed = FALSE;
282 gboolean delsp = FALSE;
283 EncodingType encoding = forced_encoding
285 : mimeinfo->encoding_type;
286 gchar lastline[BUFFSIZE];
287 memset(lastline, 0, BUFFSIZE);
289 g_return_val_if_fail(mimeinfo != NULL, FALSE);
291 if (prefs_common.respect_flowed_format &&
292 mimeinfo->type == MIMETYPE_TEXT &&
293 !strcasecmp(mimeinfo->subtype, "plain")) {
294 if (procmime_mimeinfo_get_parameter(mimeinfo, "format") != NULL &&
295 !strcasecmp(procmime_mimeinfo_get_parameter(mimeinfo, "format"),"flowed"))
298 procmime_mimeinfo_get_parameter(mimeinfo, "delsp") != NULL &&
299 !strcasecmp(procmime_mimeinfo_get_parameter(mimeinfo, "delsp"),"yes"))
304 encoding == ENC_UNKNOWN ||
305 encoding == ENC_BINARY ||
306 encoding == ENC_7BIT ||
311 infp = g_fopen(mimeinfo->data.filename, "rb");
316 fseek(infp, mimeinfo->offset, SEEK_SET);
318 outfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
325 readend = mimeinfo->offset + mimeinfo->length;
327 if (encoding == ENC_QUOTED_PRINTABLE) {
328 while ((ftell(infp) < readend) && (fgets(buf, sizeof(buf), infp) != NULL)) {
330 len = qp_decode_line(buf);
333 fwrite(buf, 1, len, outfp);
340 } else if (encoding == ENC_BASE64) {
341 gchar outbuf[BUFFSIZE];
343 Base64Decoder *decoder;
344 gboolean got_error = FALSE;
345 gboolean uncanonicalize = FALSE;
348 if (mimeinfo->type == MIMETYPE_TEXT ||
349 mimeinfo->type == MIMETYPE_MESSAGE) {
350 uncanonicalize = TRUE;
351 tmpfp = my_tmpfile();
354 if (tmp_file) fclose(outfp);
360 decoder = base64_decoder_new();
361 while ((ftell(infp) < readend) && (fgets(buf, sizeof(buf), infp) != NULL)) {
362 len = base64_decoder_decode(decoder, buf, outbuf);
363 if (len < 0 && !got_error) {
364 g_warning("Bad BASE64 content.\n");
365 fwrite(_("[Error decoding BASE64]\n"),
367 strlen(_("[Error decoding BASE64]\n")),
371 } else if (len >= 0) {
372 /* print out the error message only once
374 fwrite(outbuf, sizeof(gchar), len, tmpfp);
378 base64_decoder_free(decoder);
380 if (uncanonicalize) {
382 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
388 } else if (encoding == ENC_X_UUENCODE) {
389 gchar outbuf[BUFFSIZE];
391 gboolean flag = FALSE;
393 while ((ftell(infp) < readend) && (fgets(buf, sizeof(buf), infp) != NULL)) {
394 if (!flag && strncmp(buf,"begin ", 6)) continue;
397 len = fromuutobits(outbuf, buf);
400 g_warning("Bad UUENCODE content(%d)\n", len);
403 fwrite(outbuf, sizeof(gchar), len, outfp);
408 while ((ftell(infp) < readend) && (fgets(buf, sizeof(buf), infp) != NULL)) {
422 stat(tmpfilename, &statbuf);
423 if (mimeinfo->tmp && (mimeinfo->data.filename != NULL))
424 g_unlink(mimeinfo->data.filename);
425 g_free(mimeinfo->data.filename);
426 mimeinfo->data.filename = tmpfilename;
427 mimeinfo->tmp = TRUE;
428 mimeinfo->offset = 0;
429 mimeinfo->length = statbuf.st_size;
430 mimeinfo->encoding_type = ENC_BINARY;
435 #define B64_LINE_SIZE 57
436 #define B64_BUFFSIZE 77
438 gboolean procmime_encode_content(MimeInfo *mimeinfo, EncodingType encoding)
440 FILE *infp = NULL, *outfp;
445 if (mimeinfo->content == MIMECONTENT_EMPTY)
448 if (mimeinfo->encoding_type != ENC_UNKNOWN &&
449 mimeinfo->encoding_type != ENC_BINARY &&
450 mimeinfo->encoding_type != ENC_7BIT &&
451 mimeinfo->encoding_type != ENC_8BIT)
452 if(!procmime_decode_content(mimeinfo))
455 outfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmpfilename);
461 if (mimeinfo->content == MIMECONTENT_FILE) {
462 if ((infp = g_fopen(mimeinfo->data.filename, "rb")) == NULL) {
463 g_warning("Can't open file %s\n", mimeinfo->data.filename);
466 } else if (mimeinfo->content == MIMECONTENT_MEM) {
467 infp = str_open_as_stream(mimeinfo->data.mem);
472 if (encoding == ENC_BASE64) {
473 gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
475 gchar *tmp_file = NULL;
477 if (mimeinfo->type == MIMETYPE_TEXT ||
478 mimeinfo->type == MIMETYPE_MESSAGE) {
479 if (mimeinfo->content == MIMECONTENT_FILE) {
480 tmp_file = get_tmp_file();
481 if (canonicalize_file(mimeinfo->data.filename, tmp_file) < 0) {
486 if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
487 FILE_OP_ERROR(tmp_file, "fopen");
494 gchar *out = canonicalize_str(mimeinfo->data.mem);
496 infp = str_open_as_stream(out);
504 while ((len = fread(inbuf, sizeof(gchar),
505 B64_LINE_SIZE, tmp_fp))
507 base64_encode(outbuf, inbuf, B64_LINE_SIZE);
508 fputs(outbuf, outfp);
511 if (len > 0 && feof(tmp_fp)) {
512 base64_encode(outbuf, inbuf, len);
513 fputs(outbuf, outfp);
522 } else if (encoding == ENC_QUOTED_PRINTABLE) {
523 gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
525 while (fgets(inbuf, sizeof(inbuf), infp) != NULL) {
526 qp_encode_line(outbuf, inbuf);
528 if (!strncmp("From ", outbuf, sizeof("From ")-1)) {
529 gchar *tmpbuf = outbuf;
531 tmpbuf += sizeof("From ")-1;
533 fputs("=46rom ", outfp);
534 fputs(tmpbuf, outfp);
536 fputs(outbuf, outfp);
541 while (fgets(buf, sizeof(buf), infp) != NULL) {
550 if (mimeinfo->content == MIMECONTENT_FILE) {
551 if (mimeinfo->tmp && (mimeinfo->data.filename != NULL))
552 g_unlink(mimeinfo->data.filename);
553 g_free(mimeinfo->data.filename);
554 } else if (mimeinfo->content == MIMECONTENT_MEM) {
555 if (mimeinfo->tmp && (mimeinfo->data.mem != NULL))
556 g_free(mimeinfo->data.mem);
559 stat(tmpfilename, &statbuf);
560 mimeinfo->content = MIMECONTENT_FILE;
561 mimeinfo->data.filename = tmpfilename;
562 mimeinfo->tmp = TRUE;
563 mimeinfo->offset = 0;
564 mimeinfo->length = statbuf.st_size;
565 mimeinfo->encoding_type = encoding;
570 gint procmime_get_part(const gchar *outfile, MimeInfo *mimeinfo)
574 gint restlength, readlength;
575 gint saved_errno = 0;
577 g_return_val_if_fail(outfile != NULL, -1);
578 g_return_val_if_fail(mimeinfo != NULL, -1);
580 if (mimeinfo->encoding_type != ENC_BINARY && !procmime_decode_content(mimeinfo))
583 if ((infp = g_fopen(mimeinfo->data.filename, "rb")) == NULL) {
585 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
586 return -(saved_errno);
588 if (fseek(infp, mimeinfo->offset, SEEK_SET) < 0) {
590 FILE_OP_ERROR(mimeinfo->data.filename, "fseek");
592 return -(saved_errno);
594 if ((outfp = g_fopen(outfile, "wb")) == NULL) {
596 FILE_OP_ERROR(outfile, "fopen");
598 return -(saved_errno);
601 restlength = mimeinfo->length;
603 while ((restlength > 0) && ((readlength = fread(buf, 1, restlength > BUFFSIZE ? BUFFSIZE : restlength, infp)) > 0)) {
604 if (fwrite(buf, 1, readlength, outfp) != readlength) {
607 return -(saved_errno);
609 restlength -= readlength;
613 if (fclose(outfp) == EOF) {
615 FILE_OP_ERROR(outfile, "fclose");
617 return -(saved_errno);
623 struct ContentRenderer {
628 static GList * renderer_list = NULL;
630 static struct ContentRenderer *
631 content_renderer_new(char * content_type, char * renderer)
633 struct ContentRenderer * cr;
635 cr = g_new(struct ContentRenderer, 1);
639 cr->content_type = g_strdup(content_type);
640 cr->renderer = g_strdup(renderer);
645 static void content_renderer_free(struct ContentRenderer * cr)
647 g_free(cr->content_type);
648 g_free(cr->renderer);
652 void renderer_read_config(void)
658 g_list_foreach(renderer_list, (GFunc) content_renderer_free, NULL);
659 renderer_list = NULL;
661 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RENDERER_RC, NULL);
662 f = g_fopen(rcpath, "rb");
668 while (fgets(buf, BUFFSIZE, f)) {
670 struct ContentRenderer * cr;
673 p = strchr(buf, ' ');
678 cr = content_renderer_new(buf, p + 1);
682 renderer_list = g_list_append(renderer_list, cr);
688 void renderer_write_config(void)
694 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RENDERER_RC, NULL);
696 if ((pfile = prefs_write_open(rcpath)) == NULL) {
697 g_warning("failed to write configuration to file\n");
704 for (cur = renderer_list ; cur != NULL ; cur = cur->next) {
705 struct ContentRenderer * renderer;
706 renderer = cur->data;
707 fprintf(pfile->fp, "%s %s\n", renderer->content_type,
711 if (prefs_file_close(pfile) < 0) {
712 g_warning("failed to write configuration to file\n");
717 FILE *procmime_get_text_content(MimeInfo *mimeinfo)
720 const gchar *src_codeset;
721 gboolean conv_fail = FALSE;
724 struct ContentRenderer * renderer;
726 gchar *tmpfile, *content_type;
728 g_return_val_if_fail(mimeinfo != NULL, NULL);
730 if (!procmime_decode_content(mimeinfo))
733 tmpfile = procmime_get_tmp_file_name(mimeinfo);
737 if (procmime_get_part(tmpfile, mimeinfo) < 0) {
742 tmpfp = g_fopen(tmpfile, "rb");
748 if ((outfp = my_tmpfile()) == NULL) {
755 src_codeset = forced_charset
757 procmime_mimeinfo_get_parameter(mimeinfo, "charset");
761 content_type = procmime_get_content_type_str(mimeinfo->type,
763 for (cur = renderer_list ; cur != NULL ; cur = cur->next) {
764 struct ContentRenderer * cr;
767 if (g_ascii_strcasecmp(cr->content_type, content_type) == 0) {
772 g_free(content_type);
774 if (renderer != NULL) {
780 dup2(fileno(outfp), 1);
782 p = popen(renderer->renderer, "w");
787 fread(buf, sizeof(char), sizeof(buf),
789 fwrite(buf, sizeof(char), count, p);
794 /* CodeConverter seems to have no effect here */
795 } else if (mimeinfo->type == MIMETYPE_TEXT && !g_ascii_strcasecmp(mimeinfo->subtype, "html")) {
796 SC_HTMLParser *parser;
799 conv = conv_code_converter_new(src_codeset);
800 parser = sc_html_parser_new(tmpfp, conv);
801 while ((str = sc_html_parse(parser)) != NULL) {
804 sc_html_parser_destroy(parser);
805 conv_code_converter_destroy(conv);
806 } else if (mimeinfo->type == MIMETYPE_TEXT && !g_ascii_strcasecmp(mimeinfo->subtype, "enriched")) {
810 conv = conv_code_converter_new(src_codeset);
811 parser = ertf_parser_new(tmpfp, conv);
812 while ((str = ertf_parse(parser)) != NULL) {
815 ertf_parser_destroy(parser);
816 conv_code_converter_destroy(conv);
817 } else if (mimeinfo->type == MIMETYPE_TEXT) {
818 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
819 str = conv_codeset_strdup(buf, src_codeset, CS_UTF_8);
831 g_warning("procmime_get_text_content(): Code conversion failed.\n");
841 /* search the first text part of (multipart) MIME message,
842 decode, convert it and output to outfp. */
843 FILE *procmime_get_first_text_content(MsgInfo *msginfo)
846 MimeInfo *mimeinfo, *partinfo;
848 g_return_val_if_fail(msginfo != NULL, NULL);
850 mimeinfo = procmime_scan_message_short(msginfo);
851 if (!mimeinfo) return NULL;
854 while (partinfo && partinfo->type != MIMETYPE_TEXT) {
855 partinfo = procmime_mimeinfo_next(partinfo);
858 outfp = procmime_get_text_content(partinfo);
860 procmime_mimeinfo_free_all(mimeinfo);
866 static gboolean find_encrypted_func(GNode *node, gpointer data)
868 MimeInfo *mimeinfo = (MimeInfo *) node->data;
869 MimeInfo **encinfo = (MimeInfo **) data;
871 if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
879 static MimeInfo *find_encrypted_part(MimeInfo *rootinfo)
881 MimeInfo *encinfo = NULL;
883 g_node_traverse(rootinfo->node, G_IN_ORDER, G_TRAVERSE_ALL, -1,
884 find_encrypted_func, &encinfo);
889 /* search the first encrypted text part of (multipart) MIME message,
890 decode, convert it and output to outfp. */
891 FILE *procmime_get_first_encrypted_text_content(MsgInfo *msginfo)
894 MimeInfo *mimeinfo, *partinfo, *encinfo;
896 g_return_val_if_fail(msginfo != NULL, NULL);
898 mimeinfo = procmime_scan_message(msginfo);
904 if ((encinfo = find_encrypted_part(partinfo)) != NULL) {
905 debug_print("decrypting message part\n");
906 if (privacy_mimeinfo_decrypt(encinfo) < 0) {
907 alertpanel_error(_("Couldn't decrypt: %s"),
908 privacy_get_error());
913 while (partinfo && partinfo->type != MIMETYPE_TEXT) {
914 partinfo = procmime_mimeinfo_next(partinfo);
918 outfp = procmime_get_text_content(partinfo);
920 procmime_mimeinfo_free_all(mimeinfo);
925 gboolean procmime_msginfo_is_encrypted(MsgInfo *msginfo)
927 MimeInfo *mimeinfo, *partinfo;
928 gboolean result = FALSE;
930 g_return_val_if_fail(msginfo != NULL, FALSE);
932 mimeinfo = procmime_scan_message(msginfo);
938 result = (find_encrypted_part(partinfo) != NULL);
939 procmime_mimeinfo_free_all(mimeinfo);
944 gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename,
945 const gchar *str, StrFindFunc find_func)
950 g_return_val_if_fail(mimeinfo != NULL, FALSE);
951 g_return_val_if_fail(mimeinfo->type == MIMETYPE_TEXT, FALSE);
952 g_return_val_if_fail(str != NULL, FALSE);
953 g_return_val_if_fail(find_func != NULL, FALSE);
955 outfp = procmime_get_text_content(mimeinfo);
960 while (fgets(buf, sizeof(buf), outfp) != NULL) {
962 if (find_func(buf, str)) {
973 gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str,
974 StrFindFunc find_func)
979 gboolean found = FALSE;
981 g_return_val_if_fail(msginfo != NULL, FALSE);
982 g_return_val_if_fail(str != NULL, FALSE);
983 g_return_val_if_fail(find_func != NULL, FALSE);
985 filename = procmsg_get_message_file(msginfo);
986 if (!filename) return FALSE;
987 mimeinfo = procmime_scan_message(msginfo);
989 for (partinfo = mimeinfo; partinfo != NULL;
990 partinfo = procmime_mimeinfo_next(partinfo)) {
991 if (partinfo->type == MIMETYPE_TEXT) {
992 if (procmime_find_string_part
993 (partinfo, filename, str, find_func) == TRUE) {
1000 procmime_mimeinfo_free_all(mimeinfo);
1006 gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
1008 static guint32 id = 0;
1013 g_return_val_if_fail(mimeinfo != NULL, NULL);
1015 g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
1017 if ((mimeinfo->type == MIMETYPE_TEXT) && !g_ascii_strcasecmp(mimeinfo->subtype, "html"))
1018 base = g_strdup("mimetmp.html");
1020 const gchar *basetmp;
1022 basetmp = procmime_mimeinfo_get_parameter(mimeinfo, "filename");
1023 if (basetmp == NULL)
1024 basetmp = procmime_mimeinfo_get_parameter(mimeinfo, "name");
1025 if (basetmp == NULL)
1026 basetmp = "mimetmp";
1027 basetmp = g_path_get_basename(basetmp);
1028 if (*basetmp == '\0')
1029 basetmp = g_strdup("mimetmp");
1030 base = conv_filename_from_utf8(basetmp);
1031 g_free((gchar*)basetmp);
1032 subst_for_shellsafe_filename(base);
1035 filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1036 f_prefix, base, NULL);
1043 static GList *mime_type_list = NULL;
1045 gchar *procmime_get_mime_type(const gchar *filename)
1047 static GHashTable *mime_type_table = NULL;
1048 MimeType *mime_type;
1053 if (!mime_type_table) {
1054 mime_type_table = procmime_get_mime_type_table();
1055 if (!mime_type_table) return NULL;
1058 base = g_path_get_basename(filename);
1059 if ((p = strrchr(base, '.')) != NULL)
1060 Xstrdup_a(ext, p + 1, p = NULL );
1062 if (!p) return NULL;
1065 mime_type = g_hash_table_lookup(mime_type_table, ext);
1069 str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
1077 static guint procmime_str_hash(gconstpointer gptr)
1079 guint hash_result = 0;
1082 for (str = gptr; str && *str; str++) {
1083 if (isupper(*str)) hash_result += (*str + ' ');
1084 else hash_result += *str;
1090 static gint procmime_str_equal(gconstpointer gptr1, gconstpointer gptr2)
1092 const char *str1 = gptr1;
1093 const char *str2 = gptr2;
1095 return !g_utf8_collate(str1, str2);
1098 static GHashTable *procmime_get_mime_type_table(void)
1100 GHashTable *table = NULL;
1102 MimeType *mime_type;
1105 if (!mime_type_list) {
1106 mime_type_list = procmime_get_mime_type_list();
1107 if (!mime_type_list) return NULL;
1110 table = g_hash_table_new(procmime_str_hash, procmime_str_equal);
1112 for (cur = mime_type_list; cur != NULL; cur = cur->next) {
1116 mime_type = (MimeType *)cur->data;
1118 if (!mime_type->extension) continue;
1120 exts = g_strsplit(mime_type->extension, " ", 16);
1121 for (i = 0; exts[i] != NULL; i++) {
1122 /* make the key case insensitive */
1124 /* use previously dup'd key on overwriting */
1125 if (g_hash_table_lookup(table, exts[i]))
1128 key = g_strdup(exts[i]);
1129 g_hash_table_insert(table, key, mime_type);
1137 GList *procmime_get_mime_type_list(void)
1141 gchar buf[BUFFSIZE];
1144 MimeType *mime_type;
1145 gboolean fp_is_glob_file = TRUE;
1148 return mime_type_list;
1150 #if defined(__NetBSD__)
1151 if ((fp = g_fopen(DATAROOTDIR "/mime/globs", "rb")) == NULL)
1153 if ((fp = g_fopen("/usr/share/mime/globs", "rb")) == NULL)
1156 fp_is_glob_file = FALSE;
1157 if ((fp = g_fopen("/etc/mime.types", "rb")) == NULL) {
1158 if ((fp = g_fopen(SYSCONFDIR "/mime.types", "rb"))
1160 FILE_OP_ERROR(SYSCONFDIR "/mime.types",
1167 while (fgets(buf, sizeof(buf), fp) != NULL) {
1168 p = strchr(buf, '#');
1174 if (fp_is_glob_file) {
1175 while (*p && !g_ascii_isspace(*p) && (*p!=':')) p++;
1177 while (*p && !g_ascii_isspace(*p)) p++;
1184 delim = strchr(buf, '/');
1185 if (delim == NULL) continue;
1188 mime_type = g_new(MimeType, 1);
1189 mime_type->type = g_strdup(buf);
1190 mime_type->sub_type = g_strdup(delim + 1);
1192 if (fp_is_glob_file) {
1193 while (*p && (g_ascii_isspace(*p)||(*p=='*')||(*p=='.'))) p++;
1195 while (*p && g_ascii_isspace(*p)) p++;
1199 mime_type->extension = g_strdup(p);
1201 mime_type->extension = NULL;
1203 list = g_list_append(list, mime_type);
1209 g_warning("Can't read mime.types\n");
1214 EncodingType procmime_get_encoding_for_charset(const gchar *charset)
1218 else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
1219 !g_ascii_strcasecmp(charset, "US-ASCII"))
1221 else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
1222 !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
1223 !g_ascii_strcasecmp(charset, "Windows-1251"))
1225 else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
1226 return ENC_QUOTED_PRINTABLE;
1227 else if (!g_ascii_strncasecmp(charset, "UTF-8", 5))
1228 return ENC_QUOTED_PRINTABLE;
1233 EncodingType procmime_get_encoding_for_text_file(const gchar *file, gboolean *has_binary)
1236 guchar buf[BUFFSIZE];
1238 size_t octet_chars = 0;
1239 size_t total_len = 0;
1240 gfloat octet_percentage;
1241 gboolean force_b64 = FALSE;
1243 if ((fp = g_fopen(file, "rb")) == NULL) {
1244 FILE_OP_ERROR(file, "fopen");
1248 while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
1252 for (p = buf, i = 0; i < len; ++p, ++i) {
1266 octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
1268 octet_percentage = 0.0;
1270 debug_print("procmime_get_encoding_for_text_file(): "
1271 "8bit chars: %zd / %zd (%f%%)\n", octet_chars, total_len,
1272 100.0 * octet_percentage);
1274 if (octet_percentage > 0.20 || force_b64) {
1275 debug_print("using BASE64\n");
1277 } else if (octet_chars > 0) {
1278 debug_print("using quoted-printable\n");
1279 return ENC_QUOTED_PRINTABLE;
1281 debug_print("using 7bit\n");
1286 struct EncodingTable
1289 EncodingType enc_type;
1292 struct EncodingTable encoding_table[] = {
1295 {"binary", ENC_BINARY},
1296 {"quoted-printable", ENC_QUOTED_PRINTABLE},
1297 {"base64", ENC_BASE64},
1298 {"x-uuencode", ENC_UNKNOWN},
1299 {NULL, ENC_UNKNOWN},
1302 const gchar *procmime_get_encoding_str(EncodingType encoding)
1304 struct EncodingTable *enc_table;
1306 for (enc_table = encoding_table; enc_table->str != NULL; enc_table++) {
1307 if (enc_table->enc_type == encoding)
1308 return enc_table->str;
1313 /* --- NEW MIME STUFF --- */
1320 static struct TypeTable mime_type_table[] = {
1321 {"text", MIMETYPE_TEXT},
1322 {"image", MIMETYPE_IMAGE},
1323 {"audio", MIMETYPE_AUDIO},
1324 {"video", MIMETYPE_VIDEO},
1325 {"application", MIMETYPE_APPLICATION},
1326 {"message", MIMETYPE_MESSAGE},
1327 {"multipart", MIMETYPE_MULTIPART},
1331 const gchar *procmime_get_media_type_str(MimeMediaType type)
1333 struct TypeTable *type_table;
1335 for (type_table = mime_type_table; type_table->str != NULL; type_table++) {
1336 if (type_table->type == type)
1337 return type_table->str;
1342 MimeMediaType procmime_get_media_type(const gchar *str)
1344 struct TypeTable *typetablearray;
1346 for (typetablearray = mime_type_table; typetablearray->str != NULL; typetablearray++)
1347 if (g_ascii_strncasecmp(str, typetablearray->str, strlen(typetablearray->str)) == 0)
1348 return typetablearray->type;
1350 return MIMETYPE_UNKNOWN;
1354 *\brief Safe wrapper for content type string.
1356 *\return const gchar * Pointer to content type string.
1358 gchar *procmime_get_content_type_str(MimeMediaType type,
1359 const char *subtype)
1361 const gchar *type_str = NULL;
1363 if (subtype == NULL || !(type_str = procmime_get_media_type_str(type)))
1364 return g_strdup("unknown");
1365 return g_strdup_printf("%s/%s", type_str, subtype);
1368 static int procmime_parse_mimepart(MimeInfo *parent,
1369 gchar *content_type,
1370 gchar *content_encoding,
1371 gchar *content_description,
1373 gchar *content_disposition,
1374 gchar *content_location,
1375 const gchar *filename,
1378 gboolean short_scan);
1380 static void procmime_parse_message_rfc822(MimeInfo *mimeinfo, gboolean short_scan)
1382 HeaderEntry hentry[] = {{"Content-Type:", NULL, TRUE},
1383 {"Content-Transfer-Encoding:",
1385 {"Content-Description:",
1389 {"Content-Disposition:",
1391 {"Content-Location:",
1395 {NULL, NULL, FALSE}};
1396 guint content_start, i;
1401 procmime_decode_content(mimeinfo);
1403 fp = g_fopen(mimeinfo->data.filename, "rb");
1405 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
1408 fseek(fp, mimeinfo->offset, SEEK_SET);
1409 procheader_get_header_fields(fp, hentry);
1410 if (hentry[0].body != NULL) {
1411 tmp = conv_unmime_header(hentry[0].body, NULL);
1412 g_free(hentry[0].body);
1413 hentry[0].body = tmp;
1415 if (hentry[2].body != NULL) {
1416 tmp = conv_unmime_header(hentry[2].body, NULL);
1417 g_free(hentry[2].body);
1418 hentry[2].body = tmp;
1420 if (hentry[4].body != NULL) {
1421 tmp = conv_unmime_header(hentry[4].body, NULL);
1422 g_free(hentry[4].body);
1423 hentry[4].body = tmp;
1425 if (hentry[5].body != NULL) {
1426 tmp = conv_unmime_header(hentry[5].body, NULL);
1427 g_free(hentry[5].body);
1428 hentry[5].body = tmp;
1430 content_start = ftell(fp);
1433 len = mimeinfo->length - (content_start - mimeinfo->offset);
1436 procmime_parse_mimepart(mimeinfo,
1437 hentry[0].body, hentry[1].body,
1438 hentry[2].body, hentry[3].body,
1439 hentry[4].body, hentry[5].body,
1440 mimeinfo->data.filename, content_start,
1443 for (i = 0; i < (sizeof hentry / sizeof hentry[0]); i++) {
1444 g_free(hentry[i].body);
1445 hentry[i].body = NULL;
1449 static void procmime_parse_multipart(MimeInfo *mimeinfo, gboolean short_scan)
1451 HeaderEntry hentry[] = {{"Content-Type:", NULL, TRUE},
1452 {"Content-Transfer-Encoding:",
1454 {"Content-Description:",
1458 {"Content-Disposition:",
1460 {"Content-Location:",
1462 {NULL, NULL, FALSE}};
1465 gint boundary_len = 0, lastoffset = -1, i;
1466 gchar buf[BUFFSIZE];
1469 gboolean done = FALSE;
1471 boundary = g_hash_table_lookup(mimeinfo->typeparameters, "boundary");
1474 boundary_len = strlen(boundary);
1476 procmime_decode_content(mimeinfo);
1478 fp = g_fopen(mimeinfo->data.filename, "rb");
1480 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
1483 fseek(fp, mimeinfo->offset, SEEK_SET);
1484 while ((p = fgets(buf, sizeof(buf), fp)) != NULL && result == 0) {
1485 if (ftell(fp) - 1 > (mimeinfo->offset + mimeinfo->length))
1488 if (IS_BOUNDARY(buf, boundary, boundary_len)) {
1489 if (lastoffset != -1) {
1490 gint len = (ftell(fp) - strlen(buf)) - lastoffset - 1;
1493 result = procmime_parse_mimepart(mimeinfo,
1494 hentry[0].body, hentry[1].body,
1495 hentry[2].body, hentry[3].body,
1496 hentry[4].body, hentry[5].body,
1497 mimeinfo->data.filename, lastoffset,
1499 if (result == 1 && short_scan) {
1505 if (buf[2 + boundary_len] == '-' &&
1506 buf[2 + boundary_len + 1] == '-')
1509 for (i = 0; i < (sizeof hentry / sizeof hentry[0]) ; i++) {
1510 g_free(hentry[i].body);
1511 hentry[i].body = NULL;
1513 procheader_get_header_fields(fp, hentry);
1514 if (hentry[0].body != NULL) {
1515 tmp = conv_unmime_header(hentry[0].body, NULL);
1516 g_free(hentry[0].body);
1517 hentry[0].body = tmp;
1519 if (hentry[2].body != NULL) {
1520 tmp = conv_unmime_header(hentry[2].body, NULL);
1521 g_free(hentry[2].body);
1522 hentry[2].body = tmp;
1524 if (hentry[4].body != NULL) {
1525 tmp = conv_unmime_header(hentry[4].body, NULL);
1526 g_free(hentry[4].body);
1527 hentry[4].body = tmp;
1529 if (hentry[5].body != NULL) {
1530 tmp = conv_unmime_header(hentry[5].body, NULL);
1531 g_free(hentry[5].body);
1532 hentry[5].body = tmp;
1534 lastoffset = ftell(fp);
1537 for (i = 0; i < (sizeof hentry / sizeof hentry[0]); i++) {
1538 g_free(hentry[i].body);
1539 hentry[i].body = NULL;
1544 static void parse_parameters(const gchar *parameters, GHashTable *table)
1546 gchar *params, *param, *next;
1547 GSList *convlist = NULL, *concatlist = NULL, *cur;
1549 params = g_strdup(parameters);
1552 for (; next != NULL; param = next) {
1553 gchar *attribute, *value, *tmp;
1555 gboolean convert = FALSE;
1557 next = strchr_with_skip_quote(param, '"', ';');
1566 value = strchr(attribute, '=');
1572 while (value[0] == ' ')
1575 g_strdown(attribute);
1577 len = strlen(attribute);
1578 if (attribute[len - 1] == '*') {
1579 gchar *srcpos, *dstpos, *endpos;
1582 attribute[len - 1] = '\0';
1586 endpos = value + strlen(value);
1587 while (srcpos < endpos) {
1593 if (!get_hex_value(&dstvalue, srcpos[1], srcpos[2]))
1604 if (value[0] == '"')
1605 extract_quote(value, '"');
1606 else if ((tmp = strchr(value, ' ')) != NULL)
1611 while (attribute[0] == ' ')
1613 while (attribute[strlen(attribute)-1] == ' ')
1614 attribute[strlen(attribute)-1] = '\0';
1617 while (value[0] == ' ')
1619 while (value[strlen(value)-1] == ' ')
1620 value[strlen(value)-1] = '\0';
1622 if (strrchr(attribute, '*') != NULL) {
1625 tmpattr = g_strdup(attribute);
1626 tmp = strrchr(tmpattr, '*');
1629 if ((tmp[1] == '0') && (tmp[2] == '\0') &&
1630 (g_slist_find_custom(concatlist, attribute, g_str_equal) == NULL))
1631 concatlist = g_slist_prepend(concatlist, g_strdup(tmpattr));
1633 if (convert && (g_slist_find_custom(convlist, attribute, g_str_equal) == NULL))
1634 convlist = g_slist_prepend(convlist, g_strdup(tmpattr));
1637 } else if (convert) {
1638 if (g_slist_find_custom(convlist, attribute, g_str_equal) == NULL)
1639 convlist = g_slist_prepend(convlist, g_strdup(attribute));
1642 if (g_hash_table_lookup(table, attribute) == NULL)
1643 g_hash_table_insert(table, g_strdup(attribute), g_strdup(value));
1646 for (cur = concatlist; cur != NULL; cur = g_slist_next(cur)) {
1647 gchar *attribute, *attrwnum, *partvalue;
1651 attribute = (gchar *) cur->data;
1652 value = g_string_sized_new(64);
1654 attrwnum = g_strdup_printf("%s*%d", attribute, n);
1655 while ((partvalue = g_hash_table_lookup(table, attrwnum)) != NULL) {
1656 g_string_append(value, partvalue);
1660 attrwnum = g_strdup_printf("%s*%d", attribute, n);
1664 g_hash_table_insert(table, g_strdup(attribute), g_strdup(value->str));
1665 g_string_free(value, TRUE);
1667 slist_free_strings(concatlist);
1668 g_slist_free(concatlist);
1670 for (cur = convlist; cur != NULL; cur = g_slist_next(cur)) {
1671 gchar *attribute, *key, *value;
1672 gchar *charset, *lang, *oldvalue, *newvalue;
1674 attribute = (gchar *) cur->data;
1675 if (!g_hash_table_lookup_extended(
1676 table, attribute, (gpointer *)(gchar *) &key, (gpointer *)(gchar *) &value))
1680 lang = strchr(charset, '\'');
1685 oldvalue = strchr(lang, '\'');
1686 if (oldvalue == NULL)
1691 newvalue = conv_codeset_strdup(oldvalue, charset, CS_UTF_8);
1693 g_hash_table_remove(table, attribute);
1697 g_hash_table_insert(table, g_strdup(attribute), newvalue);
1699 slist_free_strings(convlist);
1700 g_slist_free(convlist);
1705 static void procmime_parse_content_type(const gchar *content_type, MimeInfo *mimeinfo)
1707 g_return_if_fail(content_type != NULL);
1708 g_return_if_fail(mimeinfo != NULL);
1710 /* RFC 2045, page 13 says that the mime subtype is MANDATORY;
1711 * if it's not available we use the default Content-Type */
1712 if ((content_type[0] == '\0') || (strchr(content_type, '/') == NULL)) {
1713 mimeinfo->type = MIMETYPE_TEXT;
1714 mimeinfo->subtype = g_strdup("plain");
1715 if (g_hash_table_lookup(mimeinfo->typeparameters,
1716 "charset") == NULL) {
1717 g_hash_table_insert(mimeinfo->typeparameters,
1718 g_strdup("charset"),
1720 conv_get_locale_charset_str_no_utf8()));
1723 gchar *type, *subtype, *params;
1725 type = g_strdup(content_type);
1726 subtype = strchr(type, '/') + 1;
1727 *(subtype - 1) = '\0';
1728 if ((params = strchr(subtype, ';')) != NULL) {
1733 mimeinfo->type = procmime_get_media_type(type);
1734 mimeinfo->subtype = g_strdup(subtype);
1736 /* Get mimeinfo->typeparameters */
1738 parse_parameters(params, mimeinfo->typeparameters);
1744 static void procmime_parse_content_disposition(const gchar *content_disposition, MimeInfo *mimeinfo)
1746 gchar *tmp, *params;
1748 g_return_if_fail(content_disposition != NULL);
1749 g_return_if_fail(mimeinfo != NULL);
1751 tmp = g_strdup(content_disposition);
1752 if ((params = strchr(tmp, ';')) != NULL) {
1758 if (!g_ascii_strcasecmp(tmp, "inline"))
1759 mimeinfo->disposition = DISPOSITIONTYPE_INLINE;
1760 else if (!g_ascii_strcasecmp(tmp, "attachment"))
1761 mimeinfo->disposition = DISPOSITIONTYPE_ATTACHMENT;
1763 mimeinfo->disposition = DISPOSITIONTYPE_ATTACHMENT;
1766 parse_parameters(params, mimeinfo->dispositionparameters);
1772 static void procmime_parse_content_encoding(const gchar *content_encoding, MimeInfo *mimeinfo)
1774 struct EncodingTable *enc_table;
1776 for (enc_table = encoding_table; enc_table->str != NULL; enc_table++) {
1777 if (g_ascii_strcasecmp(enc_table->str, content_encoding) == 0) {
1778 mimeinfo->encoding_type = enc_table->enc_type;
1782 mimeinfo->encoding_type = ENC_UNKNOWN;
1786 static GSList *registered_parsers = NULL;
1788 static MimeParser *procmime_get_mimeparser_for_type(MimeMediaType type, const gchar *sub_type)
1791 for (cur = registered_parsers; cur; cur = cur->next) {
1792 MimeParser *parser = (MimeParser *)cur->data;
1793 if (parser->type == type && !strcmp2(parser->sub_type, sub_type))
1799 void procmime_mimeparser_register(MimeParser *parser)
1801 if (!procmime_get_mimeparser_for_type(parser->type, parser->sub_type))
1802 registered_parsers = g_slist_append(registered_parsers, parser);
1806 void procmime_mimeparser_unregister(MimeParser *parser)
1808 registered_parsers = g_slist_remove(registered_parsers, parser);
1811 static gboolean procmime_mimeparser_parse(MimeParser *parser, MimeInfo *mimeinfo)
1813 g_return_val_if_fail(parser->parse != NULL, FALSE);
1814 return parser->parse(parser, mimeinfo);
1817 static int procmime_parse_mimepart(MimeInfo *parent,
1818 gchar *content_type,
1819 gchar *content_encoding,
1820 gchar *content_description,
1822 gchar *content_disposition,
1823 gchar *content_location,
1824 const gchar *filename,
1827 gboolean short_scan)
1830 MimeParser *parser = NULL;
1831 gboolean parsed = FALSE;
1834 /* Create MimeInfo */
1835 mimeinfo = procmime_mimeinfo_new();
1836 mimeinfo->content = MIMECONTENT_FILE;
1837 if (parent != NULL) {
1838 if (g_node_depth(parent->node) > 32) {
1839 /* 32 is an arbitrary value
1840 * this avoids DOSsing ourselves
1841 * with enormous messages
1843 procmime_mimeinfo_free_all(mimeinfo);
1846 g_node_append(parent->node, mimeinfo->node);
1848 mimeinfo->data.filename = g_strdup(filename);
1849 mimeinfo->offset = offset;
1850 mimeinfo->length = length;
1852 if (content_type != NULL) {
1853 procmime_parse_content_type(content_type, mimeinfo);
1855 mimeinfo->type = MIMETYPE_TEXT;
1856 mimeinfo->subtype = g_strdup("plain");
1857 if (g_hash_table_lookup(mimeinfo->typeparameters,
1858 "charset") == NULL) {
1859 g_hash_table_insert(mimeinfo->typeparameters,
1860 g_strdup("charset"),
1862 conv_get_locale_charset_str_no_utf8()));
1866 if (content_encoding != NULL) {
1867 procmime_parse_content_encoding(content_encoding, mimeinfo);
1869 mimeinfo->encoding_type = ENC_UNKNOWN;
1872 if (content_description != NULL)
1873 mimeinfo->description = g_strdup(content_description);
1875 mimeinfo->description = NULL;
1877 if (content_id != NULL)
1878 mimeinfo->id = g_strdup(content_id);
1880 mimeinfo->id = NULL;
1882 if (content_location != NULL)
1883 mimeinfo->location = g_strdup(content_location);
1885 mimeinfo->location = NULL;
1887 if (content_disposition != NULL)
1888 procmime_parse_content_disposition(content_disposition, mimeinfo);
1890 mimeinfo->disposition = DISPOSITIONTYPE_UNKNOWN;
1892 /* Call parser for mime type */
1893 if ((parser = procmime_get_mimeparser_for_type(mimeinfo->type, mimeinfo->subtype)) != NULL) {
1894 parsed = procmime_mimeparser_parse(parser, mimeinfo);
1897 switch (mimeinfo->type) {
1899 if (g_ascii_strcasecmp(mimeinfo->subtype, "plain") == 0 && short_scan) {
1904 case MIMETYPE_MESSAGE:
1905 if (g_ascii_strcasecmp(mimeinfo->subtype, "rfc822") == 0) {
1906 procmime_parse_message_rfc822(mimeinfo, short_scan);
1910 case MIMETYPE_MULTIPART:
1911 procmime_parse_multipart(mimeinfo, short_scan);
1922 static gchar *typenames[] = {
1933 static gboolean output_func(GNode *node, gpointer data)
1936 MimeInfo *mimeinfo = (MimeInfo *) node->data;
1938 depth = g_node_depth(node);
1939 for (i = 0; i < depth; i++)
1941 g_print("%s/%s (offset:%d length:%d encoding: %d)\n", typenames[mimeinfo->type], mimeinfo->subtype, mimeinfo->offset, mimeinfo->length, mimeinfo->encoding_type);
1946 static void output_mime_structure(MimeInfo *mimeinfo, int indent)
1948 g_node_traverse(mimeinfo->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, output_func, NULL);
1951 static MimeInfo *procmime_scan_file_with_offset(const gchar *filename, int offset, gboolean short_scan)
1956 stat(filename, &buf);
1958 mimeinfo = procmime_mimeinfo_new();
1959 mimeinfo->content = MIMECONTENT_FILE;
1960 mimeinfo->encoding_type = ENC_UNKNOWN;
1961 mimeinfo->type = MIMETYPE_MESSAGE;
1962 mimeinfo->subtype = g_strdup("rfc822");
1963 mimeinfo->data.filename = g_strdup(filename);
1964 mimeinfo->offset = offset;
1965 mimeinfo->length = buf.st_size - offset;
1967 procmime_parse_message_rfc822(mimeinfo, short_scan);
1968 if (debug_get_mode())
1969 output_mime_structure(mimeinfo, 0);
1974 static MimeInfo *procmime_scan_file_full(const gchar *filename, gboolean short_scan)
1978 g_return_val_if_fail(filename != NULL, NULL);
1980 mimeinfo = procmime_scan_file_with_offset(filename, 0, short_scan);
1985 MimeInfo *procmime_scan_file(const gchar *filename)
1987 return procmime_scan_file_full(filename, FALSE);
1990 static MimeInfo *procmime_scan_file_short(const gchar *filename)
1992 return procmime_scan_file_full(filename, TRUE);
1995 static MimeInfo *procmime_scan_queue_file_full(const gchar *filename, gboolean short_scan)
1999 gchar buf[BUFFSIZE];
2002 g_return_val_if_fail(filename != NULL, NULL);
2005 if ((fp = g_fopen(filename, "rb")) == NULL)
2007 /* Skip queue header */
2008 while (fgets(buf, sizeof(buf), fp) != NULL) {
2010 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
2011 strlen("X-Claws-End-Special-Headers:"))) ||
2012 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
2013 strlen("X-Sylpheed-End-Special-Headers:"))))
2016 if (buf[0] == '\r' || buf[0] == '\n') break;
2017 /* from other mailers */
2018 if (!strncmp(buf, "Date: ", 6)
2019 || !strncmp(buf, "To: ", 4)
2020 || !strncmp(buf, "From: ", 6)
2021 || !strncmp(buf, "Subject: ", 9)) {
2029 mimeinfo = procmime_scan_file_with_offset(filename, offset, short_scan);
2034 MimeInfo *procmime_scan_queue_file(const gchar *filename)
2036 return procmime_scan_queue_file_full(filename, FALSE);
2039 static MimeInfo *procmime_scan_queue_file_short(const gchar *filename)
2041 return procmime_scan_queue_file_full(filename, TRUE);
2046 ENC_AS_QUOTED_STRING,
2051 typedef struct _ParametersData {
2057 static void write_parameters(gpointer key, gpointer value, gpointer user_data)
2060 gchar *val = value, *valpos, *tmp;
2061 ParametersData *pdata = (ParametersData *)user_data;
2062 GString *buf = g_string_new("");
2064 EncodeAs encas = ENC_AS_TOKEN;
2066 for (valpos = val; *valpos != 0; valpos++) {
2067 if (!IS_ASCII(*valpos) || *valpos == '"') {
2068 encas = ENC_AS_EXTENDED;
2073 if (((*valpos >= 0) && (*valpos < 037)) || (*valpos == 0177)) {
2074 encas = ENC_AS_QUOTED_STRING;
2078 /* tspecials + SPACE */
2096 encas = ENC_AS_QUOTED_STRING;
2101 if (encas == ENC_AS_EXTENDED && pdata->ascii_only == TRUE)
2102 encas = ENC_TO_ASCII;
2106 g_string_append_printf(buf, "%s=%s", param, val);
2110 tmp = g_strdup(val);
2112 " ()<>@,';:\\/[]?=.0123456789"
2113 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2114 "abcdefghijklmnopqrstuvwxyz",
2116 g_string_append_printf(buf, "%s=\"%s\"", param, tmp);
2120 case ENC_AS_QUOTED_STRING:
2121 g_string_append_printf(buf, "%s=\"%s\"", param, val);
2124 case ENC_AS_EXTENDED:
2125 if (!g_utf8_validate(val, -1, NULL))
2126 g_string_append_printf(buf, "%s*=%s''", param,
2127 conv_get_locale_charset_str());
2129 g_string_append_printf(buf, "%s*=%s''", param,
2131 for (valpos = val; *valpos != '\0'; valpos++) {
2132 if (IS_ASCII(*valpos) && isalnum(*valpos)) {
2133 g_string_append_printf(buf, "%c", *valpos);
2135 gchar hexstr[3] = "XX";
2136 get_hex_str(hexstr, *valpos);
2137 g_string_append_printf(buf, "%%%s", hexstr);
2143 if (buf->str && strlen(buf->str)) {
2144 if (pdata->len + strlen(buf->str) + 2 > 76) {
2145 fprintf(pdata->fp, ";\n %s", buf->str);
2146 pdata->len = strlen(buf->str) + 1;
2148 fprintf(pdata->fp, "; %s", buf->str);
2149 pdata->len += strlen(buf->str) + 2;
2152 g_string_free(buf, TRUE);
2155 void procmime_write_mime_header(MimeInfo *mimeinfo, FILE *fp)
2157 struct TypeTable *type_table;
2158 ParametersData *pdata = g_new0(ParametersData, 1);
2159 debug_print("procmime_write_mime_header\n");
2162 pdata->ascii_only = FALSE;
2164 for (type_table = mime_type_table; type_table->str != NULL; type_table++)
2165 if (mimeinfo->type == type_table->type) {
2166 gchar *buf = g_strdup_printf(
2167 "Content-Type: %s/%s", type_table->str, mimeinfo->subtype);
2168 fprintf(fp, "%s", buf);
2169 pdata->len = strlen(buf);
2170 pdata->ascii_only = TRUE;
2174 g_hash_table_foreach(mimeinfo->typeparameters, write_parameters, pdata);
2179 if (mimeinfo->encoding_type != ENC_UNKNOWN)
2180 fprintf(fp, "Content-Transfer-Encoding: %s\n", procmime_get_encoding_str(mimeinfo->encoding_type));
2182 if (mimeinfo->description != NULL)
2183 fprintf(fp, "Content-Description: %s\n", mimeinfo->description);
2185 if (mimeinfo->id != NULL)
2186 fprintf(fp, "Content-ID: %s\n", mimeinfo->id);
2188 if (mimeinfo->location != NULL)
2189 fprintf(fp, "Content-Location: %s\n", mimeinfo->location);
2191 if (mimeinfo->disposition != DISPOSITIONTYPE_UNKNOWN) {
2192 ParametersData *pdata = g_new0(ParametersData, 1);
2194 if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE)
2195 buf = g_strdup("Content-Disposition: inline");
2196 else if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT)
2197 buf = g_strdup("Content-Disposition: attachment");
2199 buf = g_strdup("Content-Disposition: unknown");
2201 fprintf(fp, "%s", buf);
2202 pdata->len = strlen(buf);
2206 pdata->ascii_only = FALSE;
2208 g_hash_table_foreach(mimeinfo->dispositionparameters, write_parameters, pdata);
2216 static gint procmime_write_message_rfc822(MimeInfo *mimeinfo, FILE *fp)
2221 gchar buf[BUFFSIZE];
2222 gboolean skip = FALSE;;
2224 debug_print("procmime_write_message_rfc822\n");
2227 switch (mimeinfo->content) {
2228 case MIMECONTENT_FILE:
2229 if ((infp = g_fopen(mimeinfo->data.filename, "rb")) == NULL) {
2230 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
2233 fseek(infp, mimeinfo->offset, SEEK_SET);
2234 while (fgets(buf, sizeof(buf), infp) == buf) {
2236 if (buf[0] == '\n' && buf[1] == '\0')
2238 if (skip && (buf[0] == ' ' || buf[0] == '\t'))
2240 if (g_ascii_strncasecmp(buf, "Mime-Version:", 13) == 0 ||
2241 g_ascii_strncasecmp(buf, "Content-Type:", 13) == 0 ||
2242 g_ascii_strncasecmp(buf, "Content-Transfer-Encoding:", 26) == 0 ||
2243 g_ascii_strncasecmp(buf, "Content-Description:", 20) == 0 ||
2244 g_ascii_strncasecmp(buf, "Content-ID:", 11) == 0 ||
2245 g_ascii_strncasecmp(buf, "Content-Location:", 17) == 0 ||
2246 g_ascii_strncasecmp(buf, "Content-Disposition:", 20) == 0) {
2250 fwrite(buf, sizeof(gchar), strlen(buf), fp);
2256 case MIMECONTENT_MEM:
2257 fwrite(mimeinfo->data.mem,
2259 strlen(mimeinfo->data.mem),
2267 childnode = mimeinfo->node->children;
2268 if (childnode == NULL)
2271 child = (MimeInfo *) childnode->data;
2272 fprintf(fp, "Mime-Version: 1.0\n");
2273 procmime_write_mime_header(child, fp);
2274 return procmime_write_mimeinfo(child, fp);
2277 static gint procmime_write_multipart(MimeInfo *mimeinfo, FILE *fp)
2281 gchar *boundary, *str, *str2;
2282 gchar buf[BUFFSIZE];
2283 gboolean firstboundary;
2285 debug_print("procmime_write_multipart\n");
2287 boundary = g_hash_table_lookup(mimeinfo->typeparameters, "boundary");
2289 switch (mimeinfo->content) {
2290 case MIMECONTENT_FILE:
2291 if ((infp = g_fopen(mimeinfo->data.filename, "rb")) == NULL) {
2292 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
2295 fseek(infp, mimeinfo->offset, SEEK_SET);
2296 while (fgets(buf, sizeof(buf), infp) == buf) {
2297 if (IS_BOUNDARY(buf, boundary, strlen(boundary)))
2299 fwrite(buf, sizeof(gchar), strlen(buf), fp);
2304 case MIMECONTENT_MEM:
2305 str = g_strdup(mimeinfo->data.mem);
2306 if (((str2 = strstr(str, boundary)) != NULL) && ((str2 - str) >= 2) &&
2307 (*(str2 - 1) == '-') && (*(str2 - 2) == '-'))
2309 fwrite(str, sizeof(gchar), strlen(str), fp);
2317 childnode = mimeinfo->node->children;
2318 firstboundary = TRUE;
2319 while (childnode != NULL) {
2320 MimeInfo *child = childnode->data;
2323 firstboundary = FALSE;
2326 fprintf(fp, "--%s\n", boundary);
2328 procmime_write_mime_header(child, fp);
2329 if (procmime_write_mimeinfo(child, fp) < 0)
2332 childnode = g_node_next_sibling(childnode);
2334 fprintf(fp, "\n--%s--\n", boundary);
2339 gint procmime_write_mimeinfo(MimeInfo *mimeinfo, FILE *fp)
2343 debug_print("procmime_write_mimeinfo\n");
2345 if (G_NODE_IS_LEAF(mimeinfo->node)) {
2346 switch (mimeinfo->content) {
2347 case MIMECONTENT_FILE:
2348 if ((infp = g_fopen(mimeinfo->data.filename, "rb")) == NULL) {
2349 FILE_OP_ERROR(mimeinfo->data.filename, "fopen");
2352 copy_file_part_to_fp(infp, mimeinfo->offset, mimeinfo->length, fp);
2356 case MIMECONTENT_MEM:
2357 fwrite(mimeinfo->data.mem,
2359 strlen(mimeinfo->data.mem),
2367 /* Call writer for mime type */
2368 switch (mimeinfo->type) {
2369 case MIMETYPE_MESSAGE:
2370 if (g_ascii_strcasecmp(mimeinfo->subtype, "rfc822") == 0) {
2371 return procmime_write_message_rfc822(mimeinfo, fp);
2375 case MIMETYPE_MULTIPART:
2376 return procmime_write_multipart(mimeinfo, fp);
2388 gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
2392 if ((mimeinfo->type == MIMETYPE_TEXT) && !g_ascii_strcasecmp(mimeinfo->subtype, "html"))
2393 base = g_strdup("mimetmp.html");
2395 const gchar *basetmp;
2398 basetmp = procmime_mimeinfo_get_parameter(mimeinfo, "filename");
2399 if (basetmp == NULL)
2400 basetmp = procmime_mimeinfo_get_parameter(mimeinfo, "name");
2401 if (basetmp == NULL)
2402 basetmp = "mimetmp";
2403 basename = g_path_get_basename(basetmp);
2404 if (*basename == '\0') {
2406 basename = g_strdup("mimetmp");
2408 base = conv_filename_from_utf8(basename);
2410 subst_for_shellsafe_filename(base);