From 15ad934d815a886d2c9fc89bf493b6c2414325ad Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 29 Aug 2010 22:13:06 +0300 Subject: [PATCH] * update attachment-extraction (ie., 'mu extract'); still WIP, but basics are working --- src/mu-cmd-extract.c | 167 ++++++++++++++++++++++++++++++++++--------- src/mu-config.c | 15 ++-- src/mu-config.h | 7 +- src/mu-msg-part.c | 40 ++++++++--- src/mu-msg.c | 3 + src/mu-util.c | 5 +- 6 files changed, 183 insertions(+), 54 deletions(-) diff --git a/src/mu-cmd-extract.c b/src/mu-cmd-extract.c index 9722f059..74417668 100644 --- a/src/mu-cmd-extract.c +++ b/src/mu-cmd-extract.c @@ -29,27 +29,131 @@ #include "mu-cmd.h" static gboolean -save_part (const char* path, unsigned idx) +save_numbered_parts (MuMsg *msg, MuConfigOptions *opts) { + gboolean rv; + char **parts, **cur; + + parts = g_strsplit (opts->parts, ",", 0); + + for (rv = TRUE, cur = parts; cur && *cur; ++cur) { + + int idx; + char *endptr; + + idx = (int)strtol (*cur, &endptr, 10); + if (idx < 0 || *cur == endptr) { + g_warning ("invalid MIME-part index '%s'", *cur); + rv = FALSE; + break; + } + + /* FIXME: targetdir, overwrite */ + if (!mu_msg_mime_part_save (msg, idx, NULL, TRUE)) { + g_warning ("failed to save MIME-part %d", idx); + rv = FALSE; + break; + } + } + + g_strfreev (parts); + + return rv; +} + +struct _SaveData { + MuMsg *msg; + gboolean attachments_only; + gboolean result; + guint saved_num; +}; +typedef struct _SaveData SaveData; + + +static void +save_part_if (MuMsgPart *part, SaveData *sd) +{ + /* something went wrong somewhere; stop */ + if (!sd->result) + return; + + /* filter out non-attachments if only want attachments. Note, + * the attachment check may be a bit too strict */ + if (sd->attachments_only) + if (!part->disposition || + g_ascii_strcasecmp (part->disposition, "attachment") != 0) + return; + + /* ignore multiparts */ + if (part->type && + g_ascii_strcasecmp (part->type, "multipart") == 0) + return; + + sd->result = mu_msg_mime_part_save (sd->msg, part->index, NULL, TRUE); + if (!sd->result) + g_warning ("failed to save MIME-part %u", part->index); + else + ++sd->saved_num; + +} + +static gboolean +save_certain_parts (MuMsg *msg, gboolean attachments_only) +{ + SaveData sd; + + sd.msg = msg; + sd.result = TRUE; + sd.saved_num = 0; + sd.attachments_only = attachments_only; + + mu_msg_msg_part_foreach (msg, + (MuMsgPartForeachFunc)save_part_if, + &sd); + + if (sd.saved_num == 0) { + g_warning ("no %s extracted from this message", + attachments_only ? "attachments" : "parts"); + sd.result = FALSE; + } + + return sd.result; +} + + +static gboolean +save_parts (const char *path, MuConfigOptions *opts) +{ MuMsg* msg; + gboolean rv; msg = mu_msg_new (path, NULL); if (!msg) return FALSE; + + /* note, mu_cmd_extract already checks whether what's in opts + * is somewhat, so no need for extensive checking here */ - mu_msg_mime_part_save (msg, idx, NULL, TRUE); /* FIXME */ - + /* should we save some explicit parts? */ + if (opts->parts) + rv = save_numbered_parts (msg, opts); + else if (opts->save_attachments) /* all attachments */ + rv = save_certain_parts (msg, TRUE); + else if (opts->save_all) /* all parts */ + rv = save_certain_parts (msg, FALSE); + else + g_assert_not_reached (); + mu_msg_destroy (msg); - return TRUE; + return rv; } - static void -each_part (MuMsgPart *part, gpointer user_data) +each_part_show (MuMsgPart *part, gpointer user_data) { - g_print ("%u %s %s/%s [%s]\n", + g_print (" %u %s %s/%s [%s]\n", part->index, part->file_name ? part->file_name : "", part->type ? part->type : "", @@ -59,7 +163,7 @@ each_part (MuMsgPart *part, gpointer user_data) static gboolean -show_parts (const char* path) +show_parts (const char* path, MuConfigOptions *opts) { MuMsg* msg; @@ -67,7 +171,8 @@ show_parts (const char* path) if (!msg) return FALSE; - mu_msg_msg_part_foreach (msg, each_part, NULL); + g_print ("MIME-parts in this message:\n"); + mu_msg_msg_part_foreach (msg, each_part_show, NULL); mu_msg_destroy (msg); @@ -83,35 +188,31 @@ mu_cmd_extract (MuConfigOptions *opts) g_return_val_if_fail (opts, FALSE); g_return_val_if_fail (mu_cmd_equals (opts, "extract"), FALSE); - /* note: params[0] will be 'view' */ - if (!opts->params[0] || !opts->params[1]) { + if (!opts->params[1]) { g_warning ("missing mail file to extract something from"); return FALSE; } - mu_msg_init(); - - rv = TRUE; - if (!opts->params[2]) /* no explicit part, show, don't save */ - rv = show_parts (opts->params[1]); - else { - int i; - for (i = 2; opts->params[i]; ++i) { - unsigned idx = atoi (opts->params[i]); - if (idx == 0) { - g_warning ("not a valid index: %s", opts->params[i]); - rv = FALSE; - break; - } - if (!save_part (opts->params[1], idx)) { - g_warning ("failed to save part %d of %s", idx, - opts->params[1]); - rv = FALSE; - break; - } - } + + if (opts->save_attachments && opts->save_all) { + g_warning ("only one of --save-attachments and --save-all is allowed"); + return FALSE; + } + + if ((opts->save_attachments || opts->save_all) && opts->parts) { + g_warning ("with --save-attachments/--save-all, parts should not be specified"); + return FALSE; } - mu_msg_uninit(); + mu_msg_init(); + + if (!opts->parts && + !opts->save_attachments && + !opts->save_all) /* show, don't save */ + rv = show_parts (opts->params[1], opts); + else + rv = save_parts (opts->params[1], opts); /* save */ + + mu_msg_uninit(); return rv; } diff --git a/src/mu-config.c b/src/mu-config.c index 47fc1c6e..7a415003 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -117,8 +117,7 @@ config_options_group_index (MuConfigOptions *opts) "don't clean up the database after indexing", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL } }; - - + og = g_option_group_new ("index", "options for the 'index' command", "", NULL, NULL); @@ -211,11 +210,13 @@ config_options_group_extract (MuConfigOptions *opts) { GOptionGroup *og; GOptionEntry entries[] = { - {"all", 0, 0, G_OPTION_ARG_NONE, &opts->all, - "extract all attachments", NULL}, - {"all-parts", 0, 0, G_OPTION_ARG_NONE, &opts->all_parts, - "extract all parts (incl. non-attachments)", NULL}, - {"target-dir", 0, 0, G_OPTION_ARG_STRING, &opts->targetdir, + {"save-attachments", 'a', 0, G_OPTION_ARG_NONE, &opts->save_attachments, + "save all attachments", NULL}, + {"save-all", 0, 0, G_OPTION_ARG_NONE, &opts->save_all, + "save all parts (incl. non-attachments)", NULL}, + {"parts", 0, 0, G_OPTION_ARG_STRING, &opts->parts, + "save specific parts", NULL}, + {"target-dir", 0, 0, G_OPTION_ARG_FILENAME, &opts->targetdir, "target directory for saving", NULL}, { NULL, 0, 0, 0, NULL, NULL, NULL } }; diff --git a/src/mu-config.h b/src/mu-config.h index 9536f540..b7fb6100 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -61,9 +61,10 @@ struct _MuConfigOptions { mode_t dirmode; /* mode for the created maildir */ /* options for extracting parts */ - gboolean *all_parts; /* extract all parts */ - gboolean *all; /* extract all attachments */ - char *targetdir; /* where to save the attachments */ + gboolean *save_all; /* extract all parts */ + gboolean *save_attachments; /* extract all attachment parts */ + gchar *parts; /* comma-sep'd list of parts to save */ + char *targetdir; /* where to save the attachments */ }; typedef struct _MuConfigOptions MuConfigOptions; diff --git a/src/mu-msg-part.c b/src/mu-msg-part.c index 8e4f9dd5..7ff11380 100644 --- a/src/mu-msg-part.c +++ b/src/mu-msg-part.c @@ -64,8 +64,8 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata) void mu_msg_msg_part_foreach (MuMsg *msg, - MuMsgPartForeachFunc func, - gpointer user_data) + MuMsgPartForeachFunc func, + gpointer user_data) { PartData pdata; @@ -87,6 +87,7 @@ struct _SavePartData { const gchar* targetdir; gboolean overwrite; gboolean stream; + guint cookie; gboolean result; }; typedef struct _SavePartData SavePartData; @@ -112,7 +113,7 @@ save_part (GMimeObject *part, const char *filename, stream = g_mime_stream_fs_new (fd); if (!stream) { - g_warning ("%s: failed to create stream", __FUNCTION__); + g_critical ("%s: failed to create stream", __FUNCTION__); close (fd); return FALSE; } @@ -123,7 +124,7 @@ save_part (GMimeObject *part, const char *filename, wrapper = g_mime_part_get_content_object (GMIME_PART(part)); if (!wrapper) { g_object_unref (G_OBJECT(stream)); - g_warning ("%s: failed to create wrapper", __FUNCTION__); + g_critical ("%s: failed to create wrapper", __FUNCTION__); return FALSE; } @@ -145,7 +146,7 @@ part_foreach_save_cb (GMimeObject *parent, GMimeObject *part, if (spd->result || spd->wanted_idx != spd->idx++) return; - if (!GMIME_IS_PART(part)) + if (!GMIME_IS_PART(part)) /* ie., multiparts are ignored */ return; filename = g_mime_part_get_filename (GMIME_PART(part)); @@ -153,24 +154,43 @@ part_foreach_save_cb (GMimeObject *parent, GMimeObject *part, spd->result = save_part (part, filename, spd->targetdir, spd->overwrite); - } else - spd->result = FALSE; + } else { /* make up a filename */ + gchar *my_filename; + my_filename = g_strdup_printf ("%x-part-%u", + spd->cookie, + spd->wanted_idx); + spd->result = save_part (part, my_filename, + spd->targetdir, + spd->overwrite); + g_free (my_filename); + } } - - gboolean mu_msg_mime_part_save (MuMsg *msg, int wanted_idx, const char *targetdir, gboolean overwrite) { SavePartData spd; + const char *msgid; + + g_return_val_if_fail (msg, FALSE); + g_return_val_if_fail (wanted_idx >= 0, FALSE); + spd.idx = 0; spd.wanted_idx = wanted_idx; spd.targetdir = targetdir; spd.overwrite = overwrite; spd.stream = FALSE; spd.result = FALSE; - + + /* get something fairly unique for building a filename; + * normally based on the message id, but if that is not + * available, a random number. basing it on the message id + * gives makes it easy to distinguish the parts of different + * messages */ + msgid = mu_msg_get_msgid (msg); + spd.cookie = (guint)(msgid ? g_str_hash (msgid) : (guint)random()); + g_mime_message_foreach (msg->_mime_msg, (GMimeObjectForeachFunc)part_foreach_save_cb, &spd); diff --git a/src/mu-msg.c b/src/mu-msg.c index 6b439e31..79583673 100644 --- a/src/mu-msg.c +++ b/src/mu-msg.c @@ -800,6 +800,9 @@ void mu_msg_init (void) { if (!_initialized) { + + srandom (getpid()*time(NULL)); + g_mime_init(0); _initialized = TRUE; g_debug ("%s", __FUNCTION__); diff --git a/src/mu-util.c b/src/mu-util.c index 33e7cd52..e4fd04ef 100644 --- a/src/mu-util.c +++ b/src/mu-util.c @@ -166,7 +166,8 @@ mu_util_str_from_strv (const gchar **params) } int -mu_util_create_writeable_fd (const char* filename, const char* dir, gboolean overwrite) +mu_util_create_writeable_fd (const char* filename, const char* dir, + gboolean overwrite) { int fd; char *fullpath; @@ -192,3 +193,5 @@ mu_util_create_writeable_fd (const char* filename, const char* dir, gboolean ove return fd; } + +