X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fprocmime.c;h=97585cc3c1128c678225241330a30a78f8be88bf;hp=f12ef1e43fb5eb1dd3923bbd19f0bb329a9cd2aa;hb=99a7382d8fc0b91c98475ec8c92c9a86975accc7;hpb=30533815791802a57bbeb2244240e528ecc6c38e;ds=sidebyside diff --git a/src/procmime.c b/src/procmime.c index f12ef1e43..97585cc3c 100644 --- a/src/procmime.c +++ b/src/procmime.c @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * Copyright (C) 1999-2003 Hiroyuki Yamamoto * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,8 +33,11 @@ #include "procmime.h" #include "procheader.h" #include "base64.h" +#include "quoted-printable.h" #include "uuencode.h" #include "unmime.h" +#include "html.h" +#include "enriched.h" #include "codeconv.h" #include "utils.h" #include "prefs_common.h" @@ -43,6 +46,8 @@ # include "rfc2015.h" #endif +#include "prefs_gtk.h" + static GHashTable *procmime_get_mime_type_table (void); MimeInfo *procmime_mimeinfo_new(void) @@ -56,28 +61,6 @@ MimeInfo *procmime_mimeinfo_new(void) return mimeinfo; } -void procmime_mimeinfo_free(MimeInfo *mimeinfo) -{ - if (!mimeinfo) return; - - g_free(mimeinfo->encoding); - g_free(mimeinfo->content_type); - g_free(mimeinfo->charset); - g_free(mimeinfo->name); - g_free(mimeinfo->boundary); - g_free(mimeinfo->content_disposition); - g_free(mimeinfo->filename); -#if USE_GPGME - g_free(mimeinfo->plaintextfile); - g_free(mimeinfo->sigstatus); - g_free(mimeinfo->sigstatus_full); -#endif - - procmime_mimeinfo_free(mimeinfo->sub); - - g_free(mimeinfo); -} - void procmime_mimeinfo_free_all(MimeInfo *mimeinfo) { while (mimeinfo != NULL) { @@ -132,14 +115,8 @@ void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new) MimeInfo *parent = old->parent; MimeInfo *child; - if (!parent) { - g_warning("oops: Not top message"); - return; - } - if (new->next) { - g_message("oops: new child should not have a sibling"); - return; - } + g_return_if_fail(parent != NULL); + g_return_if_fail(new->next == NULL); for (child = parent->children; child && child != old; child = child->next) @@ -170,6 +147,12 @@ MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo) if (mimeinfo->next) return mimeinfo->next; + if (mimeinfo->main) { + mimeinfo = mimeinfo->main; + if (mimeinfo->next) + return mimeinfo->next; + } + for (mimeinfo = mimeinfo->parent; mimeinfo != NULL; mimeinfo = mimeinfo->parent) { if (mimeinfo->next) @@ -251,6 +234,7 @@ void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp) gboolean eom = FALSE; prev_fpos = fpos; + debug_print("prev_fpos: %ld\n", fpos); partinfo = procmime_scan_mime_header(fp); if (!partinfo) break; @@ -264,14 +248,20 @@ void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp) partinfo->sub = sub = procmime_scan_mime_header(fp); if (!sub) break; + sub->level = partinfo->level + 1; - sub->parent = partinfo->parent; + sub->parent = partinfo; sub->main = partinfo; - if (sub->mime_type == MIME_MULTIPART) { - if (sub->level < 8) + if (sub->level < 8) { + if (sub->mime_type == MIME_MULTIPART) { procmime_scan_multipart_message (sub, fp); + } else if (sub->mime_type == MIME_MESSAGE_RFC822) { + fseek(fp, sub->fpos, SEEK_SET); + procmime_scan_multipart_message + (sub, fp); + } } } @@ -285,15 +275,26 @@ void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp) break; } } - if (p == NULL) - eom = TRUE; /* broken MIME message */ + if (p == NULL) { + /* broken MIME, or single part MIME message */ + buf[0] = '\0'; + eom = TRUE; + } fpos = ftell(fp); + debug_print("fpos: %ld\n", fpos); partinfo->size = fpos - prev_fpos - strlen(buf); + debug_print("partinfo->size: %d\n", partinfo->size); + if (partinfo->sub && !partinfo->sub->sub && + !partinfo->sub->children) { + partinfo->sub->size = fpos - partinfo->sub->fpos - strlen(buf); + debug_print("partinfo->sub->size: %d\n", + partinfo->sub->size); + } + debug_print("boundary: %s\n", buf); if (eom) break; } - /*g_message ("** at " __PRETTY_FUNCTION__ ":%d:", __LINE__);*/ } void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding) @@ -336,10 +337,10 @@ void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type) g_free(mimeinfo->content_type); g_free(mimeinfo->charset); - g_free(mimeinfo->name); + /* g_free(mimeinfo->name); */ mimeinfo->content_type = NULL; mimeinfo->charset = NULL; - mimeinfo->name = NULL; + /* mimeinfo->name = NULL; */ if ((delim = strchr(buf, ';'))) *delim = '\0'; mimeinfo->content_type = cnttype = g_strdup(g_strstrip(buf)); @@ -380,7 +381,11 @@ void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type) len = strlen(value) + 1; Xalloca(tmp, len, return); conv_unmime_header(tmp, len, value, NULL); - mimeinfo->name = g_strdup(tmp); + g_free(mimeinfo->name); + /*pgp signatures should NOT have a name */ + if (mimeinfo->content_type + && strcasecmp(mimeinfo->content_type, "application/pgp-signature")) + mimeinfo->name = g_strdup(tmp); } else if (!strcasecmp(attr, "boundary")) mimeinfo->boundary = g_strdup(value); } @@ -444,7 +449,10 @@ void procmime_scan_content_disposition(MimeInfo *mimeinfo, Xalloca(tmp, len, return); conv_unmime_header(tmp, len, value, NULL); g_free(mimeinfo->filename); - mimeinfo->filename = g_strdup(tmp); + /*pgp signatures should NOT have a name */ + if (mimeinfo->content_type + && strcasecmp(mimeinfo->content_type, "application/pgp-signature")) + mimeinfo->filename = g_strdup(tmp); break; } } @@ -454,11 +462,65 @@ void procmime_scan_content_disposition(MimeInfo *mimeinfo, } } +void procmime_scan_content_description(MimeInfo *mimeinfo, + const gchar *content_description) +{ + gchar *buf; + + gchar *tmp; + size_t blen; + + if (conv_get_current_charset() == C_EUC_JP && + strchr(content_description, '\033')) { + gint len; + len = strlen(content_description) * 2 + 1; + Xalloca(buf, len, return); + conv_jistoeuc(buf, len, content_description); + } else + Xstrdup_a(buf, content_description, return); + + blen = strlen(buf) + 1; + Xalloca(tmp, blen, return); + conv_unmime_header(tmp, blen, buf, NULL); + g_free(mimeinfo->name); + mimeinfo->name = NULL; + /*pgp signatures should NOT have a name */ + if (mimeinfo->content_type + && strcasecmp(mimeinfo->content_type, "application/pgp-signature")) + mimeinfo->name = g_strdup(tmp); +} + +void procmime_scan_subject(MimeInfo *mimeinfo, + const gchar *subject) +{ + gchar *buf; + + gchar *tmp; + size_t blen; + + if (conv_get_current_charset() == C_EUC_JP && + strchr(subject, '\033')) { + gint len; + len = strlen(subject) * 2 + 1; + Xalloca(buf, len, return); + conv_jistoeuc(buf, len, subject); + } else + Xstrdup_a(buf, subject, return); + + blen = strlen(buf) + 1; + Xalloca(tmp, blen, return); + conv_unmime_header(tmp, blen, buf, NULL); + g_free(mimeinfo->name); + mimeinfo->name = g_strdup(tmp); +} + enum { H_CONTENT_TRANSFER_ENCODING = 0, H_CONTENT_TYPE = 1, - H_CONTENT_DISPOSITION = 2 + H_CONTENT_DISPOSITION = 2, + H_CONTENT_DESCRIPTION = 3, + H_SUBJECT = 4 }; MimeInfo *procmime_scan_mime_header(FILE *fp) @@ -468,6 +530,10 @@ MimeInfo *procmime_scan_mime_header(FILE *fp) {"Content-Type:", NULL, TRUE}, {"Content-Disposition:", NULL, TRUE}, + {"Content-description:", + NULL, TRUE}, + {"Subject:", + NULL, TRUE}, {NULL, NULL, FALSE}}; gchar buf[BUFFSIZE]; gint hnum; @@ -494,6 +560,12 @@ MimeInfo *procmime_scan_mime_header(FILE *fp) } else if (H_CONTENT_DISPOSITION == hnum) { procmime_scan_content_disposition (mimeinfo, buf + strlen(hp->name)); + } else if (H_CONTENT_DESCRIPTION == hnum) { + procmime_scan_content_description + (mimeinfo, buf + strlen(hp->name)); + } else if (H_SUBJECT == hnum) { + procmime_scan_subject + (mimeinfo, buf + strlen(hp->name)); } } @@ -506,7 +578,7 @@ MimeInfo *procmime_scan_mime_header(FILE *fp) } if (!mimeinfo->content_type) - mimeinfo->content_type = g_strdup("text/plain"); + mimeinfo->content_type = g_strdup("text/plain"); return mimeinfo; } @@ -536,15 +608,12 @@ FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo) } if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) { - gboolean softline = FALSE; - while (fgets(buf, sizeof(buf), infp) != NULL && (!boundary || !IS_BOUNDARY(buf, boundary, boundary_len))) { - guchar *p = buf; - - softline = DoOneQPLine(&p, FALSE, softline); - fwrite(buf, p - (guchar *)buf, 1, outfp); + gint len; + len = qp_decode_line(buf); + fwrite(buf, len, 1, outfp); } } else if (mimeinfo->encoding_type == ENC_BASE64) { gchar outbuf[BUFFSIZE]; @@ -606,7 +675,7 @@ gint procmime_get_part(const gchar *outfile, const gchar *infile, g_return_val_if_fail(infile != NULL, -1); g_return_val_if_fail(mimeinfo != NULL, -1); - if ((infp = fopen(infile, "r")) == NULL) { + if ((infp = fopen(infile, "rb")) == NULL) { FILE_OP_ERROR(infile, "fopen"); return -1; } @@ -615,7 +684,7 @@ gint procmime_get_part(const gchar *outfile, const gchar *infile, fclose(infp); return -1; } - if ((outfp = fopen(outfile, "w")) == NULL) { + if ((outfp = fopen(outfile, "wb")) == NULL) { FILE_OP_ERROR(outfile, "fopen"); fclose(infp); return -1; @@ -636,17 +705,115 @@ gint procmime_get_part(const gchar *outfile, const gchar *infile, return 0; } +struct ContentRenderer { + char * content_type; + char * renderer; +}; + +static GList * renderer_list = NULL; + +static struct ContentRenderer * +content_renderer_new(char * content_type, char * renderer) +{ + struct ContentRenderer * cr; + + cr = g_new(struct ContentRenderer, 1); + if (cr == NULL) + return NULL; + + cr->content_type = g_strdup(content_type); + cr->renderer = g_strdup(renderer); + + return cr; +} + +static void content_renderer_free(struct ContentRenderer * cr) +{ + g_free(cr->content_type); + g_free(cr->renderer); + g_free(cr); +} + +void renderer_read_config(void) +{ + gchar buf[BUFFSIZE]; + FILE * f; + gchar * rcpath; + + g_list_foreach(renderer_list, (GFunc) content_renderer_free, NULL); + renderer_list = NULL; + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RENDERER_RC, NULL); + f = fopen(rcpath, "rb"); + g_free(rcpath); + + if (f == NULL) + return; + + while (fgets(buf, BUFFSIZE, f)) { + char * p; + struct ContentRenderer * cr; + + strretchomp(buf); + p = strchr(buf, ' '); + if (p == NULL) + continue; + * p = 0; + + cr = content_renderer_new(buf, p + 1); + if (cr == NULL) + continue; + + renderer_list = g_list_append(renderer_list, cr); + } + + fclose(f); +} + +void renderer_write_config(void) +{ + gchar * rcpath; + PrefFile *pfile; + GList * cur; + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RENDERER_RC, NULL); + + if ((pfile = prefs_write_open(rcpath)) == NULL) { + g_warning("failed to write configuration to file\n"); + g_free(rcpath); + return; + } + + g_free(rcpath); + + for(cur = renderer_list ; cur != NULL ; cur = cur->next) { + struct ContentRenderer * renderer; + renderer = cur->data; + fprintf(pfile->fp, "%s %s\n", renderer->content_type, + renderer->renderer); + } + + if (prefs_file_close(pfile) < 0) { + g_warning("failed to write configuration to file\n"); + return; + } +} + FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp) { FILE *tmpfp, *outfp; gchar *src_codeset; gboolean conv_fail = FALSE; gchar buf[BUFFSIZE]; + gchar *str; + struct ContentRenderer * renderer; + GList * cur; g_return_val_if_fail(mimeinfo != NULL, NULL); g_return_val_if_fail(infp != NULL, NULL); g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT || - mimeinfo->mime_type == MIME_TEXT_HTML, NULL); + mimeinfo->mime_type == MIME_TEXT_HTML || + mimeinfo->mime_type == MIME_TEXT_ENRICHED, NULL); if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) { perror("fseek"); @@ -669,20 +836,75 @@ FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp) src_codeset = prefs_common.force_charset ? prefs_common.force_charset : mimeinfo->charset; - while (fgets(buf, sizeof(buf), tmpfp) != NULL) { - gchar *str; + renderer = NULL; + + for(cur = renderer_list ; cur != NULL ; cur = cur->next) { + struct ContentRenderer * cr; + cr = cur->data; + if (g_strcasecmp(cr->content_type, + mimeinfo->content_type) == 0) { + renderer = cr; + break; + } + } + + if (renderer != NULL) { + FILE * p; + int oldout; + + oldout = dup(1); + + dup2(fileno(outfp), 1); + + p = popen(renderer->renderer, "w"); + if (p != NULL) { + size_t count; + + while ((count = + fread(buf, sizeof(char), sizeof(buf), + tmpfp)) > 0) + fwrite(buf, sizeof(char), count, p); + pclose(p); + } + + dup2(oldout, 1); + } else if (mimeinfo->mime_type == MIME_TEXT) { + while (fgets(buf, sizeof(buf), tmpfp) != NULL) { + str = conv_codeset_strdup(buf, src_codeset, NULL); + if (str) { + fputs(str, outfp); + g_free(str); + } else { + conv_fail = TRUE; + fputs(buf, outfp); + } + } + } else if (mimeinfo->mime_type == MIME_TEXT_HTML) { + HTMLParser *parser; + CodeConverter *conv; - str = conv_codeset_strdup(buf, src_codeset, NULL); - if (str) { + conv = conv_code_converter_new(src_codeset); + parser = html_parser_new(tmpfp, conv); + while ((str = html_parse(parser)) != NULL) { fputs(str, outfp); - g_free(str); - } else { - conv_fail = TRUE; - fputs(buf, outfp); } + html_parser_destroy(parser); + conv_code_converter_destroy(conv); + } else if (mimeinfo->mime_type == MIME_TEXT_ENRICHED) { + ERTFParser *parser; + CodeConverter *conv; + + conv = conv_code_converter_new(src_codeset); + parser = ertf_parser_new(tmpfp, conv); + while ((str = ertf_parse(parser)) != NULL) { + fputs(str, outfp); + } + ertf_parser_destroy(parser); + conv_code_converter_destroy(conv); } + if (conv_fail) - g_warning(_("procmime_get_text_content(): Code conversion failed.\n")); + g_warning("procmime_get_text_content(): Code conversion failed.\n"); fclose(tmpfp); rewind(outfp); @@ -710,6 +932,13 @@ FILE *procmime_get_first_text_content(MsgInfo *msginfo) partinfo = mimeinfo; while (partinfo && partinfo->mime_type != MIME_TEXT) partinfo = procmime_mimeinfo_next(partinfo); + if (!partinfo) { + partinfo = mimeinfo; + while (partinfo && partinfo->mime_type != MIME_TEXT_HTML && + partinfo->mime_type != MIME_TEXT_ENRICHED) + partinfo = procmime_mimeinfo_next(partinfo); + } + if (partinfo) outfp = procmime_get_text_content(partinfo, infp); @@ -730,10 +959,11 @@ gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename, g_return_val_if_fail(mimeinfo != NULL, FALSE); g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT || - mimeinfo->mime_type == MIME_TEXT_HTML, FALSE); + mimeinfo->mime_type == MIME_TEXT_HTML || + mimeinfo->mime_type == MIME_TEXT_ENRICHED, FALSE); g_return_val_if_fail(str != NULL, FALSE); - if ((infp = fopen(filename, "r")) == NULL) { + if ((infp = fopen(filename, "rb")) == NULL) { FILE_OP_ERROR(filename, "fopen"); return FALSE; } @@ -779,7 +1009,8 @@ gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str, for (partinfo = mimeinfo; partinfo != NULL; partinfo = procmime_mimeinfo_next(partinfo)) { if (partinfo->mime_type == MIME_TEXT || - partinfo->mime_type == MIME_TEXT_HTML) { + partinfo->mime_type == MIME_TEXT_HTML || + partinfo->mime_type == MIME_TEXT_ENRICHED) { if (procmime_find_string_part (partinfo, filename, str, case_sens) == TRUE) { found = TRUE; @@ -812,6 +1043,8 @@ gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo) : mimeinfo->name ? mimeinfo->name : "mimetmp"; base = g_basename(base); if (*base == '\0') base = "mimetmp"; + Xstrdup_a(base, base, return NULL); + subst_for_filename(base); } filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S, @@ -826,6 +1059,8 @@ ContentType procmime_scan_mime_type(const gchar *mime_type) if (!strncasecmp(mime_type, "text/html", 9)) type = MIME_TEXT_HTML; + else if (!strncasecmp(mime_type, "text/enriched", 13)) + type = MIME_TEXT_ENRICHED; else if (!strncasecmp(mime_type, "text/", 5)) type = MIME_TEXT; else if (!strncasecmp(mime_type, "message/rfc822", 14)) @@ -856,7 +1091,8 @@ gchar *procmime_get_mime_type(const gchar *filename) { static GHashTable *mime_type_table = NULL; MimeType *mime_type; - const gchar *ext, *p; + const gchar *p; + gchar *ext; if (!mime_type_table) { mime_type_table = procmime_get_mime_type_table(); @@ -865,11 +1101,10 @@ gchar *procmime_get_mime_type(const gchar *filename) filename = g_basename(filename); p = strrchr(filename, '.'); - if (p) - ext = p + 1; - else - return NULL; + if (!p) return NULL; + Xstrdup_a(ext, p + 1, return NULL); + g_strdown(ext); mime_type = g_hash_table_lookup(mime_type_table, ext); if (mime_type) { gchar *str; @@ -919,15 +1154,23 @@ static GHashTable *procmime_get_mime_type_table(void) for (cur = mime_type_list; cur != NULL; cur = cur->next) { gint i; + gchar *key; mime_type = (MimeType *)cur->data; if (!mime_type->extension) continue; exts = g_strsplit(mime_type->extension, " ", 16); - for (i = 0; exts[i] != NULL; i++) - g_hash_table_insert(table, g_strdup(exts[i]), - mime_type); + for (i = 0; exts[i] != NULL; i++) { + /* make the key case insensitive */ + g_strdown(exts[i]); + /* use previously dup'd key on overwriting */ + if (g_hash_table_lookup(table, exts[i])) + key = exts[i]; + else + key = g_strdup(exts[i]); + g_hash_table_insert(table, key, mime_type); + } g_strfreev(exts); } @@ -945,8 +1188,8 @@ GList *procmime_get_mime_type_list(void) if (mime_type_list) return mime_type_list; - if ((fp = fopen("/etc/mime.types", "r")) == NULL) { - if ((fp = fopen(SYSCONFDIR "/mime.types", "r")) == NULL) { + if ((fp = fopen("/etc/mime.types", "rb")) == NULL) { + if ((fp = fopen(SYSCONFDIR "/mime.types", "rb")) == NULL) { FILE_OP_ERROR(SYSCONFDIR "/mime.types", "fopen"); return NULL; } @@ -995,21 +1238,66 @@ EncodingType procmime_get_encoding_for_charset(const gchar *charset) else if (!strncasecmp(charset, "ISO-2022-", 9) || !strcasecmp(charset, "US-ASCII")) return ENC_7BIT; + else if (!strcasecmp(charset, "ISO-8859-5") || + !strncasecmp(charset, "KOI8-", 5) || + !strcasecmp(charset, "Windows-1251")) + return ENC_8BIT; + else if (!strncasecmp(charset, "ISO-8859-", 9)) + return ENC_QUOTED_PRINTABLE; else return ENC_8BIT; - /* return ENC_BASE64; */ - /* return ENC_QUOTED_PRINTABLE; */ } -const gchar *procmime_get_encoding_str(EncodingType encoding) +EncodingType procmime_get_encoding_for_file(const gchar *file) { - static const gchar *encoding_str[] = { - "7bit", "8bit", "quoted-printable", "base64", "x-uuencode", - NULL - }; + FILE *fp; + guchar buf[BUFSIZ]; + size_t len; - if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN) - return encoding_str[encoding]; - else - return NULL; + if ((fp = fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return ENC_UNKNOWN; + } + + while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { + guchar *p; + gint i; + + for (p = buf, i = 0; i < len; p++, i++) { + if (*p & 0x80) { + fclose(fp); + return ENC_BASE64; + } + } + } + + fclose(fp); + return ENC_7BIT; +} + +struct EncodingTable +{ + gchar *str; + EncodingType enc_type; +}; + +struct EncodingTable encoding_table[] = { + {"7bit", ENC_7BIT}, + {"8bit", ENC_8BIT}, + {"binary", ENC_BINARY}, + {"quoted-printable", ENC_QUOTED_PRINTABLE}, + {"base64", ENC_BASE64}, + {"x-uuencode", ENC_UNKNOWN}, + {NULL, ENC_UNKNOWN}, +}; + +const gchar *procmime_get_encoding_str(EncodingType encoding) +{ + struct EncodingTable *enc_table; + + for (enc_table = encoding_table; enc_table->str != NULL; enc_table++) { + if (enc_table->enc_type == encoding) + return enc_table->str; + } + return NULL; }