From 43b1c0fcf122e0551b2ca9a95a7250751d2958cb Mon Sep 17 00:00:00 2001 From: djcb Date: Sun, 22 Aug 2010 22:50:19 +0300 Subject: [PATCH] * WIP: rough implementation of attachment extraction --- src/mu-cmd-extract.c | 46 ++++++++++++++++-- src/mu-msg-gmime.c | 113 +++++++++++++++++++++++++++++++------------ src/mu-msg-gmime.h | 8 +-- src/mu-util.c | 31 ++++++++++++ src/mu-util.h | 16 ++++++ 5 files changed, 177 insertions(+), 37 deletions(-) diff --git a/src/mu-cmd-extract.c b/src/mu-cmd-extract.c index 06d87063..f13951a3 100644 --- a/src/mu-cmd-extract.c +++ b/src/mu-cmd-extract.c @@ -19,11 +19,31 @@ #include "config.h" +#include + #include "mu-msg-gmime.h" #include "mu-msg-str.h" #include "mu-cmd.h" +static gboolean +save_part (const char* path, unsigned idx) +{ + MuMsgGMime* msg; + + msg = mu_msg_gmime_new (path, NULL); + if (!msg) + return FALSE; + + mu_msg_gmime_mime_part_save (msg, idx, NULL, TRUE); /* FIXME */ + + mu_msg_gmime_destroy (msg); + + return TRUE; +} + + + static void each_part (MuMsgPartInfo* part, gpointer user_data) { @@ -62,14 +82,32 @@ mu_cmd_extract (MuConfigOptions *opts) /* note: params[0] will be 'view' */ if (!opts->params[0] || !opts->params[1]) { - g_printerr ("Missing files to view\n"); + g_printerr ("missing file to extract\n"); return FALSE; } - mu_msg_gmime_init(); - rv = show_parts (opts->params[1]); - + rv = FALSE; + 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_printerr ("not a valid index: %s", opts->params[i]); + rv = FALSE; + break; + } + if (!save_part (opts->params[1], idx)) { + g_printerr ("failed to save part %d of %s", idx, + opts->params[1]); + rv = FALSE; + break; + } + } + } + mu_msg_gmime_uninit(); return rv; diff --git a/src/mu-msg-gmime.c b/src/mu-msg-gmime.c index a34140e0..ac7d3e96 100644 --- a/src/mu-msg-gmime.c +++ b/src/mu-msg-gmime.c @@ -771,18 +771,6 @@ mu_msg_gmime_get_summary (MuMsgGMime *msg, size_t max_lines) } - - - -gboolean -mu_msg_gmime_mime_part_save (MuMsgGMime *msg, unsigned idx, - const char *targetdir) -{ - return TRUE; /* FIXME */ -} - - - const char* mu_msg_gmime_get_field_string (MuMsgGMime *msg, const MuMsgField* field) { @@ -984,7 +972,6 @@ mu_msg_gmime_uninit (void) } } - struct _PartData { unsigned _idx; MuMsgPartInfoForeachFunc _func; @@ -997,28 +984,22 @@ static void part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata) { GMimeContentType *ct; - GMimeContentDisposition *cd; MuMsgPartInfo pi; - - ct = g_mime_object_get_content_type (part); - - /* ignore non-MIME */ - if (!GMIME_IS_CONTENT_TYPE(ct)) { - g_debug ("not a content type!"); - return; - } memset (&pi, 0, sizeof pi); pi.index = pdata->_idx++; pi.content_id = (char*)g_mime_object_get_content_id (part); - pi.type = (char*)g_mime_content_type_get_media_type (ct); - pi.subtype = (char*)g_mime_content_type_get_media_subtype (ct); - pi.disposition = (char*)g_mime_object_get_disposition (part); - - cd = g_mime_object_get_content_disposition (part); - if (GMIME_IS_CONTENT_DISPOSITION(cd)) - pi.file_name = (char*)g_mime_content_disposition_get_parameter - (cd , "filename"); + + ct = g_mime_object_get_content_type (part); + if (GMIME_IS_CONTENT_TYPE(ct)) { + pi.type = (char*)g_mime_content_type_get_media_type (ct); + pi.subtype = (char*)g_mime_content_type_get_media_subtype (ct); + } + + if (GMIME_IS_PART(part)) { + pi.disposition = (char*)g_mime_object_get_disposition (part); + pi.file_name = (char*)g_mime_part_get_filename (GMIME_PART(part)); + } pdata->_func(&pi, pdata->_user_data); } @@ -1046,3 +1027,75 @@ mu_msg_gmime_msg_part_infos_foreach (MuMsgGMime *msg, +struct _SavePartData { + guint idx, wanted_idx; + const gchar* targetdir; + gboolean overwrite; + gboolean stream; + gboolean result; +}; +typedef struct _SavePartData SavePartData; + + +static void +part_foreach_save_cb (GMimeObject *parent, GMimeObject *part, + SavePartData *spd) +{ + const gchar* filename; + + /* did we find the right part yet? */ + if (spd->result || spd->wanted_idx != spd->idx++) + return; + + if (!GMIME_IS_PART(part)) + return; + + filename = g_mime_part_get_filename (GMIME_PART(part)); + if (filename) { + int fd, rv; + GMimeDataWrapper *wrapper; + GMimeStream *stream; + + fd = mu_util_create_writeable_file (filename, spd->targetdir, + spd->overwrite); + if (fd == -1) { + g_warning ("error saving file %s", filename); + spd->result = FALSE; + return; + } + stream = g_mime_stream_fs_new (fd); + g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), + TRUE); /* GMimeStream will close fd */ + + wrapper = g_mime_part_get_content_object (GMIME_PART(part)); + rv = g_mime_data_wrapper_write_to_stream (wrapper, stream); + + g_object_unref (G_OBJECT(stream)); + //g_object_unref (G_OBJECT(wrapper)); + + spd->result = (rv != -1); + } else + spd->result = FALSE; +} + + + +gboolean +mu_msg_gmime_mime_part_save (MuMsgGMime *msg, int wanted_idx, + const char *targetdir, gboolean overwrite) +{ + SavePartData spd; + spd.idx = 0; + spd.wanted_idx = wanted_idx; + spd.targetdir = targetdir; + spd.overwrite = overwrite; + spd.stream = FALSE; + spd.result = FALSE; + + g_mime_message_foreach (msg->_mime_msg, + (GMimeObjectForeachFunc)part_foreach_save_cb, + &spd); + + return spd.result; +} + diff --git a/src/mu-msg-gmime.h b/src/mu-msg-gmime.h index 3668c544..5b636893 100644 --- a/src/mu-msg-gmime.h +++ b/src/mu-msg-gmime.h @@ -142,13 +142,15 @@ void mu_msg_gmime_mime_part_foreach (MuMsgGMime* msg, * save a specific attachment to some targetdir * * @param msg a valid MuMsgGMime instance - * @param index index of the attachment you want to save + * @param wanted_idx index of the attachment you want to save * @param targetdir filesystem directory to save the attachment + * @param overwrite existing files? * * @return TRUE if saving succeeded, FALSE otherwise */ -gboolean mu_msg_gmime_mime_part_save (MuMsgGMime *msg, unsigned num, - const char *targetdir); +gboolean +mu_msg_gmime_mime_part_save (MuMsgGMime *msg, int wanted_idx, + const char *targetdir, gboolean overwrite); /** * get the sender (From:) of this message diff --git a/src/mu-util.c b/src/mu-util.c index 2aa37631..30862095 100644 --- a/src/mu-util.c +++ b/src/mu-util.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -154,10 +155,40 @@ mu_util_str_from_strv (const gchar **params) str = g_string_sized_new (64); /* just a guess */ for (i = 0; params[i]; ++i) { + if (i>0) g_string_append_c (str, ' '); + g_string_append (str, params[i]); } return g_string_free (str, FALSE); } + +int +mu_util_create_writeable_file (const char* filename, const char* dir, gboolean overwrite) +{ + int fd; + char *fullpath; + + errno = 0; /* clear! */ + g_return_val_if_fail (filename, -1); + + fullpath = g_strdup_printf ("%s%s%s", + dir ? dir : "", + dir ? G_DIR_SEPARATOR_S : "", + filename); + + if (overwrite) + fd = open (fullpath, O_WRONLY|O_CREAT|O_TRUNC, 0644); + else + fd = open (fullpath, O_WRONLY|O_CREAT, 0644); + + if (fd < 0) + g_debug ("%s: cannot open %s for writing: %s", + __FUNCTION__, fullpath, strerror(errno)); + + g_free (fullpath); + + return fd; +} diff --git a/src/mu-util.h b/src/mu-util.h index 08ce9ec5..62ed2f9f 100644 --- a/src/mu-util.h +++ b/src/mu-util.h @@ -70,6 +70,22 @@ gboolean mu_util_check_dir (const gchar* path, gboolean readable, gboolean writeable) G_GNUC_WARN_UNUSED_RESULT; + +/** + * create a writeable file and return its file descriptor (which + * you'll need to close(2) when done with it.) + * + * @param filename the filename + * @param dir the target directory, or NULL for the current + * @param overwrite should we allow for overwriting existing files? + * + * @return a file descriptor, or -1 in case of error. If it's a fily + * system error, 'errno' may have more info. + */ +int mu_util_create_writeable_file (const char* filename, const char* dir, + gboolean overwrite); + + /** * convert a string array in to a string, with the elements separated * by ' '