* 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:
Dirk-Jan C. Binnema 2011-01-09 14:55:25 +02:00
parent 4ff04d7829
commit 623ec34ab8
10 changed files with 305 additions and 163 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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*/
@ -89,48 +89,77 @@ struct _SavePartData {
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;
GMimeDataWrapper *wrapper;
GMimeStream *stream; GMimeStream *stream;
GMimeDataWrapper *wrapper;
rv = TRUE; gboolean rv;
fd = mu_util_create_writeable_fd (filename, targetdir,
overwrite);
if (fd == -1) {
g_warning ("error saving file %s%s %s",
filename, errno != 0 ? ":" : "",
errno != 0 ? strerror(errno) : "");
return FALSE;
}
stream = g_mime_stream_fs_new (fd); stream = g_mime_stream_fs_new (fd);
if (!stream) { if (!stream) {
g_critical ("%s: failed to create stream", __FUNCTION__); g_critical ("%s: failed to create stream",__FUNCTION__);
close (fd);
return FALSE; return FALSE;
} }
g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE);
/* GMimeStream will close the fd */ wrapper = g_mime_part_get_content_object (GMIME_PART(part));
g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), TRUE); if (!wrapper) {
if (!(wrapper = g_mime_part_get_content_object (GMIME_PART(part)))) {
g_object_unref (G_OBJECT(stream));
g_critical ("%s: failed to create wrapper", __FUNCTION__); g_critical ("%s: failed to create wrapper", __FUNCTION__);
g_object_unref (stream);
return FALSE; return FALSE;
} }
g_object_ref (part); /* FIXME: otherwise, the unrefs below
* give errors...*/
rv = g_mime_data_wrapper_write_to_stream (wrapper, stream); rv = g_mime_data_wrapper_write_to_stream (wrapper, stream);
g_object_unref (G_OBJECT(stream)); if (!rv)
g_critical ("%s: failed to write to stream", __FUNCTION__);
return rv == -1 ? FALSE : TRUE; g_object_unref (wrapper);
g_object_unref (stream);
return rv;
}
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;
} }
@ -152,7 +181,8 @@ part_foreach_save_cb (GMimeObject *parent, GMimeObject *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,
spd->play);
} else { /* make up a filename */ } else { /* make up a filename */
gchar *my_filename; gchar *my_filename;
my_filename = g_strdup_printf ("%x-part-%u", my_filename = g_strdup_printf ("%x-part-%u",
@ -160,14 +190,15 @@ part_foreach_save_cb (GMimeObject *parent, GMimeObject *part,
spd->wanted_idx); spd->wanted_idx);
spd->result = save_part (part, my_filename, spd->result = save_part (part, my_filename,
spd->targetdir, spd->targetdir,
spd->overwrite); spd->overwrite,
spd->play);
g_free (my_filename); 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;
@ -180,6 +211,7 @@ mu_msg_mime_part_save (MuMsg *msg, unsigned wanted_idx,
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

View File

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

View File

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

View File

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

View File

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