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"
|
#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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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__);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue