aboutsummaryrefslogtreecommitdiff
path: root/libsylph/procmime.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/procmime.c')
-rw-r--r--libsylph/procmime.c1158
1 files changed, 1158 insertions, 0 deletions
diff --git a/libsylph/procmime.c b/libsylph/procmime.c
new file mode 100644
index 00000000..faa882a4
--- /dev/null
+++ b/libsylph/procmime.c
@@ -0,0 +1,1158 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2005 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+
+#include "procmime.h"
+#include "procheader.h"
+#include "base64.h"
+#include "quoted-printable.h"
+#include "uuencode.h"
+#include "html.h"
+#include "codeconv.h"
+#include "utils.h"
+#include "prefs_common.h"
+
+static GHashTable *procmime_get_mime_type_table (void);
+static GList *procmime_get_mime_type_list (const gchar *file);
+
+
+MimeInfo *procmime_mimeinfo_new(void)
+{
+ MimeInfo *mimeinfo;
+
+ mimeinfo = g_new0(MimeInfo, 1);
+ mimeinfo->mime_type = MIME_UNKNOWN;
+ mimeinfo->encoding_type = ENC_UNKNOWN;
+
+ return mimeinfo;
+}
+
+void procmime_mimeinfo_free_all(MimeInfo *mimeinfo)
+{
+ while (mimeinfo != NULL) {
+ MimeInfo *next;
+
+ 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);
+
+ g_free(mimeinfo->plaintextfile);
+ g_free(mimeinfo->sigstatus);
+ g_free(mimeinfo->sigstatus_full);
+
+ procmime_mimeinfo_free_all(mimeinfo->sub);
+ procmime_mimeinfo_free_all(mimeinfo->children);
+ procmime_mimeinfo_free_all(mimeinfo->plaintext);
+
+ next = mimeinfo->next;
+ g_free(mimeinfo);
+ mimeinfo = next;
+ }
+}
+
+MimeInfo *procmime_mimeinfo_insert(MimeInfo *parent, MimeInfo *mimeinfo)
+{
+ MimeInfo *child = parent->children;
+
+ if (!child)
+ parent->children = mimeinfo;
+ else {
+ while (child->next != NULL)
+ child = child->next;
+
+ child->next = mimeinfo;
+ }
+
+ mimeinfo->parent = parent;
+ mimeinfo->level = parent->level + 1;
+
+ return mimeinfo;
+}
+
+void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new)
+{
+ MimeInfo *parent = old->parent;
+ MimeInfo *child;
+
+ g_return_if_fail(parent != NULL);
+ g_return_if_fail(new->next == NULL);
+
+ for (child = parent->children; child && child != old;
+ child = child->next)
+ ;
+ if (!child) {
+ g_warning("oops: parent can't find it's own child");
+ return;
+ }
+ procmime_mimeinfo_free_all(old);
+
+ if (child == parent->children) {
+ new->next = parent->children->next;
+ parent->children = new;
+ } else {
+ new->next = child->next;
+ child = new;
+ }
+}
+
+MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo)
+{
+ if (!mimeinfo) return NULL;
+
+ if (mimeinfo->children)
+ return mimeinfo->children;
+ if (mimeinfo->sub)
+ return mimeinfo->sub;
+ 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)
+ return mimeinfo->next;
+ if (mimeinfo->main) {
+ mimeinfo = mimeinfo->main;
+ if (mimeinfo->next)
+ return mimeinfo->next;
+ }
+ }
+
+ return NULL;
+}
+
+#if 0
+void procmime_dump_mimeinfo(MimeInfo *mimeinfo)
+{
+ gint i;
+
+ g_print("\n");
+
+ for (; mimeinfo != NULL; mimeinfo = procmime_mimeinfo_next(mimeinfo)) {
+ for (i = 0; i < mimeinfo->level; i++)
+ g_print(" ");
+ g_print("%s%s\n", mimeinfo->main ? "sub: " : "",
+ mimeinfo->content_type);
+ }
+}
+#endif
+
+MimeInfo *procmime_scan_message(MsgInfo *msginfo)
+{
+ FILE *fp;
+ MimeInfo *mimeinfo;
+
+ g_return_val_if_fail(msginfo != NULL, NULL);
+
+ if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
+ return NULL;
+
+ if (mimeinfo) {
+ mimeinfo->size = msginfo->size;
+ mimeinfo->content_size = get_left_file_size(fp);
+ if (mimeinfo->encoding_type == ENC_BASE64)
+ mimeinfo->content_size = mimeinfo->content_size / 4 * 3;
+ if (mimeinfo->mime_type == MIME_MULTIPART ||
+ mimeinfo->mime_type == MIME_MESSAGE_RFC822)
+ procmime_scan_multipart_message(mimeinfo, fp);
+ }
+
+ fclose(fp);
+
+ return mimeinfo;
+}
+
+void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp)
+{
+ gchar *p;
+ gchar *boundary;
+ gint boundary_len = 0;
+ gchar buf[BUFFSIZE];
+ glong fpos, prev_fpos;
+
+ g_return_if_fail(mimeinfo != NULL);
+ g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART ||
+ mimeinfo->mime_type == MIME_MESSAGE_RFC822);
+
+ if (mimeinfo->mime_type == MIME_MULTIPART) {
+ g_return_if_fail(mimeinfo->boundary != NULL);
+ g_return_if_fail(mimeinfo->sub == NULL);
+ }
+ g_return_if_fail(fp != NULL);
+
+ boundary = mimeinfo->boundary;
+
+ if (boundary) {
+ boundary_len = strlen(boundary);
+
+ /* look for first boundary */
+ while ((p = fgets(buf, sizeof(buf), fp)) != NULL)
+ if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
+ if (!p) return;
+ } else if (mimeinfo->parent && mimeinfo->parent->boundary) {
+ boundary = mimeinfo->parent->boundary;
+ boundary_len = strlen(boundary);
+ }
+
+ if ((fpos = ftell(fp)) < 0) {
+ perror("ftell");
+ return;
+ }
+
+ for (;;) {
+ MimeInfo *partinfo;
+ gboolean eom = FALSE;
+ glong content_pos;
+ gboolean is_base64;
+ gint len;
+ guint b64_content_len = 0;
+ gint b64_pad_len = 0;
+
+ prev_fpos = fpos;
+ debug_print("prev_fpos: %ld\n", fpos);
+
+ /* scan part header */
+ if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
+ MimeInfo *sub;
+
+ mimeinfo->sub = sub = procmime_scan_mime_header(fp);
+ if (!sub) break;
+
+ debug_print("message/rfc822 part found\n");
+ sub->level = mimeinfo->level + 1;
+ sub->parent = mimeinfo->parent;
+ sub->main = mimeinfo;
+
+ partinfo = sub;
+ } else {
+ partinfo = procmime_scan_mime_header(fp);
+ if (!partinfo) break;
+ procmime_mimeinfo_insert(mimeinfo, partinfo);
+ debug_print("content-type: %s\n",
+ partinfo->content_type);
+ }
+
+ /* begin content */
+ content_pos = ftell(fp);
+ debug_print("content_pos: %ld\n", content_pos);
+
+ if (partinfo->mime_type == MIME_MULTIPART ||
+ partinfo->mime_type == MIME_MESSAGE_RFC822) {
+ if (partinfo->level < 8)
+ procmime_scan_multipart_message(partinfo, fp);
+ }
+
+ /* look for next boundary */
+ buf[0] = '\0';
+ is_base64 = partinfo->encoding_type == ENC_BASE64;
+ while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
+ if (IS_BOUNDARY(buf, boundary, boundary_len)) {
+ if (buf[2 + boundary_len] == '-' &&
+ buf[2 + boundary_len + 1] == '-')
+ eom = TRUE;
+ break;
+ } else if (is_base64) {
+ const gchar *s;
+ for (s = buf; *s && *s != '\r' && *s != '\n';
+ ++s)
+ if (*s == '=')
+ ++b64_pad_len;
+ b64_content_len += s - buf;
+ }
+ }
+ if (p == NULL) {
+ /* broken MIME, or single part MIME message */
+ buf[0] = '\0';
+ eom = TRUE;
+ }
+ debug_print("boundary: %s\n", buf);
+
+ fpos = ftell(fp);
+ debug_print("fpos: %ld\n", fpos);
+
+ len = strlen(buf);
+ partinfo->size = fpos - prev_fpos - len;
+ if (is_base64)
+ partinfo->content_size =
+ b64_content_len / 4 * 3 - b64_pad_len;
+ else
+ partinfo->content_size = fpos - content_pos - len;
+ debug_print("partinfo->size: %d\n", partinfo->size);
+ debug_print("partinfo->content_size: %d\n",
+ partinfo->content_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);
+ }
+
+ if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
+ if (len > 0 && fseek(fp, fpos - len, SEEK_SET) < 0)
+ perror("fseek");
+ break;
+ }
+
+ if (eom) break;
+ }
+}
+
+void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding)
+{
+ gchar *buf;
+
+ Xstrdup_a(buf, encoding, return);
+
+ g_free(mimeinfo->encoding);
+
+ mimeinfo->encoding = g_strdup(g_strstrip(buf));
+ if (!g_ascii_strcasecmp(buf, "7bit"))
+ mimeinfo->encoding_type = ENC_7BIT;
+ else if (!g_ascii_strcasecmp(buf, "8bit"))
+ mimeinfo->encoding_type = ENC_8BIT;
+ else if (!g_ascii_strcasecmp(buf, "quoted-printable"))
+ mimeinfo->encoding_type = ENC_QUOTED_PRINTABLE;
+ else if (!g_ascii_strcasecmp(buf, "base64"))
+ mimeinfo->encoding_type = ENC_BASE64;
+ else if (!g_ascii_strcasecmp(buf, "x-uuencode"))
+ mimeinfo->encoding_type = ENC_X_UUENCODE;
+ else
+ mimeinfo->encoding_type = ENC_UNKNOWN;
+
+}
+
+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->boundary);
+ mimeinfo->content_type = NULL;
+ mimeinfo->charset = NULL;
+ mimeinfo->name = NULL;
+ mimeinfo->boundary = NULL;
+
+ procmime_scan_content_type_str(content_type, &mimeinfo->content_type,
+ &mimeinfo->charset, &mimeinfo->name,
+ &mimeinfo->boundary);
+
+ mimeinfo->mime_type = procmime_scan_mime_type(mimeinfo->content_type);
+ if (mimeinfo->mime_type == MIME_MULTIPART && !mimeinfo->boundary)
+ mimeinfo->mime_type = MIME_TEXT;
+}
+
+void procmime_scan_content_type_str(const gchar *content_type,
+ gchar **mime_type, gchar **charset,
+ gchar **name, gchar **boundary)
+{
+ gchar *delim, *p;
+ gchar *buf;
+
+ Xstrdup_a(buf, content_type, return);
+
+ if ((delim = strchr(buf, ';'))) *delim = '\0';
+ if (mime_type)
+ *mime_type = g_strdup(g_strstrip(buf));
+
+ if (!delim) return;
+ p = delim + 1;
+
+ for (;;) {
+ gchar *eq;
+ gchar *attr, *value;
+
+ if ((delim = strchr(p, ';'))) *delim = '\0';
+
+ if (!(eq = strchr(p, '='))) break;
+
+ *eq = '\0';
+ attr = p;
+ g_strstrip(attr);
+ value = eq + 1;
+ g_strstrip(value);
+
+ if (*value == '"')
+ extract_quote(value, '"');
+ else {
+ eliminate_parenthesis(value, '(', ')');
+ g_strstrip(value);
+ }
+
+ if (*value) {
+ if (charset && !g_ascii_strcasecmp(attr, "charset"))
+ *charset = g_strdup(value);
+ else if (name && !g_ascii_strcasecmp(attr, "name"))
+ *name = conv_unmime_header(value, NULL);
+ else if (boundary &&
+ !g_ascii_strcasecmp(attr, "boundary"))
+ *boundary = g_strdup(value);
+ }
+
+ if (!delim) break;
+ p = delim + 1;
+ }
+}
+
+void procmime_scan_content_disposition(MimeInfo *mimeinfo,
+ const gchar *content_disposition)
+{
+ gchar *delim, *p, *dispos;
+ gchar *buf;
+
+ Xstrdup_a(buf, content_disposition, return);
+
+ if ((delim = strchr(buf, ';'))) *delim = '\0';
+ mimeinfo->content_disposition = dispos = g_strdup(g_strstrip(buf));
+
+ if (!delim) return;
+ p = delim + 1;
+
+ for (;;) {
+ gchar *eq;
+ gchar *attr, *value;
+
+ if ((delim = strchr(p, ';'))) *delim = '\0';
+
+ if (!(eq = strchr(p, '='))) break;
+
+ *eq = '\0';
+ attr = p;
+ g_strstrip(attr);
+ value = eq + 1;
+ g_strstrip(value);
+
+ if (*value == '"')
+ extract_quote(value, '"');
+ else {
+ eliminate_parenthesis(value, '(', ')');
+ g_strstrip(value);
+ }
+
+ if (*value) {
+ if (!g_ascii_strcasecmp(attr, "filename")) {
+ g_free(mimeinfo->filename);
+ mimeinfo->filename =
+ conv_unmime_header(value, NULL);
+ break;
+ }
+ }
+
+ if (!delim) break;
+ p = delim + 1;
+ }
+}
+
+enum
+{
+ H_CONTENT_TRANSFER_ENCODING = 0,
+ H_CONTENT_TYPE = 1,
+ H_CONTENT_DISPOSITION = 2
+};
+
+MimeInfo *procmime_scan_mime_header(FILE *fp)
+{
+ static HeaderEntry hentry[] = {{"Content-Transfer-Encoding:",
+ NULL, FALSE},
+ {"Content-Type:", NULL, TRUE},
+ {"Content-Disposition:",
+ NULL, TRUE},
+ {NULL, NULL, FALSE}};
+ gchar buf[BUFFSIZE];
+ gint hnum;
+ HeaderEntry *hp;
+ MimeInfo *mimeinfo;
+
+ g_return_val_if_fail(fp != NULL, NULL);
+
+ mimeinfo = procmime_mimeinfo_new();
+ mimeinfo->mime_type = MIME_TEXT;
+ mimeinfo->encoding_type = ENC_7BIT;
+ mimeinfo->fpos = ftell(fp);
+
+ while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
+ != -1) {
+ hp = hentry + hnum;
+
+ if (H_CONTENT_TRANSFER_ENCODING == hnum) {
+ procmime_scan_encoding
+ (mimeinfo, buf + strlen(hp->name));
+ } else if (H_CONTENT_TYPE == hnum) {
+ procmime_scan_content_type
+ (mimeinfo, buf + strlen(hp->name));
+ } else if (H_CONTENT_DISPOSITION == hnum) {
+ procmime_scan_content_disposition
+ (mimeinfo, buf + strlen(hp->name));
+ }
+ }
+
+ if (mimeinfo->mime_type == MIME_APPLICATION_OCTET_STREAM &&
+ mimeinfo->name) {
+ const gchar *type;
+ type = procmime_get_mime_type(mimeinfo->name);
+ if (type)
+ mimeinfo->mime_type = procmime_scan_mime_type(type);
+ }
+
+ if (!mimeinfo->content_type)
+ mimeinfo->content_type = g_strdup("text/plain");
+
+ return mimeinfo;
+}
+
+FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo)
+{
+ gchar buf[BUFFSIZE];
+ gchar *boundary = NULL;
+ gint boundary_len = 0;
+ gboolean tmp_file = FALSE;
+
+ g_return_val_if_fail(infp != NULL, NULL);
+ g_return_val_if_fail(mimeinfo != NULL, NULL);
+
+ if (!outfp) {
+ outfp = my_tmpfile();
+ if (!outfp) {
+ perror("tmpfile");
+ return NULL;
+ }
+ tmp_file = TRUE;
+ }
+
+ if (mimeinfo->parent && mimeinfo->parent->boundary) {
+ boundary = mimeinfo->parent->boundary;
+ boundary_len = strlen(boundary);
+ }
+
+ if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) {
+ while (fgets(buf, sizeof(buf), infp) != NULL &&
+ (!boundary ||
+ !IS_BOUNDARY(buf, boundary, boundary_len))) {
+ gint len;
+ len = qp_decode_line(buf);
+ fwrite(buf, len, 1, outfp);
+ }
+ } else if (mimeinfo->encoding_type == ENC_BASE64) {
+ gchar outbuf[BUFFSIZE];
+ gint len;
+ Base64Decoder *decoder;
+ gboolean uncanonicalize = FALSE;
+ FILE *tmpfp = outfp;
+ ContentType content_type;
+
+ content_type = procmime_scan_mime_type(mimeinfo->content_type);
+ if (content_type == MIME_TEXT ||
+ content_type == MIME_TEXT_HTML ||
+ content_type == MIME_MESSAGE_RFC822) {
+ uncanonicalize = TRUE;
+ tmpfp = my_tmpfile();
+ if (!tmpfp) {
+ perror("tmpfile");
+ if (tmp_file) fclose(outfp);
+ return NULL;
+ }
+ }
+
+ decoder = base64_decoder_new();
+ while (fgets(buf, sizeof(buf), infp) != NULL &&
+ (!boundary ||
+ !IS_BOUNDARY(buf, boundary, boundary_len))) {
+ len = base64_decoder_decode(decoder, buf, outbuf);
+ if (len < 0) {
+ g_warning("Bad BASE64 content\n");
+ break;
+ }
+ fwrite(outbuf, sizeof(gchar), len, tmpfp);
+ }
+ base64_decoder_free(decoder);
+
+ if (uncanonicalize) {
+ rewind(tmpfp);
+ while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
+ strcrchomp(buf);
+ fputs(buf, outfp);
+ }
+ fclose(tmpfp);
+ }
+ } else if (mimeinfo->encoding_type == ENC_X_UUENCODE) {
+ gchar outbuf[BUFFSIZE];
+ gint len;
+ gboolean flag = FALSE;
+
+ while (fgets(buf, sizeof(buf), infp) != NULL &&
+ (!boundary ||
+ !IS_BOUNDARY(buf, boundary, boundary_len))) {
+ if(!flag && strncmp(buf,"begin ", 6)) continue;
+
+ if (flag) {
+ len = fromuutobits(outbuf, buf);
+ if (len <= 0) {
+ if (len < 0)
+ g_warning("Bad UUENCODE content(%d)\n", len);
+ break;
+ }
+ fwrite(outbuf, sizeof(gchar), len, outfp);
+ } else
+ flag = TRUE;
+ }
+ } else {
+ while (fgets(buf, sizeof(buf), infp) != NULL &&
+ (!boundary ||
+ !IS_BOUNDARY(buf, boundary, boundary_len))) {
+ fputs(buf, outfp);
+ }
+ }
+
+ if (tmp_file) rewind(outfp);
+ return outfp;
+}
+
+gint procmime_get_part(const gchar *outfile, const gchar *infile,
+ MimeInfo *mimeinfo)
+{
+ FILE *infp;
+ gint ret;
+
+ g_return_val_if_fail(outfile != NULL, -1);
+ g_return_val_if_fail(infile != NULL, -1);
+ g_return_val_if_fail(mimeinfo != NULL, -1);
+
+ if ((infp = g_fopen(infile, "rb")) == NULL) {
+ FILE_OP_ERROR(infile, "fopen");
+ return -1;
+ }
+ ret = procmime_get_part_fp(outfile, infp, mimeinfo);
+ fclose(infp);
+
+ return ret;
+}
+
+gint procmime_get_part_fp(const gchar *outfile, FILE *infp, MimeInfo *mimeinfo)
+{
+ FILE *outfp;
+ gchar buf[BUFFSIZE];
+
+ g_return_val_if_fail(outfile != NULL, -1);
+ g_return_val_if_fail(infp != NULL, -1);
+ g_return_val_if_fail(mimeinfo != NULL, -1);
+
+ if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
+ FILE_OP_ERROR("procmime_get_part_fp()", "fseek");
+ return -1;
+ }
+ if ((outfp = g_fopen(outfile, "wb")) == NULL) {
+ FILE_OP_ERROR(outfile, "fopen");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), infp) != NULL)
+ if (buf[0] == '\r' || buf[0] == '\n') break;
+
+ procmime_decode_content(outfp, infp, mimeinfo);
+
+ if (fclose(outfp) == EOF) {
+ FILE_OP_ERROR(outfile, "fclose");
+ g_unlink(outfile);
+ return -1;
+ }
+
+ return 0;
+}
+
+FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp,
+ const gchar *encoding)
+{
+ FILE *tmpfp, *outfp;
+ const gchar *src_encoding;
+ gboolean conv_fail = FALSE;
+ gchar buf[BUFFSIZE];
+
+ 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);
+
+ if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) {
+ perror("fseek");
+ return NULL;
+ }
+
+ while (fgets(buf, sizeof(buf), infp) != NULL)
+ if (buf[0] == '\r' || buf[0] == '\n') break;
+
+ tmpfp = procmime_decode_content(NULL, infp, mimeinfo);
+ if (!tmpfp)
+ return NULL;
+
+ if ((outfp = my_tmpfile()) == NULL) {
+ perror("tmpfile");
+ fclose(tmpfp);
+ return NULL;
+ }
+
+ src_encoding = prefs_common.force_charset
+ ? prefs_common.force_charset : mimeinfo->charset;
+
+ if (mimeinfo->mime_type == MIME_TEXT) {
+ while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
+ gchar *str;
+
+ str = conv_codeset_strdup(buf, src_encoding, encoding);
+ 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;
+ const gchar *str;
+
+ conv = conv_code_converter_new(src_encoding, encoding);
+ parser = html_parser_new(tmpfp, conv);
+ while ((str = html_parse(parser)) != NULL) {
+ fputs(str, outfp);
+ }
+ html_parser_destroy(parser);
+ conv_code_converter_destroy(conv);
+ }
+
+ if (conv_fail)
+ g_warning(_("procmime_get_text_content(): Code conversion failed.\n"));
+
+ fclose(tmpfp);
+ rewind(outfp);
+
+ return outfp;
+}
+
+/* search the first text part of (multipart) MIME message,
+ decode, convert it and output to outfp. */
+FILE *procmime_get_first_text_content(MsgInfo *msginfo, const gchar *encoding)
+{
+ FILE *infp, *outfp = NULL;
+ MimeInfo *mimeinfo, *partinfo;
+
+ g_return_val_if_fail(msginfo != NULL, NULL);
+
+ mimeinfo = procmime_scan_message(msginfo);
+ if (!mimeinfo) return NULL;
+
+ if ((infp = procmsg_open_message(msginfo)) == NULL) {
+ procmime_mimeinfo_free_all(mimeinfo);
+ return NULL;
+ }
+
+ 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 = procmime_mimeinfo_next(partinfo);
+ }
+
+ if (partinfo)
+ outfp = procmime_get_text_content(partinfo, infp, encoding);
+
+ fclose(infp);
+ procmime_mimeinfo_free_all(mimeinfo);
+
+ return outfp;
+}
+
+gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename,
+ const gchar *str, StrFindFunc find_func)
+{
+
+ FILE *infp, *outfp;
+ gchar buf[BUFFSIZE];
+
+ 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);
+ g_return_val_if_fail(str != NULL, FALSE);
+ g_return_val_if_fail(find_func != NULL, FALSE);
+
+ if ((infp = g_fopen(filename, "rb")) == NULL) {
+ FILE_OP_ERROR(filename, "fopen");
+ return FALSE;
+ }
+
+ outfp = procmime_get_text_content(mimeinfo, infp, NULL);
+ fclose(infp);
+
+ if (!outfp)
+ return FALSE;
+
+ while (fgets(buf, sizeof(buf), outfp) != NULL) {
+ strretchomp(buf);
+ if (find_func(buf, str)) {
+ fclose(outfp);
+ return TRUE;
+ }
+ }
+
+ fclose(outfp);
+
+ return FALSE;
+}
+
+gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str,
+ StrFindFunc find_func)
+{
+ MimeInfo *mimeinfo;
+ MimeInfo *partinfo;
+ gchar *filename;
+ gboolean found = FALSE;
+
+ g_return_val_if_fail(msginfo != NULL, FALSE);
+ g_return_val_if_fail(str != NULL, FALSE);
+ g_return_val_if_fail(find_func != NULL, FALSE);
+
+ filename = procmsg_get_message_file(msginfo);
+ if (!filename) return FALSE;
+ mimeinfo = procmime_scan_message(msginfo);
+
+ for (partinfo = mimeinfo; partinfo != NULL;
+ partinfo = procmime_mimeinfo_next(partinfo)) {
+ if (partinfo->mime_type == MIME_TEXT ||
+ partinfo->mime_type == MIME_TEXT_HTML) {
+ if (procmime_find_string_part
+ (partinfo, filename, str, find_func) == TRUE) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ procmime_mimeinfo_free_all(mimeinfo);
+ g_free(filename);
+
+ return found;
+}
+
+gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
+{
+ gchar *base;
+ const gchar *base_;
+
+ base_ = mimeinfo->filename ? mimeinfo->filename
+ : mimeinfo->name ? mimeinfo->name : "mimetmp";
+ base_ = g_basename(base_);
+ if (*base_ == '\0') base_ = "mimetmp";
+ base = conv_filename_from_utf8(base_);
+ subst_for_filename(base);
+
+ return base;
+}
+
+gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo)
+{
+ static guint32 id = 0;
+ gchar *base;
+ gchar *filename;
+ gchar f_prefix[10];
+
+ g_return_val_if_fail(mimeinfo != NULL, NULL);
+
+ g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++);
+
+ if (MIME_TEXT_HTML == mimeinfo->mime_type)
+ base = g_strdup("mimetmp.html");
+ else
+ base = procmime_get_part_file_name(mimeinfo);
+
+ filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
+ f_prefix, base, NULL);
+
+ g_free(base);
+
+ return filename;
+}
+
+ContentType procmime_scan_mime_type(const gchar *mime_type)
+{
+ ContentType type;
+
+ if (!g_ascii_strncasecmp(mime_type, "text/html", 9))
+ type = MIME_TEXT_HTML;
+ else if (!g_ascii_strncasecmp(mime_type, "text/", 5))
+ type = MIME_TEXT;
+ else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14))
+ type = MIME_MESSAGE_RFC822;
+ else if (!g_ascii_strncasecmp(mime_type, "message/", 8))
+ type = MIME_TEXT;
+ else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream",
+ 24))
+ type = MIME_APPLICATION_OCTET_STREAM;
+ else if (!g_ascii_strncasecmp(mime_type, "application/", 12))
+ type = MIME_APPLICATION;
+ else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10))
+ type = MIME_MULTIPART;
+ else if (!g_ascii_strncasecmp(mime_type, "image/", 6))
+ type = MIME_IMAGE;
+ else if (!g_ascii_strncasecmp(mime_type, "audio/", 6))
+ type = MIME_AUDIO;
+ else if (!g_ascii_strcasecmp(mime_type, "text"))
+ type = MIME_TEXT;
+ else
+ type = MIME_UNKNOWN;
+
+ return type;
+}
+
+static GList *mime_type_list = NULL;
+
+gchar *procmime_get_mime_type(const gchar *filename)
+{
+ static GHashTable *mime_type_table = NULL;
+ MimeType *mime_type;
+ const gchar *p;
+ gchar *ext;
+
+ if (!mime_type_table) {
+ mime_type_table = procmime_get_mime_type_table();
+ if (!mime_type_table) return NULL;
+ }
+
+ filename = g_basename(filename);
+ p = strrchr(filename, '.');
+ 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;
+
+ str = g_strconcat(mime_type->type, "/", mime_type->sub_type,
+ NULL);
+ return str;
+ }
+
+ return NULL;
+}
+
+static GHashTable *procmime_get_mime_type_table(void)
+{
+ GHashTable *table = NULL;
+ GList *cur;
+ MimeType *mime_type;
+ gchar **exts;
+
+ if (!mime_type_list) {
+ GList *list;
+ gchar *dir;
+
+ mime_type_list =
+ procmime_get_mime_type_list(SYSCONFDIR "/mime.types");
+ if (!mime_type_list) {
+ list = procmime_get_mime_type_list("/etc/mime.types");
+ mime_type_list = g_list_concat(mime_type_list, list);
+ }
+ dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ "mime.types", NULL);
+ list = procmime_get_mime_type_list(dir);
+ g_free(dir);
+ mime_type_list = g_list_concat(mime_type_list, list);
+
+ if (!mime_type_list) {
+ g_warning("mime.types not found\n");
+ return NULL;
+ }
+ }
+
+ table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ 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++) {
+ /* 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);
+ }
+
+ return table;
+}
+
+static GList *procmime_get_mime_type_list(const gchar *file)
+{
+ GList *list = NULL;
+ FILE *fp;
+ gchar buf[BUFFSIZE];
+ gchar *p;
+ gchar *delim;
+ MimeType *mime_type;
+
+ if ((fp = g_fopen(file, "rb")) == NULL) return NULL;
+
+ debug_print("Reading %s ...\n", file);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ p = strchr(buf, '#');
+ if (p) *p = '\0';
+ g_strstrip(buf);
+
+ p = buf;
+ while (*p && !g_ascii_isspace(*p)) p++;
+ if (*p) {
+ *p = '\0';
+ p++;
+ }
+ delim = strchr(buf, '/');
+ if (delim == NULL) continue;
+ *delim = '\0';
+
+ mime_type = g_new(MimeType, 1);
+ mime_type->type = g_strdup(buf);
+ mime_type->sub_type = g_strdup(delim + 1);
+
+ while (*p && g_ascii_isspace(*p)) p++;
+ if (*p)
+ mime_type->extension = g_strdup(p);
+ else
+ mime_type->extension = NULL;
+
+ list = g_list_append(list, mime_type);
+ }
+
+ fclose(fp);
+
+ if (!list)
+ g_warning("Can't read mime.types\n");
+
+ return list;
+}
+
+EncodingType procmime_get_encoding_for_charset(const gchar *charset)
+{
+ if (!charset)
+ return ENC_8BIT;
+ else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) ||
+ !g_ascii_strcasecmp(charset, "US-ASCII"))
+ return ENC_7BIT;
+ else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") ||
+ !g_ascii_strncasecmp(charset, "KOI8-", 5) ||
+ !g_ascii_strcasecmp(charset, "Windows-1251"))
+ return ENC_8BIT;
+ else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9))
+ return ENC_QUOTED_PRINTABLE;
+ else
+ return ENC_8BIT;
+}
+
+EncodingType procmime_get_encoding_for_text_file(const gchar *file)
+{
+ FILE *fp;
+ guchar buf[BUFFSIZE];
+ size_t len;
+ size_t octet_chars = 0;
+ size_t total_len = 0;
+ gfloat octet_percentage;
+
+ if ((fp = g_fopen(file, "rb")) == NULL) {
+ FILE_OP_ERROR(file, "fopen");
+ return ENC_UNKNOWN;
+ }
+
+ while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) {
+ guchar *p;
+ gint i;
+
+ for (p = buf, i = 0; i < len; ++p, ++i) {
+ if (*p & 0x80)
+ ++octet_chars;
+ }
+ total_len += len;
+ }
+
+ fclose(fp);
+
+ if (total_len > 0)
+ octet_percentage = (gfloat)octet_chars / (gfloat)total_len;
+ else
+ octet_percentage = 0.0;
+
+ debug_print("procmime_get_encoding_for_text_file(): "
+ "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len,
+ 100.0 * octet_percentage);
+
+ if (octet_percentage > 0.20) {
+ debug_print("using BASE64\n");
+ return ENC_BASE64;
+ } else if (octet_chars > 0) {
+ debug_print("using quoted-printable\n");
+ return ENC_QUOTED_PRINTABLE;
+ } else {
+ debug_print("using 7bit\n");
+ return ENC_7BIT;
+ }
+}
+
+const gchar *procmime_get_encoding_str(EncodingType encoding)
+{
+ static const gchar *encoding_str[] = {
+ "7bit", "8bit", "quoted-printable", "base64", "x-uuencode",
+ NULL
+ };
+
+ if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN)
+ return encoding_str[encoding];
+ else
+ return NULL;
+}