* update attachment-extraction (ie., 'mu extract'); still WIP, but basics are working

This commit is contained in:
Dirk-Jan C. Binnema 2010-08-29 22:13:06 +03:00
parent a7a08dde7f
commit 15ad934d81
6 changed files with 183 additions and 54 deletions

View File

@ -29,27 +29,131 @@
#include "mu-cmd.h" #include "mu-cmd.h"
static gboolean 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; MuMsg* msg;
gboolean rv;
msg = mu_msg_new (path, NULL); msg = mu_msg_new (path, NULL);
if (!msg) if (!msg)
return FALSE; 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); mu_msg_destroy (msg);
return TRUE; return rv;
} }
static void 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->index,
part->file_name ? part->file_name : "<none>", part->file_name ? part->file_name : "<none>",
part->type ? part->type : "", part->type ? part->type : "",
@ -59,7 +163,7 @@ each_part (MuMsgPart *part, gpointer user_data)
static gboolean static gboolean
show_parts (const char* path) show_parts (const char* path, MuConfigOptions *opts)
{ {
MuMsg* msg; MuMsg* msg;
@ -67,7 +171,8 @@ show_parts (const char* path)
if (!msg) if (!msg)
return FALSE; 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); 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 (opts, FALSE);
g_return_val_if_fail (mu_cmd_equals (opts, "extract"), FALSE); g_return_val_if_fail (mu_cmd_equals (opts, "extract"), FALSE);
/* note: params[0] will be 'view' */ if (!opts->params[1]) {
if (!opts->params[0] || !opts->params[1]) {
g_warning ("missing mail file to extract something from"); g_warning ("missing mail file to extract something from");
return FALSE; return FALSE;
} }
mu_msg_init();
if (opts->save_attachments && opts->save_all) {
rv = TRUE; g_warning ("only one of --save-attachments and --save-all is allowed");
if (!opts->params[2]) /* no explicit part, show, don't save */ return FALSE;
rv = show_parts (opts->params[1]); }
else {
int i; if ((opts->save_attachments || opts->save_all) && opts->parts) {
for (i = 2; opts->params[i]; ++i) { g_warning ("with --save-attachments/--save-all, parts should not be specified");
unsigned idx = atoi (opts->params[i]); return FALSE;
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;
}
}
} }
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; return rv;
} }

View File

