* 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"
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 : "<none>",
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;
}

View File

@ -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 }
};

View File

@ -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;

View File

@ -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);

View File

@ -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__);

View File

@ -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;
}