From 6355e78bc10df97e154362de98b28e43604e82c8 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sat, 21 May 2011 14:12:01 +0300 Subject: [PATCH] * implement saving mime-part by file-name, improve error handling --- src/mu-cmd-extract.c | 69 ++++++-- src/mu-msg-part.c | 400 +++++++++++++++++++++++-------------------- src/mu-msg-part.h | 26 ++- 3 files changed, 292 insertions(+), 203 deletions(-) diff --git a/src/mu-cmd-extract.c b/src/mu-cmd-extract.c index 48205b1c..baa60369 100644 --- a/src/mu-cmd-extract.c +++ b/src/mu-cmd-extract.c @@ -33,17 +33,21 @@ static gboolean save_part (MuMsg *msg, const char *targetdir, guint partidx, gboolean overwrite, gboolean play) { + GError *err; gchar *filepath; filepath = mu_msg_part_filepath (msg, targetdir, partidx); if (!filepath) { - g_warning ("%s: failed to get filepath", __FUNCTION__); + g_warning ("failed to get filepath"); return FALSE; } - - if (!mu_msg_part_save (msg, filepath, partidx, overwrite, FALSE)) { - g_warning ("%s: failed to save MIME-part %d at %s", - __FUNCTION__, partidx, filepath); + + err = NULL; + if (!mu_msg_part_save (msg, filepath, partidx, overwrite, FALSE, &err)) { + g_warning ("failed to save MIME-part: %s", + err&&err->message ? err->message : "error"); + if (err) + g_error_free (err); g_free (filepath); return FALSE; } @@ -51,6 +55,7 @@ save_part (MuMsg *msg, const char *targetdir, guint partidx, gboolean overwrite, if (play) mu_util_play (filepath, TRUE, FALSE); + g_free (filepath); return TRUE; } @@ -89,6 +94,26 @@ save_numbered_parts (MuMsg *msg, MuConfig *opts) return rv; } +static gboolean +save_part_with_filename (MuMsg *msg, const char *filename, MuConfig *opts) +{ + int idx; + + idx = mu_msg_part_find_file (msg, filename); + if (idx == -1) { + g_warning ("file '%s' not found in this message", filename); + return FALSE; + } + + if (!save_part (msg, opts->targetdir, idx, opts->overwrite, + opts->play)) + return FALSE; + + return TRUE; +} + + + struct _SaveData { gboolean attachments_only; gboolean result; @@ -126,6 +151,7 @@ save_part_if (MuMsg *msg, MuMsgPart *part, SaveData *sd) { gchar *filepath; gboolean rv; + GError *err; if (ignore_part (msg, part, sd)) return; @@ -136,10 +162,16 @@ save_part_if (MuMsg *msg, MuMsgPart *part, SaveData *sd) filepath = mu_msg_part_filepath (msg, sd->targetdir, part->index); if (!filepath) goto leave; - + + err = NULL; if (!mu_msg_part_save (msg, filepath, part->index, - sd->overwrite, FALSE)) + sd->overwrite, FALSE, &err)) { + g_warning ("failed to save MIME-part: %s", + err&&err->message ? err->message : "error"); + if (err) + g_error_free (err); goto leave; + } if (sd->play && !mu_util_play (filepath, TRUE, FALSE)) goto leave; @@ -183,7 +215,7 @@ save_certain_parts (MuMsg *msg, gboolean attachments_only, static gboolean -save_parts (const char *path, MuConfig *opts) +save_parts (const char *path, const char *filename, MuConfig *opts) { MuMsg* msg; gboolean rv; @@ -202,7 +234,9 @@ save_parts (const char *path, MuConfig *opts) /* should we save some explicit parts? */ if (opts->parts) - rv = save_numbered_parts (msg, opts); + rv = save_numbered_parts (msg, opts); + else if (filename) + rv = save_part_with_filename (msg, filename, opts); else if (opts->save_attachments) /* all attachments */ rv = save_certain_parts (msg, TRUE, opts->targetdir, opts->overwrite, @@ -257,8 +291,14 @@ show_parts (const char* path, MuConfig *opts) static gboolean check_params (MuConfig *opts) { - if (!opts->params[1] || opts->params[2]) { - g_warning ("usage: mu extract [options] "); + if (!opts->params[1] || (opts->params[2] && opts->params[3])) { + g_warning ("usage: mu extract [options] []"); + return FALSE; + } + + if (opts->params[2] && (opts->save_attachments || opts->save_all)) { + g_warning ("--save-attachments --save-all is allowed don't accept " + "a filename"); return FALSE; } @@ -289,7 +329,8 @@ mu_cmd_extract (MuConfig *opts) if (!check_params (opts)) return MU_EXITCODE_ERROR; - if (!opts->parts && + if (!opts->params[2] && + !opts->parts && !opts->save_attachments && !opts->save_all) /* show, don't save */ rv = show_parts (opts->params[1], opts); @@ -299,7 +340,9 @@ mu_cmd_extract (MuConfig *opts) g_warning ("target '%s' is not a writable directory", opts->targetdir); else - rv = save_parts (opts->params[1], opts); /* save */ + rv = save_parts (opts->params[1], + opts->params[2], + opts); /* save */ } return rv ? MU_EXITCODE_OK : MU_EXITCODE_ERROR; diff --git a/src/mu-msg-part.c b/src/mu-msg-part.c index 24de9bdd..dcdfc43c 100644 --- a/src/mu-msg-part.c +++ b/src/mu-msg-part.c @@ -1,3 +1,5 @@ +/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ + /* ** Copyright (C) 2008-2011 Dirk-Jan C. Binnema ** @@ -29,8 +31,8 @@ #include "mu-msg-part.h" struct _FindPartData { - guint idx, wanted_idx; - GMimeObject *part; + guint idx, wanted_idx; + GMimeObject *part; }; typedef struct _FindPartData FindPartData; @@ -38,32 +40,32 @@ typedef struct _FindPartData FindPartData; static void find_part_cb (GMimeObject *parent, GMimeObject *part, FindPartData *fpdata) { - if (fpdata->part || fpdata->wanted_idx != fpdata->idx++) - return; /* not yet found */ + if (fpdata->part || fpdata->wanted_idx != fpdata->idx++) + return; /* not yet found */ - fpdata->part = part; + fpdata->part = part; } static GMimeObject* find_part (MuMsg* msg, guint partidx) { - FindPartData fpdata; + FindPartData fpdata; - fpdata.wanted_idx = partidx; - fpdata.idx = 0; - fpdata.part = NULL; + fpdata.wanted_idx = partidx; + fpdata.idx = 0; + fpdata.part = NULL; - g_mime_message_foreach (msg->_file->_mime_msg, - (GMimeObjectForeachFunc)find_part_cb, - &fpdata); - return fpdata.part; + g_mime_message_foreach (msg->_file->_mime_msg, + (GMimeObjectForeachFunc)find_part_cb, + &fpdata); + return fpdata.part; } struct _PartData { - MuMsg *_msg; - unsigned _idx; - MuMsgPartForeachFunc _func; - gpointer _user_data; + MuMsg *_msg; + unsigned _idx; + MuMsgPartForeachFunc _func; + gpointer _user_data; }; typedef struct _PartData PartData; @@ -71,134 +73,141 @@ typedef struct _PartData PartData; static void part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata) { - GMimeContentType *ct; - MuMsgPart pi; + GMimeContentType *ct; + MuMsgPart pi; - memset (&pi, 0, sizeof pi); - pi.index = pdata->_idx++; - pi.content_id = (char*)g_mime_object_get_content_id (part); + memset (&pi, 0, sizeof pi); + pi.index = pdata->_idx++; + pi.content_id = (char*)g_mime_object_get_content_id (part); - ct = g_mime_object_get_content_type (part); + 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_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)); - } + 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(pdata->_msg, &pi, pdata->_user_data); + pdata->_func(pdata->_msg, &pi, pdata->_user_data); } void mu_msg_part_foreach (MuMsg *msg, MuMsgPartForeachFunc func, - gpointer user_data) + gpointer user_data) { - PartData pdata; + PartData pdata; - g_return_if_fail (msg); - g_return_if_fail (GMIME_IS_OBJECT(msg->_file->_mime_msg)); + g_return_if_fail (msg); + g_return_if_fail (GMIME_IS_OBJECT(msg->_file->_mime_msg)); - pdata._msg = msg; - pdata._idx = 0; - pdata._func = func; - pdata._user_data = user_data; + pdata._msg = msg; + pdata._idx = 0; + pdata._func = func; + pdata._user_data = user_data; - g_mime_message_foreach (msg->_file->_mime_msg, - (GMimeObjectForeachFunc)part_foreach_cb, - &pdata); + g_mime_message_foreach (msg->_file->_mime_msg, + (GMimeObjectForeachFunc)part_foreach_cb, + &pdata); } static gboolean write_to_stream (GMimeObject *part, int fd) { - GMimeStream *stream; - GMimeDataWrapper *wrapper; - gboolean rv; + GMimeStream *stream; + GMimeDataWrapper *wrapper; + gboolean rv; - stream = g_mime_stream_fs_new (fd); - if (!GMIME_IS_STREAM(stream)) { - g_critical ("%s: failed to create stream",__FUNCTION__); - return FALSE; - } - g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE); + stream = g_mime_stream_fs_new (fd); + if (!GMIME_IS_STREAM(stream)) { + g_critical ("%s: failed to create stream",__FUNCTION__); + return FALSE; + } + g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE); - wrapper = g_mime_part_get_content_object (GMIME_PART(part)); - if (!GMIME_IS_DATA_WRAPPER(wrapper)) { - g_critical ("%s: failed to create wrapper", __FUNCTION__); - g_object_unref (stream); - return FALSE; - } - g_object_ref (part); /* FIXME: otherwise, the unrefs below - * give errors...*/ - - rv = g_mime_data_wrapper_write_to_stream (wrapper, stream); - if (!rv) - g_critical ("%s: failed to write to stream", __FUNCTION__); - - g_object_unref (wrapper); + wrapper = g_mime_part_get_content_object (GMIME_PART(part)); + if (!GMIME_IS_DATA_WRAPPER(wrapper)) { + g_critical ("%s: failed to create wrapper", __FUNCTION__); g_object_unref (stream); + return FALSE; + } + g_object_ref (part); /* FIXME: otherwise, the unrefs below + * give errors...*/ - return rv; + rv = g_mime_data_wrapper_write_to_stream (wrapper, stream); + if (!rv) + g_critical ("%s: failed to write to stream", __FUNCTION__); + + g_object_unref (wrapper); + g_object_unref (stream); + + return rv; } gboolean save_part (GMimeObject *part, const char *fullpath, - gboolean overwrite, gboolean use_existing) + gboolean overwrite, gboolean use_existing, GError **err) { - int fd; - gboolean rv; - - /* don't try to overwrite when we already have it; useful when - * you're sure it's not a different file with the same name */ - if (use_existing && access (fullpath, F_OK) == 0) - return TRUE; - - /* ok, try to create the file */ - fd = mu_util_create_writeable_fd (fullpath, 0600, overwrite); - if (fd == -1) - return FALSE; - - rv = write_to_stream (part, fd); - if (close (fd) != 0) - g_warning ("%s: failed to close %s: %s", __FUNCTION__, - fullpath, strerror(errno)); + int fd; + gboolean rv; + /* don't try to overwrite when we already have it; useful when + * you're sure it's not a different file with the same name */ + if (use_existing && access (fullpath, F_OK) == 0) return TRUE; + + /* ok, try to create the file */ + fd = mu_util_create_writeable_fd (fullpath, 0600, overwrite); + if (fd == -1) { + g_set_error (err, 0, MU_ERROR_FILE, + "could not open '%s' for writing: %s", + fullpath, errno ? strerror(errno) : "error"); + return FALSE; + } + + rv = write_to_stream (part, fd); + if (close (fd) != 0) { + g_set_error (err, 0, MU_ERROR_FILE, + "could not close '%s': %s", + fullpath, errno ? strerror(errno) : "error"); + return FALSE; + } + + return TRUE; } gchar* mu_msg_part_filepath (MuMsg *msg, const char* targetdir, guint partidx) { - char *fname, *filepath; - GMimeObject* part; + char *fname, *filepath; + GMimeObject* part; - part = find_part (msg, partidx); - if (!part) { - g_warning ("%s: cannot find part %u", __FUNCTION__, partidx); - return NULL; - } + part = find_part (msg, partidx); + if (!part) { + g_warning ("%s: cannot find part %u", __FUNCTION__, partidx); + return NULL; + } - /* the easy case: the part has a filename */ - fname = (gchar*)g_mime_part_get_filename (GMIME_PART(part)); - if (fname) /* security: don't include any directory components... */ - fname = g_path_get_basename (fname); - else - fname = g_strdup_printf ("%x-part-%u", - g_str_hash (mu_msg_get_path (msg)), - partidx); + /* the easy case: the part has a filename */ + fname = (gchar*)g_mime_part_get_filename (GMIME_PART(part)); + if (fname) /* security: don't include any directory components... */ + fname = g_path_get_basename (fname); + else + fname = g_strdup_printf ("%x-part-%u", + g_str_hash (mu_msg_get_path (msg)), + partidx); - filepath = g_build_path (G_DIR_SEPARATOR_S, targetdir ? targetdir : "", - fname, NULL); - g_free (fname); + filepath = g_build_path (G_DIR_SEPARATOR_S, targetdir ? targetdir : "", + fname, NULL); + g_free (fname); - return filepath; + return filepath; } @@ -206,143 +215,168 @@ mu_msg_part_filepath (MuMsg *msg, const char* targetdir, guint partidx) gchar* mu_msg_part_filepath_cache (MuMsg *msg, guint partid) { - char *dirname, *filepath; - const char* path; + char *dirname, *filepath; + const char* path; - g_return_val_if_fail (msg, NULL); + g_return_val_if_fail (msg, NULL); - path = mu_msg_get_path (msg); - if (!path) - return NULL; + path = mu_msg_get_path (msg); + if (!path) + return NULL; - /* g_compute_checksum_for_string may be better, but requires - * rel. new glib (2.16) */ + /* g_compute_checksum_for_string may be better, but requires + * rel. new glib (2.16) */ dirname = g_strdup_printf ("%s%c%x%c%u", - mu_util_cache_dir(), G_DIR_SEPARATOR, - g_str_hash (path), G_DIR_SEPARATOR, - partid); + mu_util_cache_dir(), G_DIR_SEPARATOR, + g_str_hash (path), G_DIR_SEPARATOR, + partid); - if (!mu_util_create_dir_maybe (dirname, 0700)) { - g_free (dirname); - return NULL; - } - - filepath = mu_msg_part_filepath (msg, dirname, partid); + if (!mu_util_create_dir_maybe (dirname, 0700)) { g_free (dirname); - if (!filepath) - g_warning ("%s: could not get filename", __FUNCTION__); + return NULL; + } + + filepath = mu_msg_part_filepath (msg, dirname, partid); + g_free (dirname); + if (!filepath) + g_warning ("%s: could not get filename", __FUNCTION__); - return filepath; + return filepath; } gboolean mu_msg_part_save (MuMsg *msg, const char *fullpath, guint partidx, - gboolean overwrite, gboolean use_cached) + gboolean overwrite, gboolean use_cached, GError **err) { - GMimeObject *part; + GMimeObject *part; - g_return_val_if_fail (msg, FALSE); - g_return_val_if_fail (fullpath, FALSE); - g_return_val_if_fail (!overwrite||!use_cached, FALSE); + g_return_val_if_fail (msg, FALSE); + g_return_val_if_fail (fullpath, FALSE); + g_return_val_if_fail (!overwrite||!use_cached, FALSE); - part = find_part (msg, partidx); - if (!GMIME_IS_PART(part)) { - g_warning ("%s: cannot find part %u", __FUNCTION__, partidx); - return FALSE; - } + part = find_part (msg, partidx); + if (!GMIME_IS_PART(part)) { + g_set_error (err, 0, MU_ERROR_GMIME, + "cannot find part %u", partidx); + return FALSE; + } - if (!save_part (part, fullpath, overwrite, use_cached)) { - g_warning ("%s: failed to save '%s'", __FUNCTION__, fullpath); - return FALSE; - } + if (!save_part (part, fullpath, overwrite, use_cached, err)) + return FALSE; - return TRUE; + return TRUE; } - - - typedef gboolean (*MatchFunc) (GMimeObject *part, gpointer data); struct _MatchData { - MatchFunc _matcher; - gpointer _user_data; - gint _idx, _found_idx; + MatchFunc _matcher; + gpointer _user_data; + gint _idx, _found_idx; }; typedef struct _MatchData MatchData; static void part_match_foreach_cb (GMimeObject *parent, GMimeObject *part, MatchData *mdata) { - if (mdata->_found_idx < 0) - if (mdata->_matcher (part, mdata->_user_data)) - mdata->_found_idx = mdata->_idx; + if (mdata->_found_idx < 0) + if (mdata->_matcher (part, mdata->_user_data)) + mdata->_found_idx = mdata->_idx; - ++mdata->_idx; + ++mdata->_idx; } static int msg_part_find_idx (GMimeMessage *msg, MatchFunc func, gpointer user_data) { - MatchData mdata; + MatchData mdata; - g_return_val_if_fail (msg, -1); - g_return_val_if_fail (GMIME_IS_MESSAGE(msg), -1); + g_return_val_if_fail (msg, -1); + g_return_val_if_fail (GMIME_IS_MESSAGE(msg), -1); - mdata._idx = 0; - mdata._found_idx = -1; - mdata._matcher = func; - mdata._user_data = user_data; + mdata._idx = 0; + mdata._found_idx = -1; + mdata._matcher = func; + mdata._user_data = user_data; - g_mime_message_foreach (msg, - (GMimeObjectForeachFunc)part_match_foreach_cb, - &mdata); + g_mime_message_foreach (msg, + (GMimeObjectForeachFunc)part_match_foreach_cb, + &mdata); - return mdata._found_idx; + return mdata._found_idx; } static gboolean match_content_id (GMimeObject *part, const char *cid) { - return g_strcmp0 (g_mime_object_get_content_id (part), - cid) == 0 ? TRUE : FALSE; + return g_strcmp0 (g_mime_object_get_content_id (part), + cid) == 0 ? TRUE : FALSE; } int mu_msg_part_find_cid (MuMsg *msg, const char* sought_cid) { - const char* cid; + const char* cid; - g_return_val_if_fail (msg, -1L); - g_return_val_if_fail (sought_cid, -1); + g_return_val_if_fail (msg, -1L); + g_return_val_if_fail (sought_cid, -1); - cid = g_str_has_prefix (sought_cid, "cid:") ? - sought_cid + 4 : sought_cid; + cid = g_str_has_prefix (sought_cid, "cid:") ? + sought_cid + 4 : sought_cid; - return msg_part_find_idx (msg->_file->_mime_msg, - (MatchFunc)match_content_id, - (gpointer)cid); + return msg_part_find_idx (msg->_file->_mime_msg, + (MatchFunc)match_content_id, + (gpointer)cid); } +static gboolean +match_filename (GMimeObject *part, const char *sought_filename) +{ + const char *fname; + + if (!GMIME_IS_PART(part)) + return FALSE; + + fname = g_mime_part_get_filename (GMIME_PART(part)); + if (!fname) + return FALSE; + + return g_strcmp0 (fname, sought_filename) == 0 ? TRUE : FALSE; +} + + +int +mu_msg_part_find_file (MuMsg *msg, const char* sought_filename) +{ + g_return_val_if_fail (msg, -1); + g_return_val_if_fail (sought_filename, -1); + + return msg_part_find_idx (msg->_file->_mime_msg, + (MatchFunc)match_filename, + (gpointer)sought_filename); +} + + + gboolean mu_msg_part_looks_like_attachment (MuMsgPart *part, gboolean include_inline) { - g_return_val_if_fail (part, FALSE); - - if (!part->disposition) - return FALSE; - - if (g_ascii_strcasecmp (part->disposition, - GMIME_DISPOSITION_ATTACHMENT) == 0) - return TRUE; - - if (include_inline && - g_ascii_strcasecmp (part->disposition, - GMIME_DISPOSITION_INLINE) == 0) - return TRUE; + g_return_val_if_fail (part, FALSE); + if (!part->disposition) return FALSE; + + if (g_ascii_strcasecmp (part->disposition, + GMIME_DISPOSITION_ATTACHMENT) == 0) + return TRUE; + + if (include_inline && + g_ascii_strcasecmp (part->disposition, + GMIME_DISPOSITION_INLINE) == 0) + return TRUE; + + return FALSE; } diff --git a/src/mu-msg-part.h b/src/mu-msg-part.h index e7f22b11..9711865f 100644 --- a/src/mu-msg-part.h +++ b/src/mu-msg-part.h @@ -1,3 +1,5 @@ +/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ + /* ** Copyright (C) 2008-2010 Dirk-Jan C. Binnema ** @@ -101,14 +103,15 @@ gboolean mu_msg_part_looks_like_attachment (MuMsgPart *part, * @param partidx index of the attachment you want to save * @param overwrite overwrite existing files? * @param don't raise error when the file already exists + * @param err receives error information (when function returns NULL) * * @return full path to the message part saved or NULL in case or error; free with g_free */ gboolean mu_msg_part_save (MuMsg *msg, const char *filepath, guint partidx, - gboolean overwrite, gboolean use_cached); + gboolean overwrite, gboolean use_cached, GError **err); -/** +/** * get a filename for the saving the message part; try the filename * specified for the message part if any, otherwise determine a unique * name based on the partidx and the message path @@ -123,7 +126,7 @@ gchar* mu_msg_part_filepath (MuMsg *msg, const char* targetdir, guint partidx) G_GNUC_WARN_UNUSED_RESULT; -/** +/** * get a full path name for saving the message part in the cache * directory for this message; if needed, create the directory (but * not the file) @@ -137,8 +140,8 @@ gchar* mu_msg_part_filepath_cache (MuMsg *msg, guint partid) G_GNUC_WARN_UNUSED_RESULT; -/** - * get the part inede for the message part with a certain content-id +/** + * get the part index for the message part with a certain content-id * * @param msg a message * @param content_id a content-id to search @@ -147,10 +150,19 @@ gchar* mu_msg_part_filepath_cache (MuMsg *msg, guint partid) */ int mu_msg_part_find_cid (MuMsg *msg, const char* content_id); +/** + * get the (first) part index for the message part with a certain + * filename + * + * @param msg a message + * @param sought_filename filename to look for + * + * @return the part index number of the found part, or -1 if it was not found + */ +int mu_msg_part_find_file (MuMsg *msg, const char* sought_filename); -typedef void (*MuMsgPartForeachFunc) (MuMsg *, MuMsgPart *, gpointer); - +typedef void (*MuMsgPartForeachFunc) (MuMsg*, MuMsgPart*, gpointer); /** * call a function for each of the mime part in a message *