mirror of https://github.com/djcb/mu.git
* improvements in attachment / mime-part handling:
- add mu_util_play for 'playing' (opening) attachments; depends on xdg-open - stricter check for mu extract cmdline params - don't allow overwriting unless --overwrite was specified - mu extract now has a --play option to 'play' (open) attachments - added unit test to verify --overwrite - some cleanups in attachment/mime-part handling
This commit is contained in:
parent
4ff04d7829
commit
623ec34ab8
24
configure.ac
24
configure.ac
|
@ -122,6 +122,16 @@ AC_SUBST(WEBKIT_CFLAGS)
|
||||||
AC_SUBST(WEBKIT_LIBS)
|
AC_SUBST(WEBKIT_LIBS)
|
||||||
AM_CONDITIONAL(HAVE_WEBKIT, [test "x$have_webkit" = "xyes"])
|
AM_CONDITIONAL(HAVE_WEBKIT, [test "x$have_webkit" = "xyes"])
|
||||||
|
|
||||||
|
# gio is needed for some widget/ things
|
||||||
|
PKG_CHECK_MODULES(GIO,gio-2.0,[have_gio=yes],[have_gio=no])
|
||||||
|
AS_IF([test "x$have_gio" = "xyes"],[
|
||||||
|
gio_version="`pkg-config --modversion gio-2.0`"])
|
||||||
|
AC_SUBST(GIO_CFLAGS)
|
||||||
|
AC_SUBST(GIO_LIBS)
|
||||||
|
AM_CONDITIONAL(HAVE_WEBKIT, [test "x$have_webkit" = "xyes"])
|
||||||
|
|
||||||
|
# should we build the widgets/ dir?
|
||||||
|
AM_CONDITIONAL(BUILD_WIDGETS, [test "x$have_webkit" = "xyes" -a "x$have_gio" = "xyes"])
|
||||||
|
|
||||||
# xapian?
|
# xapian?
|
||||||
AC_CHECK_PROG(XAPIAN,xapian-config,xapian-config,no)
|
AC_CHECK_PROG(XAPIAN,xapian-config,xapian-config,no)
|
||||||
|
@ -184,6 +194,13 @@ else
|
||||||
have_gtk="no"
|
have_gtk="no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# check for xdg-open
|
||||||
|
AC_PATH_PROG(XDGOPEN, [xdg-open], [], [$PATH])
|
||||||
|
AS_IF([test "x$XDGOPEN" != "x"],[
|
||||||
|
AC_DEFINE_UNQUOTED([XDGOPEN], ["$XDGOPEN"],[Path to xdg-open])],[
|
||||||
|
AC_MSG_WARN([xdg-open not found, mu cannot open attachments])])
|
||||||
|
|
||||||
|
|
||||||
# check for pmccabe
|
# check for pmccabe
|
||||||
AC_PATH_PROG([PMCCABE],[pmccabe],[pmccabe],[no])
|
AC_PATH_PROG([PMCCABE],[pmccabe],[pmccabe],[no])
|
||||||
AS_IF([test "x$PMCCABE" = "xno"],[
|
AS_IF([test "x$PMCCABE" = "xno"],[
|
||||||
|
@ -228,7 +245,11 @@ if test "x$have_gtk" != "xno"; then
|
||||||
echo "GTK+ version : $gtk_version"
|
echo "GTK+ version : $gtk_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x$have_webkit" != "xno"; then
|
if test "x$have_gio" = "xyes"; then
|
||||||
|
echo "GIO version : $gio_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$have_webkit" = "xyes"; then
|
||||||
echo "Webkit version : $webkit_version"
|
echo "Webkit version : $webkit_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -236,6 +257,7 @@ echo
|
||||||
echo "Build unit tests (glib >= 2.22) : $have_gtest"
|
echo "Build unit tests (glib >= 2.22) : $have_gtest"
|
||||||
echo "Build 'mug' (requires GTK+) : $have_gtk"
|
echo "Build 'mug' (requires GTK+) : $have_gtk"
|
||||||
echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe"
|
echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe"
|
||||||
|
echo "xdg-open : $XDGOPEN"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "Have direntry->d_ino : $use_dirent_d_ino"
|
echo "Have direntry->d_ino : $use_dirent_d_ino"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.TH MU EXTRACT 1 "October 2010" "User Manuals"
|
.TH MU EXTRACT 1 "January 2011" "User Manuals"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
|
||||||
|
@ -41,7 +41,14 @@ the current working directory.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-overwrite\fR
|
\fB\-\-overwrite\fR
|
||||||
overwrite existing files with the same name.
|
overwrite existing files with the same name; by default overwriting is not
|
||||||
|
allowed.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-\-play\fR
|
||||||
|
Try to 'play' (open) the attachment with the corresponding
|
||||||
|
application. Depends on the \fBxdg-open\fR utility.
|
||||||
|
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report bugs if you find them:
|
Please report bugs if you find them:
|
||||||
|
@ -54,4 +61,4 @@ Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
|
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
|
|
||||||
.BR mu(1)
|
.BR mu(1) xdg-open(1)
|
||||||
|
|
17
src/mu-cmd.c
17
src/mu-cmd.c
|
@ -52,7 +52,7 @@ save_numbered_parts (MuMsg *msg, MuConfig *opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mu_msg_mime_part_save
|
if (!mu_msg_mime_part_save
|
||||||
(msg, idx, opts->targetdir, opts->overwrite)) {
|
(msg, idx, opts->targetdir, opts->overwrite, opts->play)) {
|
||||||
g_warning ("failed to save MIME-part %d", idx);
|
g_warning ("failed to save MIME-part %d", idx);
|
||||||
rv = FALSE;
|
rv = FALSE;
|
||||||
break;
|
break;
|
||||||
|
@ -71,6 +71,7 @@ struct _SaveData {
|
||||||
guint saved_num;
|
guint saved_num;
|
||||||
const gchar* targetdir;
|
const gchar* targetdir;
|
||||||
gboolean overwrite;
|
gboolean overwrite;
|
||||||
|
gboolean play;
|
||||||
};
|
};
|
||||||
typedef struct _SaveData SaveData;
|
typedef struct _SaveData SaveData;
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ save_part_if (MuMsgPart *part, SaveData *sd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sd->result = mu_msg_mime_part_save (sd->msg, part->index,
|
sd->result = mu_msg_mime_part_save (sd->msg, part->index,
|
||||||
sd->targetdir, sd->overwrite);
|
sd->targetdir, sd->overwrite, sd->play);
|
||||||
if (!sd->result)
|
if (!sd->result)
|
||||||
g_warning ("failed to save MIME-part %u", part->index);
|
g_warning ("failed to save MIME-part %u", part->index);
|
||||||
else
|
else
|
||||||
|
@ -107,7 +108,7 @@ save_part_if (MuMsgPart *part, SaveData *sd)
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
save_certain_parts (MuMsg *msg, gboolean attachments_only,
|
save_certain_parts (MuMsg *msg, gboolean attachments_only,
|
||||||
const gchar *targetdir, gboolean overwrite)
|
const gchar *targetdir, gboolean overwrite, gboolean play)
|
||||||
{
|
{
|
||||||
SaveData sd;
|
SaveData sd;
|
||||||
|
|
||||||
|
@ -117,6 +118,7 @@ save_certain_parts (MuMsg *msg, gboolean attachments_only,
|
||||||
sd.attachments_only = attachments_only;
|
sd.attachments_only = attachments_only;
|
||||||
sd.overwrite = overwrite;
|
sd.overwrite = overwrite;
|
||||||
sd.targetdir = targetdir;
|
sd.targetdir = targetdir;
|
||||||
|
sd.play = play;
|
||||||
|
|
||||||
mu_msg_msg_part_foreach (msg,
|
mu_msg_msg_part_foreach (msg,
|
||||||
(MuMsgPartForeachFunc)save_part_if,
|
(MuMsgPartForeachFunc)save_part_if,
|
||||||
|
@ -155,10 +157,12 @@ save_parts (const char *path, MuConfig *opts)
|
||||||
rv = save_numbered_parts (msg, opts);
|
rv = save_numbered_parts (msg, opts);
|
||||||
else if (opts->save_attachments) /* all attachments */
|
else if (opts->save_attachments) /* all attachments */
|
||||||
rv = save_certain_parts (msg, TRUE,
|
rv = save_certain_parts (msg, TRUE,
|
||||||
opts->targetdir, opts->overwrite);
|
opts->targetdir, opts->overwrite,
|
||||||
|
opts->play);
|
||||||
else if (opts->save_all) /* all parts */
|
else if (opts->save_all) /* all parts */
|
||||||
rv = save_certain_parts (msg, FALSE,
|
rv = save_certain_parts (msg, FALSE,
|
||||||
opts->targetdir, opts->overwrite);
|
opts->targetdir, opts->overwrite,
|
||||||
|
opts->play);
|
||||||
else
|
else
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
@ -196,7 +200,6 @@ show_parts (const char* path, MuConfig *opts)
|
||||||
|
|
||||||
g_print ("MIME-parts in this message:\n");
|
g_print ("MIME-parts in this message:\n");
|
||||||
mu_msg_msg_part_foreach (msg, each_part_show, NULL);
|
mu_msg_msg_part_foreach (msg, each_part_show, NULL);
|
||||||
|
|
||||||
mu_msg_destroy (msg);
|
mu_msg_destroy (msg);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -206,7 +209,7 @@ show_parts (const char* path, MuConfig *opts)
|
||||||
static gboolean
|
static gboolean
|
||||||
check_params (MuConfig *opts)
|
check_params (MuConfig *opts)
|
||||||
{
|
{
|
||||||
if (!opts->params[1]) {
|
if (!opts->params[1] || opts->params[2]) {
|
||||||
g_warning ("usage: mu extract [options] <file>");
|
g_warning ("usage: mu extract [options] <file>");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,8 @@ config_options_group_extract(MuConfig *opts)
|
||||||
"target directory for saving", NULL},
|
"target directory for saving", NULL},
|
||||||
{"overwrite", 0, 0, G_OPTION_ARG_NONE, &opts->overwrite,
|
{"overwrite", 0, 0, G_OPTION_ARG_NONE, &opts->overwrite,
|
||||||
"overwrite existing files (false)", NULL},
|
"overwrite existing files (false)", NULL},
|
||||||
|
{"play", 0, 0, G_OPTION_ARG_NONE, &opts->play,
|
||||||
|
"try to 'play' (open) the extracted parts", NULL},
|
||||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,9 +98,10 @@ struct _MuConfig {
|
||||||
gboolean *save_all; /* extract all parts */
|
gboolean *save_all; /* extract all parts */
|
||||||
gboolean *save_attachments; /* extract all attachment parts */
|
gboolean *save_attachments; /* extract all attachment parts */
|
||||||
gchar *parts; /* comma-sep'd list of parts
|
gchar *parts; /* comma-sep'd list of parts
|
||||||
* to save */
|
* to save / open */
|
||||||
char *targetdir; /* where to save the attachments */
|
char *targetdir; /* where to save the attachments */
|
||||||
gboolean overwrite; /* should we overwrite same-named files */
|
gboolean overwrite; /* should we overwrite same-named files */
|
||||||
|
gboolean play; /* after saving, try to play (open) the attmnt */
|
||||||
};
|
};
|
||||||
typedef struct _MuConfig MuConfig;
|
typedef struct _MuConfig MuConfig;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
** Copyright (C) 2008-2010 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
** 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
|
** under the terms of the GNU General Public License as published by the
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#if HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
|
||||||
|
@ -29,169 +29,201 @@
|
||||||
#include "mu-msg-part.h"
|
#include "mu-msg-part.h"
|
||||||
|
|
||||||
struct _PartData {
|
struct _PartData {
|
||||||
unsigned _idx;
|
unsigned _idx;
|
||||||
MuMsgPartForeachFunc _func;
|
MuMsgPartForeachFunc _func;
|
||||||
gpointer _user_data;
|
gpointer _user_data;
|
||||||
};
|
};
|
||||||
typedef struct _PartData PartData;
|
typedef struct _PartData PartData;
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata)
|
part_foreach_cb (GMimeObject *parent, GMimeObject *part, PartData *pdata)
|
||||||
{
|
{
|
||||||
GMimeContentType *ct;
|
GMimeContentType *ct;
|
||||||
MuMsgPart pi;
|
MuMsgPart pi;
|
||||||
|
|
||||||
memset (&pi, 0, sizeof pi);
|
memset (&pi, 0, sizeof pi);
|
||||||
pi.index = pdata->_idx++;
|
pi.index = pdata->_idx++;
|
||||||
pi.content_id = (char*)g_mime_object_get_content_id (part);
|
pi.content_id = (char*)g_mime_object_get_content_id (part);
|
||||||
|
|
||||||
ct = g_mime_object_get_content_type (part);
|
ct = g_mime_object_get_content_type (part);
|
||||||
if (GMIME_IS_CONTENT_TYPE(ct)) {
|
if (GMIME_IS_CONTENT_TYPE(ct)) {
|
||||||
pi.type = (char*)g_mime_content_type_get_media_type (ct);
|
pi.type = (char*)g_mime_content_type_get_media_type (ct);
|
||||||
pi.subtype = (char*)g_mime_content_type_get_media_subtype (ct);
|
pi.subtype = (char*)g_mime_content_type_get_media_subtype (ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GMIME_IS_PART(part)) {
|
if (GMIME_IS_PART(part)) {
|
||||||
pi.disposition = (char*)g_mime_object_get_disposition (part);
|
pi.disposition = (char*)g_mime_object_get_disposition (part);
|
||||||
pi.file_name = (char*)g_mime_part_get_filename (GMIME_PART(part));
|
pi.file_name = (char*)g_mime_part_get_filename (GMIME_PART(part));
|
||||||
}
|
}
|
||||||
|
|
||||||
pdata->_func(&pi, pdata->_user_data);
|
pdata->_func(&pi, pdata->_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
g_return_if_fail (msg);
|
g_return_if_fail (msg);
|
||||||
g_return_if_fail (GMIME_IS_OBJECT(msg->_mime_msg));
|
g_return_if_fail (GMIME_IS_OBJECT(msg->_mime_msg));
|
||||||
|
|
||||||
pdata._idx = 0;
|
pdata._idx = 0;
|
||||||
pdata._func = func;
|
pdata._func = func;
|
||||||
pdata._user_data = user_data;
|
pdata._user_data = user_data;
|
||||||
|
|
||||||
g_mime_message_foreach (msg->_mime_msg,
|
g_mime_message_foreach (msg->_mime_msg,
|
||||||
(GMimeObjectForeachFunc)part_foreach_cb,
|
(GMimeObjectForeachFunc)part_foreach_cb,
|
||||||
&pdata);
|
&pdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct _SavePartData {
|
struct _SavePartData {
|
||||||
guint idx, wanted_idx;
|
guint idx, wanted_idx;
|
||||||
const gchar* targetdir;
|
const gchar* targetdir;
|
||||||
gboolean overwrite;
|
gboolean overwrite;
|
||||||
gboolean stream;
|
gboolean stream;
|
||||||
guint cookie;
|
guint cookie;
|
||||||
gboolean result;
|
gboolean result;
|
||||||
|
gboolean play;
|
||||||
};
|
};
|
||||||
typedef struct _SavePartData SavePartData;
|
typedef struct _SavePartData SavePartData;
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
save_part (GMimeObject *part, const char *filename,
|
write_to_stream (GMimeObject *part, int fd)
|
||||||
const char *targetdir, gboolean overwrite)
|
|
||||||
{
|
{
|
||||||
int fd, rv;
|
GMimeStream *stream;
|
||||||
GMimeDataWrapper *wrapper;
|
GMimeDataWrapper *wrapper;
|
||||||
GMimeStream *stream;
|
gboolean rv;
|
||||||
|
|
||||||
rv = TRUE;
|
stream = g_mime_stream_fs_new (fd);
|
||||||
fd = mu_util_create_writeable_fd (filename, targetdir,
|
if (!stream) {
|
||||||
overwrite);
|
g_critical ("%s: failed to create stream",__FUNCTION__);
|
||||||
if (fd == -1) {
|
return FALSE;
|
||||||
g_warning ("error saving file %s%s %s",
|
}
|
||||||
filename, errno != 0 ? ":" : "",
|
g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE);
|
||||||
errno != 0 ? strerror(errno) : "");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream = g_mime_stream_fs_new (fd);
|
wrapper = g_mime_part_get_content_object (GMIME_PART(part));
|
||||||
if (!stream) {
|
if (!wrapper) {
|
||||||
g_critical ("%s: failed to create stream", __FUNCTION__);
|
g_critical ("%s: failed to create wrapper", __FUNCTION__);
|
||||||
close (fd);
|
g_object_unref (stream);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
g_object_ref (part); /* FIXME: otherwise, the unrefs below
|
||||||
|
* give errors...*/
|
||||||
|
|
||||||
/* GMimeStream will close the fd */
|
rv = g_mime_data_wrapper_write_to_stream (wrapper, stream);
|
||||||
g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), TRUE);
|
if (!rv)
|
||||||
|
g_critical ("%s: failed to write to stream", __FUNCTION__);
|
||||||
|
|
||||||
if (!(wrapper = g_mime_part_get_content_object (GMIME_PART(part)))) {
|
g_object_unref (wrapper);
|
||||||
g_object_unref (G_OBJECT(stream));
|
g_object_unref (stream);
|
||||||
g_critical ("%s: failed to create wrapper", __FUNCTION__);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = g_mime_data_wrapper_write_to_stream (wrapper, stream);
|
return rv;
|
||||||
g_object_unref (G_OBJECT(stream));
|
}
|
||||||
|
|
||||||
return rv == -1 ? FALSE : TRUE;
|
|
||||||
|
static gboolean
|
||||||
|
save_part (GMimeObject *part, const char *filename,
|
||||||
|
const char *targetdir, gboolean overwrite, gboolean tryplay)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *fullpath;
|
||||||
|
gboolean rv;
|
||||||
|
|
||||||
|
fullpath = g_strdup_printf ("%s%s%s",
|
||||||
|
targetdir ? targetdir : "",
|
||||||
|
targetdir ? G_DIR_SEPARATOR_S : "",
|
||||||
|
filename);
|
||||||
|
|
||||||
|
fd = mu_util_create_writeable_fd (fullpath, 0600, overwrite);
|
||||||
|
if (fd == -1) {
|
||||||
|
g_free (fullpath);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = write_to_stream (part, fd);
|
||||||
|
if (close (fd) != 0)
|
||||||
|
g_warning ("%s: failed to close %s: %s", __FUNCTION__,
|
||||||
|
fullpath, strerror(errno));
|
||||||
|
|
||||||
|
if (rv && tryplay) {
|
||||||
|
rv = mu_util_play (fullpath);
|
||||||
|
if (!rv)
|
||||||
|
g_warning ("%s: failed to play %s", __FUNCTION__,
|
||||||
|
fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
part_foreach_save_cb (GMimeObject *parent, GMimeObject *part,
|
part_foreach_save_cb (GMimeObject *parent, GMimeObject *part,
|
||||||
SavePartData *spd)
|
SavePartData *spd)
|
||||||
{
|
{
|
||||||
const gchar* filename;
|
const gchar* filename;
|
||||||
|
|
||||||
/* did we find the right part yet? */
|
/* did we find the right part yet? */
|
||||||
if (spd->result || spd->wanted_idx != spd->idx++)
|
if (spd->result || spd->wanted_idx != spd->idx++)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!GMIME_IS_PART(part)) /* ie., multiparts are ignored */
|
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));
|
||||||
if (filename) {
|
if (filename) {
|
||||||
spd->result = save_part (part, filename,
|
spd->result = save_part (part, filename,
|
||||||
spd->targetdir,
|
spd->targetdir,
|
||||||
spd->overwrite);
|
spd->overwrite,
|
||||||
} else { /* make up a filename */
|
spd->play);
|
||||||
gchar *my_filename;
|
} else { /* make up a filename */
|
||||||
my_filename = g_strdup_printf ("%x-part-%u",
|
gchar *my_filename;
|
||||||
spd->cookie,
|
my_filename = g_strdup_printf ("%x-part-%u",
|
||||||
spd->wanted_idx);
|
spd->cookie,
|
||||||
spd->result = save_part (part, my_filename,
|
spd->wanted_idx);
|
||||||
spd->targetdir,
|
spd->result = save_part (part, my_filename,
|
||||||
spd->overwrite);
|
spd->targetdir,
|
||||||
g_free (my_filename);
|
spd->overwrite,
|
||||||
}
|
spd->play);
|
||||||
|
g_free (my_filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
mu_msg_mime_part_save (MuMsg *msg, unsigned wanted_idx,
|
mu_msg_mime_part_save (MuMsg *msg, unsigned wanted_idx,
|
||||||
const char *targetdir, gboolean overwrite)
|
const char *targetdir, gboolean overwrite, gboolean play)
|
||||||
{
|
{
|
||||||
SavePartData spd;
|
SavePartData spd;
|
||||||
const char *msgid;
|
const char *msgid;
|
||||||
|
|
||||||
g_return_val_if_fail (msg, FALSE);
|
g_return_val_if_fail (msg, 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; /* not used yet */
|
spd.stream = FALSE; /* not used yet */
|
||||||
spd.result = FALSE;
|
spd.result = FALSE;
|
||||||
|
spd.play = play;
|
||||||
|
|
||||||
/* get something fairly unique for building a filename;
|
/* get something fairly unique for building a filename;
|
||||||
* normally based on the message id, but if that is not
|
* normally based on the message id, but if that is not
|
||||||
* available, a random number. basing it on the message id
|
* available, a random number. basing it on the message id
|
||||||
* gives makes it easy to distinguish the parts of different
|
* gives makes it easy to distinguish the parts of different
|
||||||
* messages */
|
* messages */
|
||||||
msgid = mu_msg_get_msgid (msg);
|
msgid = mu_msg_get_msgid (msg);
|
||||||
spd.cookie = (guint)(msgid ? g_str_hash (msgid) : (guint)random());
|
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);
|
||||||
return spd.result;
|
return spd.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/mu-msg.h
16
src/mu-msg.h
|
@ -27,18 +27,6 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/* what kind of message is this; use by the indexer */
|
|
||||||
enum _MuMsgStatus {
|
|
||||||
MU_MSG_STATUS_NEW, /* message is new */
|
|
||||||
MU_MSG_STATUS_UPDATE, /* message is to be updated */
|
|
||||||
MU_MSG_STATUS_CLEANUP, /* message is to be cleaned up from db */
|
|
||||||
MU_MSG_STATUS_CLEANED_UP, /* message has been cleaned up from db */
|
|
||||||
MU_MSG_STATUS_EXISTS, /* message exists (will not be cleaned up) */
|
|
||||||
MU_MSG_STATUS_UPTODATE /* message is up-to-date */
|
|
||||||
};
|
|
||||||
typedef enum _MuMsgStatus MuMsgStatus;
|
|
||||||
|
|
||||||
|
|
||||||
struct _MuMsg;
|
struct _MuMsg;
|
||||||
typedef struct _MuMsg MuMsg;
|
typedef struct _MuMsg MuMsg;
|
||||||
|
|
||||||
|
@ -131,11 +119,13 @@ const char* mu_msg_get_summary (MuMsg *msg, size_t max_lines);
|
||||||
* @param wanted_idx index of the attachment you want to save
|
* @param wanted_idx index of the attachment you want to save
|
||||||
* @param targetdir filesystem directory to save the attachment
|
* @param targetdir filesystem directory to save the attachment
|
||||||
* @param overwrite existing files?
|
* @param overwrite existing files?
|
||||||
|
* @param try to 'play' (open) the saved mime-part after saving
|
||||||
*
|
*
|
||||||
* @return TRUE if saving succeeded, FALSE otherwise
|
* @return TRUE if saving succeeded, FALSE otherwise
|
||||||
*/
|
*/
|
||||||
gboolean mu_msg_mime_part_save (MuMsg *msg, unsigned wanted_idx,
|
gboolean mu_msg_mime_part_save (MuMsg *msg, unsigned wanted_idx,
|
||||||
const char *targetdir, gboolean overwrite);
|
const char *targetdir, gboolean overwrite,
|
||||||
|
gboolean tryplay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the sender (From:) of this message
|
* get the sender (From:) of this message
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
**
|
**
|
||||||
** Copyright (C) 2008-2010 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or modify
|
** 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
|
** it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#if HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
|
||||||
|
@ -248,33 +248,59 @@ mu_util_str_from_strv (const gchar **params)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
mu_util_create_writeable_fd (const char* filename, const char* dir,
|
mu_util_create_writeable_fd (const char* path, mode_t mode,
|
||||||
gboolean overwrite)
|
gboolean overwrite)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
char *fullpath;
|
|
||||||
|
|
||||||
errno = 0; /* clear! */
|
errno = 0; /* clear! */
|
||||||
g_return_val_if_fail (filename, -1);
|
g_return_val_if_fail (path, -1);
|
||||||
|
|
||||||
fullpath = g_strdup_printf ("%s%s%s",
|
|
||||||
dir ? dir : "",
|
|
||||||
dir ? G_DIR_SEPARATOR_S : "",
|
|
||||||
filename);
|
|
||||||
|
|
||||||
if (overwrite)
|
if (overwrite)
|
||||||
fd = open (fullpath, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
fd = open (path, O_WRONLY|O_CREAT|O_TRUNC, mode);
|
||||||
else
|
else
|
||||||
fd = open (fullpath, O_WRONLY|O_CREAT, 0644);
|
fd = open (path, O_WRONLY|O_CREAT|O_EXCL, mode);
|
||||||
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
g_debug ("%s: cannot open %s for writing: %s",
|
g_warning ("%s: cannot open %s for writing: %s",
|
||||||
__FUNCTION__, fullpath, strerror(errno));
|
__FUNCTION__, path, strerror(errno));
|
||||||
|
|
||||||
g_free (fullpath);
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mu_util_play (const char *path)
|
||||||
|
{
|
||||||
|
#ifndef XDGOPEN
|
||||||
|
g_warning ("opening files not supported (xdg-open missing)");
|
||||||
|
return FALSE;
|
||||||
|
#else
|
||||||
|
gboolean rv;
|
||||||
|
GError *err;
|
||||||
|
const gchar *argv[3];
|
||||||
|
|
||||||
|
g_return_val_if_fail (path, FALSE);
|
||||||
|
g_return_val_if_fail (access (path, R_OK) == 0, FALSE);
|
||||||
|
|
||||||
|
argv[0] = XDGOPEN;
|
||||||
|
argv[1] = path;
|
||||||
|
argv[2] = NULL;
|
||||||
|
|
||||||
|
err = NULL;
|
||||||
|
rv = g_spawn_async (NULL, (gchar**)&argv, NULL, 0,
|
||||||
|
NULL, NULL, NULL, &err);
|
||||||
|
|
||||||
|
if (!rv) {
|
||||||
|
g_warning ("failed to spawn xdg-open: %s",
|
||||||
|
err->message ? err->message : "error");
|
||||||
|
g_error_free (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
#endif /*XDGOPEN*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char
|
unsigned char
|
||||||
mu_util_get_dtype_with_lstat (const char *path)
|
mu_util_get_dtype_with_lstat (const char *path)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h> /* for mode_t */
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -100,19 +101,30 @@ gboolean mu_util_check_dir (const gchar* path, gboolean readable,
|
||||||
* create a writeable file and return its file descriptor (which
|
* create a writeable file and return its file descriptor (which
|
||||||
* you'll need to close(2) when done with it.)
|
* you'll need to close(2) when done with it.)
|
||||||
*
|
*
|
||||||
* @param filename the filename
|
* @param path the full path of the file to create
|
||||||
* @param dir the target directory, or NULL for the current
|
* @param the mode to open (ie. 0644 or 0600 etc., see chmod(3)
|
||||||
* @param overwrite should we allow for overwriting existing files?
|
* @param overwrite should we allow for overwriting existing files?
|
||||||
*
|
*
|
||||||
* @return a file descriptor, or -1 in case of error. If it's a fily
|
* @return a file descriptor, or -1 in case of error. If it's a file
|
||||||
* system error, 'errno' may have more info. use 'close()' when done
|
* system error, 'errno' may contain more info. use 'close()' when done
|
||||||
* with the file descriptor
|
* with the file descriptor
|
||||||
*/
|
*/
|
||||||
int mu_util_create_writeable_fd (const char* filename, const char* dir,
|
int mu_util_create_writeable_fd (const char* path, mode_t mode,
|
||||||
gboolean overwrite)
|
gboolean overwrite)
|
||||||
G_GNUC_WARN_UNUSED_RESULT;
|
G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try to 'play' (ie., open with it's associated program) a
|
||||||
|
* file. depends on xdg-open to do the actual opening
|
||||||
|
*
|
||||||
|
* @param path full path of the file to open
|
||||||
|
*
|
||||||
|
* @return TRUE if it succeeded, FALSE otherwise
|
||||||
|
*/
|
||||||
|
gboolean mu_util_play (const char *path);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the version of the xapian database (ie., the version of the
|
* get the version of the xapian database (ie., the version of the
|
||||||
* 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION,
|
* 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION,
|
||||||
|
|
|
@ -307,6 +307,51 @@ test_mu_extract_03 (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_mu_extract_04 (void)
|
||||||
|
{
|
||||||
|
gchar *cmdline, *output, *erroutput, *tmpdir;
|
||||||
|
|
||||||
|
tmpdir = test_mu_common_get_random_tmpdir();
|
||||||
|
|
||||||
|
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||||
|
|
||||||
|
cmdline = g_strdup_printf ("%s extract -a --target-dir=%s %s%cFoo%ccur%cmail5",
|
||||||
|
MU_PROGRAM, tmpdir,
|
||||||
|
MU_TESTMAILDIR2, G_DIR_SEPARATOR,
|
||||||
|
G_DIR_SEPARATOR, G_DIR_SEPARATOR);
|
||||||
|
|
||||||
|
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput, NULL, NULL));
|
||||||
|
g_assert_cmpstr (output, ==, "");
|
||||||
|
g_assert_cmpstr (erroutput, ==, "");
|
||||||
|
g_free (erroutput);
|
||||||
|
g_free (output);
|
||||||
|
|
||||||
|
/* now, it should fail, because we don't allow overwrites without --overwrite */
|
||||||
|
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput, NULL, NULL));
|
||||||
|
g_assert_cmpstr (output, ==, "");
|
||||||
|
g_assert_cmpstr (erroutput, !=, "");
|
||||||
|
g_free (erroutput);
|
||||||
|
g_free (output);
|
||||||
|
|
||||||
|
g_free (cmdline);
|
||||||
|
/* this should work now, because we have specified --overwrite */
|
||||||
|
cmdline = g_strdup_printf ("%s extract -a --overwrite "
|
||||||
|
"--target-dir=%s %s%cFoo%ccur%cmail5",
|
||||||
|
MU_PROGRAM, tmpdir, MU_TESTMAILDIR2, G_DIR_SEPARATOR,
|
||||||
|
G_DIR_SEPARATOR, G_DIR_SEPARATOR);
|
||||||
|
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput, NULL, NULL));
|
||||||
|
g_assert_cmpstr (output, ==, "");
|
||||||
|
g_assert_cmpstr (erroutput, ==, "");
|
||||||
|
g_free (erroutput);
|
||||||
|
g_free (output);
|
||||||
|
|
||||||
|
g_free (tmpdir);
|
||||||
|
g_free (cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_mu_view_01 (void)
|
test_mu_view_01 (void)
|
||||||
{
|
{
|
||||||
|
@ -384,11 +429,13 @@ main (int argc, char *argv[])
|
||||||
g_test_add_func ("/mu-cmd/test-mu-extract-01", test_mu_extract_01);
|
g_test_add_func ("/mu-cmd/test-mu-extract-01", test_mu_extract_01);
|
||||||
g_test_add_func ("/mu-cmd/test-mu-extract-02", test_mu_extract_02);
|
g_test_add_func ("/mu-cmd/test-mu-extract-02", test_mu_extract_02);
|
||||||
g_test_add_func ("/mu-cmd/test-mu-extract-03", test_mu_extract_03);
|
g_test_add_func ("/mu-cmd/test-mu-extract-03", test_mu_extract_03);
|
||||||
|
g_test_add_func ("/mu-cmd/test-mu-extract-04", test_mu_extract_04);
|
||||||
g_test_add_func ("/mu-cmd/test-mu-view-01", test_mu_view_01);
|
g_test_add_func ("/mu-cmd/test-mu-view-01", test_mu_view_01);
|
||||||
g_test_add_func ("/mu-cmd/test-mu-mkdir-01", test_mu_mkdir_01);
|
g_test_add_func ("/mu-cmd/test-mu-mkdir-01", test_mu_mkdir_01);
|
||||||
|
|
||||||
g_log_set_handler (NULL,
|
g_log_set_handler (NULL,
|
||||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING|
|
||||||
|
G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||||
(GLogFunc)black_hole, NULL);
|
(GLogFunc)black_hole, NULL);
|
||||||
|
|
||||||
mu_msg_gmime_init ();
|
mu_msg_gmime_init ();
|
||||||
|
|
Loading…
Reference in New Issue