@ -117,8 +117,7 @@ config_options_group_index (MuConfigOptions *opts)
"don't clean up the database after indexing", NULL}, "don't clean up the database after indexing", NULL},
{ NULL, 0, 0, 0, NULL, NULL, NULL } { NULL, 0, 0, 0, NULL, NULL, NULL }
}; };
og = g_option_group_new ("index", og = g_option_group_new ("index",
"options for the 'index' command", "options for the 'index' command",
"", NULL, NULL); "", NULL, NULL);
@ -211,11 +210,13 @@ config_options_group_extract (MuConfigOptions *opts)
{ {
GOptionGroup *og; GOptionGroup *og;
GOptionEntry entries[] = { GOptionEntry entries[] = {
{"all", 0, 0, G_OPTION_ARG_NONE, &opts->all, {"save-attachments", 'a', 0, G_OPTION_ARG_NONE, &opts->save_attachments,
"extract all attachments", NULL}, "save all attachments", NULL},
{"all-parts", 0, 0, G_OPTION_ARG_NONE, &opts->all_parts, {"save-all", 0, 0, G_OPTION_ARG_NONE, &opts->save_all,
"extract all parts (incl. non-attachments)", NULL}, "save all parts (incl. non-attachments)", NULL},
{"target-dir", 0, 0, G_OPTION_ARG_STRING, &opts->targetdir, {"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}, "target directory for saving", NULL},
{ NULL, 0, 0, 0, NULL, NULL, NULL } { NULL, 0, 0, 0, NULL, NULL, NULL }
}; };

View File

@ -61,9 +61,10 @@ struct _MuConfigOptions {
mode_t dirmode; /* mode for the created maildir */ mode_t dirmode; /* mode for the created maildir */
/* options for extracting parts */ /* options for extracting parts */
gboolean *all_parts; /* extract all parts */ gboolean *save_all; /* extract all parts */
gboolean *all; /* extract all attachments */ gboolean *save_attachments; /* extract all attachment parts */
char *targetdir; /* where to save the attachments */ gchar *parts; /* comma-sep'd list of parts to save */
char *targetdir; /* where to save the attachments */
}; };
typedef struct _MuConfigOptions MuConfigOptions; typedef struct _MuConfigOptions MuConfigOptions;

View File

@ -64,8 +64,8 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata)
void void
mu_msg_msg_part_foreach (MuMsg *msg, mu_msg_msg_part_foreach (MuMsg *msg,
MuMsgPartForeachFunc func, MuMsgPartForeachFunc func,
gpointer user_data) gpointer user_data)
{ {
PartData pdata; PartData pdata;
@ -87,6 +87,7 @@ struct _SavePartData {
const gchar* targetdir; const gchar* targetdir;
gboolean overwrite; gboolean overwrite;
gboolean stream; gboolean stream;
guint cookie;
gboolean result; gboolean result;
}; };
typedef struct _SavePartData SavePartData; typedef struct _SavePartData SavePartData;
@ -112,7 +113,7 @@ save_part (GMimeObject *part, const char *filename,
stream = g_mime_stream_fs_new (fd); stream = g_mime_stream_fs_new (fd);
if (!stream) { if (!stream) {
g_warning ("%s: failed to create stream", __FUNCTION__); g_critical ("%s: failed to create stream", __FUNCTION__);
close (fd); close (fd);
return FALSE; return FALSE;
} }
@ -123,7 +124,7 @@ save_part (GMimeObject *part, const char *filename,
wrapper = g_mime_part_get_content_object (GMIME_PART(part)); wrapper = g_mime_part_get_content_object (GMIME_PART(part));
if (!wrapper) { if (!wrapper) {
g_object_unref (G_OBJECT(stream)); g_object_unref (G_OBJECT(stream));
g_warning ("%s: failed to create wrapper", __FUNCTION__); g_critical ("%s: failed to create wrapper", __FUNCTION__);
return FALSE; return FALSE;
} }
@ -145,7 +146,7 @@ part_foreach_save_cb (GMimeObject *parent, GMimeObject *part,
if (spd->result || spd->wanted_idx != spd->idx++) if (spd->result || spd->wanted_idx != spd->idx++)
return; return;
if (!GMIME_IS_PART(part)) if (!GMIME_IS_PART(part)) /* ie., multiparts are ignored */
return; return;
filename = g_mime_part_get_filename (GMIME_PART(part)); 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->result = save_part (part, filename,
spd->targetdir, spd->targetdir,
spd->overwrite); spd->overwrite);
} else } else { /* make up a filename */
spd->result = FALSE; 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 gboolean
mu_msg_mime_part_save (MuMsg *msg, int wanted_idx, mu_msg_mime_part_save (MuMsg *msg, int wanted_idx,
const char *targetdir, gboolean overwrite) const char *targetdir, gboolean overwrite)
{ {
SavePartData spd; 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.idx = 0;
spd.wanted_idx = wanted_idx; spd.wanted_idx = wanted_idx;
spd.targetdir = targetdir; spd.targetdir = targetdir;
spd.overwrite = overwrite; spd.overwrite = overwrite;
spd.stream = FALSE; spd.stream = FALSE;
spd.result = 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, g_mime_message_foreach (msg->_mime_msg,
(GMimeObjectForeachFunc)part_foreach_save_cb, (GMimeObjectForeachFunc)part_foreach_save_cb,
&spd); &spd);

View File

@ -800,6 +800,9 @@ void
mu_msg_init (void) mu_msg_init (void)
{ {
if (!_initialized) { if (!_initialized) {
srandom (getpid()*time(NULL));
g_mime_init(0); g_mime_init(0);
_initialized = TRUE; _initialized = TRUE;
g_debug ("%s", __FUNCTION__); g_debug ("%s", __FUNCTION__);

View File

@ -166,7 +166,8 @@ mu_util_str_from_strv (const gchar **params)
} }
int 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; int fd;
char *fullpath; char *fullpath;
@ -192,3 +193,5 @@ mu_util_create_writeable_fd (const char* filename, const char* dir, gboolean ove
return fd; return fd;
} }