mirror of https://github.com/djcb/mu.git
* update attachment-extraction (ie., 'mu extract'); still WIP, but basics are working
This commit is contained in:
parent
a7a08dde7f
commit
15ad934d81
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue