diff --git a/man/mu-server.1 b/man/mu-server.1 index 9c3bb555..d5bd6d39 100644 --- a/man/mu-server.1 +++ b/man/mu-server.1 @@ -215,8 +215,11 @@ update the database accordingly. Using the \fBview\fR command, we can all information (including the body) of a particular e-mail message. +If the optional parameter \fBextract-images\fR is \fBtrue\fR, extract images +to temp files, and include links to them in the returned s-exp. + .nf --> view docid:|msgid: +-> view docid:|msgid: [extract-images:true] <- (:view ) .fi diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index 81904c64..bd073ffc 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -743,7 +743,7 @@ output_sexp (MuMsgIter *iter, gboolean threads, ti = threads ? mu_msg_iter_get_thread_info (iter) : NULL; sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter), - ti, TRUE); + ti, TRUE, FALSE); fputs (sexp, stdout); g_free (sexp); diff --git a/src/mu-cmd-server.c b/src/mu-cmd-server.c index c963e4cb..5c52eb94 100644 --- a/src/mu-cmd-server.c +++ b/src/mu-cmd-server.c @@ -188,7 +188,7 @@ read_line_as_list (GError **err) } -const char* +static const char* get_string_from_args (GSList *args, const char *param, gboolean optional, GError **err) { @@ -215,6 +215,27 @@ get_string_from_args (GSList *args, const char *param, gboolean optional, return NULL; } +static gboolean +get_bool_from_args (GSList *args, const char *param, gboolean optional, GError **err) +{ + const char *val; + + val = get_string_from_args (args, param, optional, err); + if (err && (*err != NULL)) + return FALSE; + + if (g_strcmp0 (val, "true") == 0) + return TRUE; + + if (!val || g_strcmp0 (val, "false") == 0) + return FALSE; + + mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, + "invalid value for parameter '%s'", param); + return FALSE; + +} + #define GET_STRING_OR_ERROR_RETURN(ARGS,PARAM,SP,E) \ do { \ @@ -479,7 +500,7 @@ cmd_compose (MuStore *store, MuQuery *query, GSList *args, GError **err) print_and_clear_g_error (err); return MU_OK; } - sexp = mu_msg_to_sexp (msg, atoi(docidstr), NULL, FALSE); + sexp = mu_msg_to_sexp (msg, atoi(docidstr), NULL, FALSE, FALSE); atts = (ctype == FORWARD) ? include_attachments (msg) : NULL; mu_msg_unref (msg); } else @@ -512,7 +533,7 @@ print_sexps (MuMsgIter *iter, int maxnum) char *sexp; sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter), mu_msg_iter_get_thread_info (iter), - TRUE); + TRUE, FALSE); print_expr ("%s", sexp); g_free (sexp); ++u; @@ -846,7 +867,8 @@ do_move (MuStore *store, unsigned docid, MuMsg *msg, const char *maildir, print_and_clear_g_error (err); } - sexp = mu_msg_to_sexp (msg, docid, NULL, FALSE/*include body*/); + sexp = mu_msg_to_sexp (msg, docid, NULL, FALSE/*include body*/, + FALSE/*do not include images*/); /* note, the :move t thing is a hint to the frontend that it * could remove the particular header */ print_expr ("(:update %s :move %s)", sexp, @@ -1049,11 +1071,7 @@ cmd_remove (MuStore *store, MuQuery *query, GSList *args, GError **err) path = get_path_from_docid (store, docid, err); if (!path) { - if (err && *err) - print_and_clear_g_error (err); - else - print_error (MU_ERROR_IN_PARAMETERS, - "no path for docid"); + print_and_clear_g_error (err); return MU_OK; } @@ -1114,6 +1132,13 @@ cmd_view (MuStore *store, MuQuery *query, GSList *args, GError **err) MuMsg *msg; unsigned docid; char *sexp; + gboolean extract_images; + + extract_images = get_bool_from_args (args, "extract-images", FALSE, err); + if (err && *err) { + print_and_clear_g_error (err); + return MU_OK; + } docid = determine_docid (query, args, err); if (docid == MU_STORE_INVALID_DOCID) { @@ -1123,15 +1148,11 @@ cmd_view (MuStore *store, MuQuery *query, GSList *args, GError **err) msg = mu_store_get_msg (store, docid, err); if (!msg) { - if (err && *err) - print_and_clear_g_error (err); - else - print_error (MU_ERROR_IN_PARAMETERS, - "failed to get message"); + print_and_clear_g_error (err); return MU_OK; } - sexp = mu_msg_to_sexp (msg, docid, NULL, FALSE); + sexp = mu_msg_to_sexp (msg, docid, NULL, FALSE, extract_images); mu_msg_unref (msg); print_expr ("(:view %s)\n", sexp); diff --git a/src/mu-cmd.c b/src/mu-cmd.c index 86a975b2..491b7be8 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -48,7 +48,7 @@ view_msg_sexp (MuMsg *msg) { char *sexp; - sexp = mu_msg_to_sexp (msg, 0, NULL, FALSE); + sexp = mu_msg_to_sexp (msg, 0, NULL, FALSE, FALSE); fputs (sexp, stdout); g_free (sexp); diff --git a/src/mu-msg-sexp.c b/src/mu-msg-sexp.c index f552d9a4..8363027c 100644 --- a/src/mu-msg-sexp.c +++ b/src/mu-msg-sexp.c @@ -1,5 +1,5 @@ /* -** Copyright (C) 2011 Dirk-Jan C. Binnema +** Copyright (C) 2011-2012 Dirk-Jan C. Binnema ** ** 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 @@ -201,12 +201,40 @@ append_sexp_flags (GString *gstr, MuMsg *msg) g_free (fdata.flagstr); } +static char* +get_temp_file (MuMsg *msg, unsigned index) +{ + char *path; + GError *err; + + err = NULL; + path = mu_msg_part_filepath_cache (msg, index); + if (!mu_msg_part_save (msg, path, index, + FALSE/*overwrite*/, TRUE/*use cache*/, &err)) { + g_warning ("failed to save mime part: %s", + err->message ? err->message : "something went wrong"); + g_clear_error (&err); + g_free (path); + return NULL; + } + + return path; +} + + +struct _PartInfo { + char *parts; + gboolean want_images; +}; +typedef struct _PartInfo PartInfo; + static void -each_part (MuMsg *msg, MuMsgPart *part, gchar **parts) +each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) { const char *fname; char *name, *tmp; + char *tmpfile; fname = mu_msg_part_file_name (part); if (!fname) @@ -221,31 +249,44 @@ each_part (MuMsg *msg, MuMsgPart *part, gchar **parts) part->subtype ? part->subtype : "octet-stream", part->index); + tmpfile = NULL; + if (pinfo->want_images && g_ascii_strcasecmp (part->type, "image") == 0) { + char *tmp; + tmp = get_temp_file (msg, part->index); + if (tmp) { + tmpfile = mu_str_escape_c_literal (tmp, TRUE); + g_free (tmp); + } + } + tmp = g_strdup_printf - ("%s(:index %d :name %s :mime-type \"%s/%s\" :attachment %s :size %i)", - *parts ? *parts : "", part->index, name, + ("%s(:index %d :name %s :mime-type \"%s/%s\"%s%s :attachment %s :size %i)", + pinfo->parts ? pinfo->parts : "", part->index, name, part->type ? part->type : "application", part->subtype ? part->subtype : "octet-stream", + tmpfile ? " :temp" : "", tmpfile ? tmpfile : "", mu_msg_part_looks_like_attachment (part, TRUE) ? "t" : "nil", (int)part->size); - g_free (*parts); - *parts = tmp; + g_free (pinfo->parts); + pinfo->parts = tmp; } static void -append_sexp_parts (GString *gstr, MuMsg *msg) +append_sexp_parts (GString *gstr, MuMsg *msg, gboolean want_images) { - char *parts; + PartInfo pinfo; + + pinfo.parts = NULL; + pinfo.want_images = want_images; - parts = NULL; mu_msg_part_foreach (msg, FALSE, - (MuMsgPartForeachFunc)each_part, &parts); + (MuMsgPartForeachFunc)each_part, &pinfo); - if (parts) { - g_string_append_printf (gstr, "\t:parts (%s)\n", parts); - g_free (parts); + if (pinfo.parts) { + g_string_append_printf (gstr, "\t:parts (%s)\n", pinfo.parts); + g_free (pinfo.parts); } } @@ -254,9 +295,7 @@ append_sexp_parts (GString *gstr, MuMsg *msg) static void append_sexp_message_file_attr (GString *gstr, MuMsg *msg) { - append_sexp_parts (gstr, msg); - - append_sexp_attr_list (gstr, "references", mu_msg_get_references (msg)); + append_sexp_attr_list (gstr, "references", mu_msg_get_references (msg)); append_sexp_attr (gstr, "in-reply-to", mu_msg_get_header (msg, "In-Reply-To")); append_sexp_attr (gstr, "body-txt", @@ -285,18 +324,21 @@ append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti) char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti, - gboolean header) + gboolean header_only, gboolean extract_images) { GString *gstr; time_t t; - gstr = g_string_sized_new (header ? 1024 : 8192); + g_return_val_if_fail (msg, NULL); + g_return_val_if_fail (!(header_only && extract_images), NULL); + + gstr = g_string_sized_new (header_only ? 1024 : 8192); g_string_append (gstr, "(\n"); if (docid != 0) g_string_append_printf (gstr, "\t:docid %u\n", docid); - if (!header) /* force loading of file... should do this a bit + if (!header_only) /* force loading of file... should do this a bit * more elegantly */ mu_msg_get_header (msg, "Reply-To"); @@ -327,8 +369,10 @@ mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti, * * file attr things can only be gotten from the file (ie., mu * view), not from the database (mu find). */ - if (!header) + if (!header_only) { append_sexp_message_file_attr (gstr, msg); + append_sexp_parts (gstr, msg, extract_images); + } g_string_append (gstr, ")\n"); diff --git a/src/mu-msg.h b/src/mu-msg.h index 0a538c47..6a846f83 100644 --- a/src/mu-msg.h +++ b/src/mu-msg.h @@ -390,15 +390,17 @@ struct _MuMsgIterThreadInfo; * @param msg a valid message * @param docid the docid for this message, or 0 * @param ti thread info for the current message, or NULL - * @param dbonly if TRUE, only include message fields which can be + * @param headers if TRUE, only include message fields which can be * obtained from the database (this is much faster if the MuMsg is * database-backed, so no file needs to be opened) + * @param extract_images if TRUE, extract image attachments as temporary + * files and include links to those in the sexp * * @return a string with the sexp (free with g_free) or NULL in case of error */ char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const struct _MuMsgIterThreadInfo *ti, - gboolean dbonly); + gboolean headers_only, gboolean extract_images); /** * move a message to another maildir; note that this does _not_ update