don't get a fright when encountering an unknown Content-Type
[claws.git] / src / procmime.c
index bcf68e2c97f04ee12a359c8c716908528aa577f6..0a40426877ac66f833eef8972914fec2ac1dcf05 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2016 Hiroyuki Yamamoto & The Claws Mail Team
+ * Copyright (C) 1999-2020 the Claws Mail Team and 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
@@ -52,7 +52,7 @@
 #include "timing.h"
 #include "privacy.h"
 #include "account.h"
-#include "claws_io.h"
+#include "file-utils.h"
 
 static GHashTable *procmime_get_mime_type_table        (void);
 static MimeInfo *procmime_scan_file_short(const gchar *filename);
@@ -262,7 +262,7 @@ const gchar *procmime_mimeinfo_get_parameter(MimeInfo *mimeinfo, const gchar *na
                gint llen = 0;                                                  \
                strretchomp(lastline);                                          \
                llen = strlen(lastline);                                        \
-               if (lastline[llen-1] == ' ' && !account_signatures_matchlist_str_found(lastline, "%s") &&       \
+               if (lastline[llen-1] == ' ' && !account_sigsep_matchlist_str_found(lastline, "%s") &&   \
                    !(llen == 2 && lastline[1] == ' ' && strchr(prefs_common.quote_chars, lastline[0]))) {                                      \
                        /* this is flowed */                                    \
                        if (delsp)                                              \
@@ -348,7 +348,7 @@ gboolean procmime_decode_content(MimeInfo *mimeinfo)
        tmp_file = TRUE;
        readend = mimeinfo->offset + mimeinfo->length;
 
-       account_signatures_matchlist_create(); /* FLUSH_LASTLINE will use it */
+       account_sigsep_matchlist_create(); /* FLUSH_LASTLINE will use it */
 
        *buf = '\0';
        if (encoding == ENC_QUOTED_PRINTABLE) {
@@ -366,7 +366,7 @@ gboolean procmime_decode_content(MimeInfo *mimeinfo)
                if (flowed)
                        FLUSH_LASTLINE();
        } else if (encoding == ENC_BASE64) {
-               gchar outbuf[BUFFSIZE];
+               gchar outbuf[BUFFSIZE + 1];
                gint len, inlen, inread;
                gboolean got_error = FALSE;
                gboolean uncanonicalize = FALSE;
@@ -390,6 +390,7 @@ gboolean procmime_decode_content(MimeInfo *mimeinfo)
 
                while ((inlen = MIN(readend - ftell(infp), sizeof(buf))) > 0 && !err) {
                        inread = claws_fread(buf, 1, inlen, infp);
+                       memset(outbuf, 0, sizeof(buf));
                        len = g_base64_decode_step(buf, inlen, outbuf, &state, &save);
                        if (uncanonicalize == TRUE && strlen(outbuf) < len && starting) {
                                uncanonicalize = FALSE;
@@ -428,8 +429,9 @@ gboolean procmime_decode_content(MimeInfo *mimeinfo)
                                        err = TRUE;
                        }
                }
-               if (tmpfp != outfp)
+               if (tmpfp != outfp) {
                        claws_fclose(tmpfp);
+               }
        } else if (encoding == ENC_X_UUENCODE) {
                gchar outbuf[BUFFSIZE];
                gint len;
@@ -468,7 +470,7 @@ gboolean procmime_decode_content(MimeInfo *mimeinfo)
        claws_fclose(outfp);
        claws_fclose(infp);
 
-       account_signatures_matchlist_delete();
+       account_sigsep_matchlist_delete();
 
        if (err == TRUE) {
                return FALSE;
@@ -733,7 +735,6 @@ gboolean procmime_scan_text_content(MimeInfo *mimeinfo,
        gchar buf[BUFFSIZE];
        gchar *str;
        gboolean scan_ret = FALSE;
-       gchar *tmpfile = NULL;
        int r;
 
        cm_return_val_if_fail(mimeinfo != NULL, TRUE);
@@ -742,27 +743,15 @@ gboolean procmime_scan_text_content(MimeInfo *mimeinfo,
        if (!procmime_decode_content(mimeinfo))
                return TRUE;
 
-#if HAVE_FMEMOPEN
-       tmpfp = fmemopen(NULL, mimeinfo->length * 2, "w+");
-#else
-       tmpfile = procmime_get_tmp_file_name(mimeinfo);
-       if (tmpfile == NULL) {
-               g_warning("no filename\n");
-               return TRUE;
-       }
-
-       tmpfp = claws_fopen(tmpfile, "w+");
-#endif
+       tmpfp = my_tmpfile();
 
        if (tmpfp == NULL) {
-               g_free(tmpfile);
-               FILE_OP_ERROR(tmpfile, "open");
+               FILE_OP_ERROR("tmpfile", "open");
                return TRUE;
        }
 
        if ((r = procmime_get_part_to_stream(tmpfp, mimeinfo)) < 0) {
                g_warning("procmime_get_part_to_stream error %d\n", r);
-               g_free(tmpfile);
                return TRUE;
        }
 
@@ -828,11 +817,6 @@ gboolean procmime_scan_text_content(MimeInfo *mimeinfo,
 
        claws_fclose(tmpfp);
 
-#if !HAVE_FMEMOPEN
-       claws_unlink(tmpfile);
-       g_free(tmpfile);
-#endif
-
        return scan_ret;
 }
 
@@ -868,29 +852,13 @@ FILE *procmime_get_text_content(MimeInfo *mimeinfo)
 FILE *procmime_get_binary_content(MimeInfo *mimeinfo)
 {
        FILE *outfp;
-#if !HAVE_FMEMOPEN
-       gchar *tmpfile = NULL;
-#endif
 
        cm_return_val_if_fail(mimeinfo != NULL, NULL);
 
        if (!procmime_decode_content(mimeinfo))
                return NULL;
 
-#if HAVE_FMEMOPEN
-       outfp = fmemopen(NULL, mimeinfo->length * 2, "w+");
-#else
-       tmpfile = procmime_get_tmp_file_name(mimeinfo);
-       if (tmpfile == NULL) {
-               g_warning("no filename\n");
-               return TRUE;
-       }
-
-       outfp = procmime_fopen(tmpfile, "w+");
-
-       g_unlink(tmpfile);
-       g_free(tmpfile);
-#endif
+       outfp = my_tmpfile();
 
        if (procmime_get_part_to_stream(outfp, mimeinfo) < 0) {
                return NULL;
@@ -1303,7 +1271,7 @@ EncodingType procmime_get_encoding_for_text_file(const gchar *file, gboolean *ha
                octet_percentage = 0.0;
 
        debug_print("procmime_get_encoding_for_text_file(): "
-                   "8bit chars: %zd / %zd (%f%%)\n", octet_chars, total_len,
+                   "8bit chars: %"G_GSIZE_FORMAT" / %"G_GSIZE_FORMAT" (%f%%)\n", octet_chars, total_len,
                    100.0 * octet_percentage);
 
        if (octet_percentage > 0.20 || force_b64) {
@@ -1357,6 +1325,7 @@ static struct TypeTable mime_type_table[] = {
        {"image", MIMETYPE_IMAGE},
        {"audio", MIMETYPE_AUDIO},
        {"video", MIMETYPE_VIDEO},
+       {"model", MIMETYPE_MODEL},
        {"application", MIMETYPE_APPLICATION},
        {"message", MIMETYPE_MESSAGE},
        {"multipart", MIMETYPE_MULTIPART},
@@ -1972,7 +1941,7 @@ static MimeParser *procmime_get_mimeparser_for_type(MimeMediaType type, const gc
        GSList *cur;
        for (cur = registered_parsers; cur; cur = cur->next) {
                MimeParser *parser = (MimeParser *)cur->data;
-               if (parser->type == type && !strcmp2(parser->sub_type, sub_type))
+               if (parser->type == type && !g_strcmp0(parser->sub_type, sub_type))
                        return parser;
        }
        return NULL;
@@ -2138,7 +2107,9 @@ static gboolean output_func(GNode *node, gpointer data)
        depth = g_node_depth(node);
        for (i = 0; i < depth; i++)
                g_print("    ");
-       g_print("%s/%s (offset:%d length:%d encoding: %d)\n", typenames[mimeinfo->type], mimeinfo->subtype, mimeinfo->offset, mimeinfo->length, mimeinfo->encoding_type);
+       g_print("%s/%s (offset:%d length:%d encoding: %d)\n", 
+               (mimeinfo->type < 8)? typenames[mimeinfo->type] : "unknown", 
+               mimeinfo->subtype, mimeinfo->offset, mimeinfo->length, mimeinfo->encoding_type);
 
        return FALSE;
 }
@@ -2505,7 +2476,7 @@ static gint procmime_write_message_rfc822(MimeInfo *mimeinfo, FILE *fp)
                        }
                        len = strlen(buf);
                        if (claws_fwrite(buf, sizeof(gchar), len, fp) < len) {
-                               g_warning("failed to dump %zd bytes from file", len);
+                               g_warning("failed to dump %"G_GSIZE_FORMAT" bytes from file", len);
                                claws_fclose(infp);
                                return -1;
                        }
@@ -2517,7 +2488,7 @@ static gint procmime_write_message_rfc822(MimeInfo *mimeinfo, FILE *fp)
        case MIMECONTENT_MEM:
                len = strlen(mimeinfo->data.mem);
                if (claws_fwrite(mimeinfo->data.mem, sizeof(gchar), len, fp) < len) {
-                       g_warning("failed to dump %zd bytes from mem", len);
+                       g_warning("failed to dump %"G_GSIZE_FORMAT" bytes from mem", len);
                        return -1;
                }
                break;
@@ -2569,7 +2540,7 @@ static gint procmime_write_multipart(MimeInfo *mimeinfo, FILE *fp)
                                break;
                        len = strlen(buf);
                        if (claws_fwrite(buf, sizeof(gchar), len, fp) < len) {
-                               g_warning("failed to write %zd", len);
+                               g_warning("failed to write %"G_GSIZE_FORMAT, len);
                                claws_fclose(infp);
                                return -1;
                        }
@@ -2584,7 +2555,7 @@ static gint procmime_write_multipart(MimeInfo *mimeinfo, FILE *fp)
                        *(str2 - 2) = '\0';
                len = strlen(str);
                if (claws_fwrite(str, sizeof(gchar), len, fp) < len) {
-                       g_warning("failed to write %zd from mem", len);
+                       g_warning("failed to write %"G_GSIZE_FORMAT" from mem", len);
                        g_free(str);
                        return -1;
                }
@@ -2695,3 +2666,104 @@ gchar *procmime_get_part_file_name(MimeInfo *mimeinfo)
        return base;
 }
 
+void *procmime_get_part_as_string(MimeInfo *mimeinfo,
+               gboolean null_terminate)
+{
+       FILE *infp;
+       gchar *data;
+       gint length, readlength;
+
+       cm_return_val_if_fail(mimeinfo != NULL, NULL);
+
+       if (mimeinfo->encoding_type != ENC_BINARY &&
+                       !procmime_decode_content(mimeinfo))
+               return NULL;
+
+       if (mimeinfo->content == MIMECONTENT_MEM)
+               return g_strdup(mimeinfo->data.mem);
+
+       if ((infp = claws_fopen(mimeinfo->data.filename, "rb")) == NULL) {
+               FILE_OP_ERROR(mimeinfo->data.filename, "claws_fopen");
+               return NULL;
+       }
+
+       if (fseek(infp, mimeinfo->offset, SEEK_SET) < 0) {
+               FILE_OP_ERROR(mimeinfo->data.filename, "fseek");
+               claws_fclose(infp);
+               return NULL;
+       }
+
+       length = mimeinfo->length;
+
+       data = g_malloc(null_terminate ? length + 1 : length);
+       if (data == NULL) {
+               g_warning("Could not allocate %d bytes for procmime_get_part_as_string.\n",
+                               (null_terminate ? length + 1 : length));
+               claws_fclose(infp);
+               return NULL;
+       }
+
+       readlength = claws_fread(data, length, 1, infp);
+       if (readlength <= 0) {
+               FILE_OP_ERROR(mimeinfo->data.filename, "fread");
+               g_free(data);
+               claws_fclose(infp);
+               return NULL;
+       }
+
+       claws_fclose(infp);
+
+       if (null_terminate)
+               data[length] = '\0';
+
+       return data;
+}
+
+/* Returns an open GInputStream. The caller should just
+ * read mimeinfo->length bytes from it and then release it. */
+GInputStream *procmime_get_part_as_inputstream(MimeInfo *mimeinfo)
+{
+       cm_return_val_if_fail(mimeinfo != NULL, NULL);
+
+       if (mimeinfo->encoding_type != ENC_BINARY &&
+                       !procmime_decode_content(mimeinfo)) {
+               g_warning("could not decode part");
+               return NULL;
+       }
+       if (mimeinfo->content == MIMECONTENT_MEM) {
+               /* NULL for destroy func, since we're not copying
+                * the data for the stream. */
+               return g_memory_input_stream_new_from_data(
+                               mimeinfo->data.mem,
+                               mimeinfo->length, NULL);
+       } else {
+               return g_memory_input_stream_new_from_data(
+                               procmime_get_part_as_string(mimeinfo, FALSE),
+                               mimeinfo->length, g_free);
+       }
+}
+
+GdkPixbuf *procmime_get_part_as_pixbuf(MimeInfo *mimeinfo, GError **error)
+{
+       GdkPixbuf *pixbuf;
+       GInputStream *stream;
+
+       if (error)
+               *error = NULL;
+
+       stream = procmime_get_part_as_inputstream(mimeinfo);
+       if (stream == NULL) {
+               if (error)
+                       *error = g_error_new_literal(G_FILE_ERROR, -1, _("Could not decode part"));
+               return NULL;
+       }
+
+       pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, error);
+       g_object_unref(stream);
+
+       if (error && *error != NULL)
+               return NULL;
+
+       return pixbuf;
+}
+