* refactoring: split src/ into mu/ and lib/
This commit is contained in:
244
mu/mu-cmd-cfind.c
Normal file
244
mu/mu-cmd-cfind.c
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
** Copyright (C) 2011-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mu-cmd.h"
|
||||
#include "mu-util.h"
|
||||
#include "mu-str.h"
|
||||
#include "mu-date.h"
|
||||
#include "mu-contacts.h"
|
||||
#include "mu-runtime.h"
|
||||
|
||||
static void
|
||||
print_header (MuConfigFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case MU_CONFIG_FORMAT_BBDB:
|
||||
g_print (";; -*-coding: utf-8-emacs;-*-\n"
|
||||
";;; file-version: 6\n");
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_MUTT_AB:
|
||||
g_print ("Matching addresses in the mu database:\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
each_contact_bbdb (const char *email, const char *name, time_t tstamp)
|
||||
{
|
||||
char *fname, *lname, *now, *timestamp;
|
||||
|
||||
fname = mu_str_guess_first_name (name);
|
||||
lname = mu_str_guess_last_name (name);
|
||||
now = mu_date_str ("%Y-%m-%d", time(NULL));
|
||||
timestamp = mu_date_str ("%Y-%m-%d", tstamp);
|
||||
|
||||
g_print ("[\"%s\" \"%s\" nil nil nil nil (\"%s\") "
|
||||
"((creation-date . \"%s\") (time-stamp . \"%s\")) nil]\n",
|
||||
fname, lname, email, now, timestamp);
|
||||
|
||||
g_free (now);
|
||||
g_free (timestamp);
|
||||
g_free (fname);
|
||||
g_free (lname);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_contact_mutt_alias (const char *email, const char *name)
|
||||
{
|
||||
gchar *nick;
|
||||
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
nick = mu_str_guess_nick (name);
|
||||
mu_util_print_encoded ("alias %s %s <%s>\n",
|
||||
nick, name, email);
|
||||
g_free (nick);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_contact_wl (const char *email, const char *name)
|
||||
{
|
||||
gchar *nick;
|
||||
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
nick = mu_str_guess_nick (name);
|
||||
mu_util_print_encoded ("%s \"%s\" \"%s\"\n",
|
||||
email, nick, name);
|
||||
g_free (nick);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_contact_org_contact (const char *email, const char *name)
|
||||
{
|
||||
if (name)
|
||||
mu_util_print_encoded (
|
||||
"* %s\n:PROPERTIES:\n:EMAIL: %s\n:END:\n\n",
|
||||
name, email);
|
||||
}
|
||||
|
||||
static void
|
||||
print_plain (const char *email, const char *name, gboolean color)
|
||||
{
|
||||
if (name) {
|
||||
if (color) fputs (MU_COLOR_MAGENTA, stdout);
|
||||
mu_util_fputs_encoded (name, stdout);
|
||||
fputs (" ", stdout);
|
||||
}
|
||||
|
||||
if (color)
|
||||
fputs (MU_COLOR_GREEN, stdout);
|
||||
|
||||
mu_util_fputs_encoded (email, stdout);
|
||||
|
||||
if (color)
|
||||
fputs (MU_COLOR_DEFAULT, stdout);
|
||||
|
||||
fputs ("\n", stdout);
|
||||
}
|
||||
|
||||
struct _ECData {
|
||||
MuConfigFormat format;
|
||||
gboolean color;
|
||||
};
|
||||
typedef struct _ECData ECData;
|
||||
|
||||
|
||||
static void
|
||||
each_contact (const char *email, const char *name, time_t tstamp,
|
||||
ECData *ecdata)
|
||||
{
|
||||
switch (ecdata->format) {
|
||||
case MU_CONFIG_FORMAT_MUTT_ALIAS:
|
||||
each_contact_mutt_alias (email, name);
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_MUTT_AB:
|
||||
mu_util_print_encoded ("%s\t%s\t\n",
|
||||
email, name ? name : "");
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_WL:
|
||||
each_contact_wl (email, name);
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_ORG_CONTACT:
|
||||
each_contact_org_contact (email, name);
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_BBDB:
|
||||
each_contact_bbdb (email, name, tstamp);
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_CSV:
|
||||
mu_util_print_encoded ("%s,%s\n", name ? name : "", email);
|
||||
break;
|
||||
default:
|
||||
print_plain (email, name, ecdata->color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static MuError
|
||||
run_cmd_cfind (const char* pattern, MuConfigFormat format,
|
||||
gboolean color, GError **err)
|
||||
{
|
||||
gboolean rv;
|
||||
MuContacts *contacts;
|
||||
size_t num;
|
||||
ECData ecdata;
|
||||
|
||||
ecdata.format = format;
|
||||
ecdata.color = color;
|
||||
|
||||
contacts = mu_contacts_new (mu_runtime_path(MU_RUNTIME_PATH_CONTACTS));
|
||||
if (!contacts) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_CONTACTS_CANNOT_RETRIEVE,
|
||||
"could not retrieve contacts");
|
||||
return MU_ERROR_CONTACTS_CANNOT_RETRIEVE;
|
||||
}
|
||||
|
||||
print_header (format);
|
||||
rv = mu_contacts_foreach (contacts,
|
||||
(MuContactsForeachFunc)each_contact,
|
||||
&ecdata, pattern, &num);
|
||||
mu_contacts_destroy (contacts);
|
||||
|
||||
if (num == 0) {
|
||||
g_warning ("no matching contacts found");
|
||||
return MU_ERROR_NO_MATCHES;
|
||||
}
|
||||
|
||||
return rv ? MU_OK : MU_ERROR_CONTACTS;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cfind_params_valid (MuConfig *opts)
|
||||
{
|
||||
switch (opts->format) {
|
||||
case MU_CONFIG_FORMAT_PLAIN:
|
||||
case MU_CONFIG_FORMAT_MUTT_ALIAS:
|
||||
case MU_CONFIG_FORMAT_MUTT_AB:
|
||||
case MU_CONFIG_FORMAT_WL:
|
||||
case MU_CONFIG_FORMAT_BBDB:
|
||||
case MU_CONFIG_FORMAT_CSV:
|
||||
case MU_CONFIG_FORMAT_ORG_CONTACT:
|
||||
break;
|
||||
default:
|
||||
g_warning ("invalid output format %s",
|
||||
opts->formatstr ? opts->formatstr : "<none>");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* only one pattern allowed */
|
||||
if (opts->params[1] && opts->params[2]) {
|
||||
g_warning ("usage: mu cfind [options] [<ptrn>]");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_cfind (MuConfig *opts, GError **err)
|
||||
{
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_CFIND,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
if (!cfind_params_valid (opts)) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"invalid parameters");
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
|
||||
return run_cmd_cfind (opts->params[1], opts->format,
|
||||
!opts->nocolor, err);
|
||||
}
|
||||
417
mu/mu-cmd-extract.c
Normal file
417
mu/mu-cmd-extract.c
Normal file
@ -0,0 +1,417 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2010-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mu-msg.h"
|
||||
#include "mu-msg-part.h"
|
||||
#include "mu-cmd.h"
|
||||
#include "mu-util.h"
|
||||
#include "mu-str.h"
|
||||
|
||||
static gboolean
|
||||
save_part (MuMsg *msg, const char *targetdir, guint partidx, gboolean overwrite,
|
||||
gboolean play)
|
||||
{
|
||||
GError *err;
|
||||
gchar *filepath;
|
||||
gboolean rv;
|
||||
|
||||
err = NULL;
|
||||
rv = FALSE;
|
||||
|
||||
filepath = mu_msg_part_filepath (msg, targetdir, partidx, &err);
|
||||
if (!filepath)
|
||||
goto exit;
|
||||
|
||||
if (!mu_msg_part_save (msg, filepath, partidx, overwrite, FALSE, &err))
|
||||
goto exit;
|
||||
|
||||
if (play)
|
||||
rv = mu_util_play (filepath, TRUE, FALSE, &err);
|
||||
else
|
||||
rv = TRUE;
|
||||
exit:
|
||||
if (err)
|
||||
g_warning ("error with MIME-part: %s",
|
||||
err->message);
|
||||
g_clear_error (&err);
|
||||
|
||||
g_free (filepath);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
save_numbered_parts (MuMsg *msg, MuConfig *opts)
|
||||
{
|
||||
gboolean rv;
|
||||
char **parts, **cur;
|
||||
|
||||
parts = g_strsplit (opts->parts, ",", 0);
|
||||
|
||||
for (rv = TRUE, cur = parts; cur && *cur; ++cur) {
|
||||
|
||||
unsigned idx;
|
||||
int i;
|
||||
char *endptr;
|
||||
|
||||
idx = (unsigned)(i = strtol (*cur, &endptr, 10));
|
||||
if (i < 0 || *cur == endptr) {
|
||||
g_warning ("invalid MIME-part index '%s'", *cur);
|
||||
rv = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!save_part (msg, opts->targetdir, idx, opts->overwrite,
|
||||
opts->play)) {
|
||||
g_warning ("failed to save MIME-part %d", idx);
|
||||
rv = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (parts);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static GRegex*
|
||||
anchored_regex (const char* pattern)
|
||||
{
|
||||
GRegex *rx;
|
||||
GError *err;
|
||||
gchar *anchored;
|
||||
|
||||
|
||||
anchored = g_strdup_printf
|
||||
("%s%s%s",
|
||||
pattern[0] == '^' ? "" : "^",
|
||||
pattern,
|
||||
pattern[strlen(pattern)-1] == '$' ? "" : "$");
|
||||
|
||||
err = NULL;
|
||||
rx = g_regex_new (anchored, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0,
|
||||
&err);
|
||||
g_free (anchored);
|
||||
|
||||
if (!rx) {
|
||||
g_warning ("error in regular expression '%s': %s",
|
||||
pattern, err->message ? err->message : "error");
|
||||
g_error_free (err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
save_part_with_filename (MuMsg *msg, const char *pattern, MuConfig *opts)
|
||||
{
|
||||
GSList *lst, *cur;
|
||||
GRegex *rx;
|
||||
gboolean rv;
|
||||
|
||||
/* 'anchor' the pattern with '^...$' if not already */
|
||||
rx = anchored_regex (pattern);
|
||||
if (!rx)
|
||||
return FALSE;
|
||||
|
||||
lst = mu_msg_part_find_files (msg, rx);
|
||||
g_regex_unref (rx);
|
||||
if (!lst) {
|
||||
g_warning ("no matching attachments found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (cur = lst, rv = TRUE; cur; cur = g_slist_next (cur))
|
||||
rv &= save_part (msg, opts->targetdir,
|
||||
GPOINTER_TO_UINT(cur->data),
|
||||
opts->overwrite, opts->play);
|
||||
g_slist_free (lst);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct _SaveData {
|
||||
gboolean attachments_only;
|
||||
gboolean result;
|
||||
guint saved_num;
|
||||
const gchar* targetdir;
|
||||
gboolean overwrite;
|
||||
gboolean play;
|
||||
};
|
||||
typedef struct _SaveData SaveData;
|
||||
|
||||
|
||||
static gboolean
|
||||
ignore_part (MuMsg *msg, MuMsgPart *part, SaveData *sd)
|
||||
{
|
||||
/* something went wrong somewhere; stop */
|
||||
if (!sd->result)
|
||||
return TRUE;
|
||||
|
||||
/* filter out non-attachments if only want attachments */
|
||||
if (sd->attachments_only &&
|
||||
!mu_msg_part_looks_like_attachment (part, TRUE))
|
||||
return TRUE;
|
||||
|
||||
/* ignore multiparts */
|
||||
if (part->type &&
|
||||
g_ascii_strcasecmp (part->type, "multipart") == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
save_part_if (MuMsg *msg, MuMsgPart *part, SaveData *sd)
|
||||
{
|
||||
gchar *filepath;
|
||||
gboolean rv;
|
||||
GError *err;
|
||||
|
||||
if (ignore_part (msg, part, sd))
|
||||
return;
|
||||
|
||||
rv = FALSE;
|
||||
filepath = NULL;
|
||||
|
||||
err = NULL;
|
||||
filepath = mu_msg_part_filepath (msg, sd->targetdir, part->index, &err);
|
||||
if (!filepath)
|
||||
goto exit;
|
||||
|
||||
if (!mu_msg_part_save (msg, filepath, part->index,
|
||||
sd->overwrite, FALSE, &err))
|
||||
goto exit;
|
||||
|
||||
if (sd->play)
|
||||
rv = mu_util_play (filepath, TRUE, FALSE, &err);
|
||||
else
|
||||
rv = TRUE;
|
||||
|
||||
++sd->saved_num;
|
||||
exit:
|
||||
if (err)
|
||||
g_warning ("error saving MIME part: %s", err->message);
|
||||
|
||||
g_free (filepath);
|
||||
g_clear_error (&err);
|
||||
sd->result = rv;
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_certain_parts (MuMsg *msg, gboolean attachments_only,
|
||||
const gchar *targetdir, gboolean overwrite, gboolean play)
|
||||
{
|
||||
SaveData sd;
|
||||
|
||||
sd.result = TRUE;
|
||||
sd.saved_num = 0;
|
||||
sd.attachments_only = attachments_only;
|
||||
sd.overwrite = overwrite;
|
||||
sd.targetdir = targetdir;
|
||||
sd.play = play;
|
||||
|
||||
mu_msg_part_foreach (msg, FALSE,
|
||||
(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, const char *filename, MuConfig *opts)
|
||||
{
|
||||
MuMsg* msg;
|
||||
gboolean rv;
|
||||
GError *err;
|
||||
|
||||
err = NULL;
|
||||
msg = mu_msg_new_from_file (path, NULL, &err);
|
||||
if (!msg) {
|
||||
if (err) {
|
||||
g_warning ("error: %s", err->message);
|
||||
g_error_free (err);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* note, mu_cmd_extract already checks whether what's in opts
|
||||
* is somewhat, so no need for extensive checking here */
|
||||
|
||||
/* should we save some explicit parts? */
|
||||
if (opts->parts)
|
||||
rv = save_numbered_parts (msg, opts);
|
||||
else if (filename)
|
||||
rv = save_part_with_filename (msg, filename, opts);
|
||||
else if (opts->save_attachments) /* all attachments */
|
||||
rv = save_certain_parts (msg, TRUE,
|
||||
opts->targetdir, opts->overwrite,
|
||||
opts->play);
|
||||
else if (opts->save_all) /* all parts */
|
||||
rv = save_certain_parts (msg, FALSE,
|
||||
opts->targetdir, opts->overwrite,
|
||||
opts->play);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
mu_msg_unref (msg);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#define color_maybe(C) do{ if (color) fputs ((C),stdout);}while(0)
|
||||
|
||||
static void
|
||||
each_part_show (MuMsg *msg, MuMsgPart *part, gboolean color)
|
||||
{
|
||||
/* index */
|
||||
g_print (" %u ", part->index);
|
||||
|
||||
/* filename */
|
||||
color_maybe (MU_COLOR_GREEN);
|
||||
mu_util_fputs_encoded (part->file_name ? part->file_name : "<none>",
|
||||
stdout);
|
||||
/* content-type */
|
||||
color_maybe (MU_COLOR_BLUE);
|
||||
mu_util_print_encoded (
|
||||
" %s/%s ",
|
||||
part->type ? part->type : "<none>",
|
||||
part->subtype ? part->subtype : "<none>");
|
||||
|
||||
/* disposition */
|
||||
color_maybe (MU_COLOR_MAGENTA);
|
||||
mu_util_print_encoded (
|
||||
"[%s]", part->disposition ? part->disposition : "<none>");
|
||||
|
||||
|
||||
/* size */
|
||||
if (part->size > 0) {
|
||||
color_maybe (MU_COLOR_CYAN);
|
||||
g_print (" (%s)", mu_str_size_s (part->size));
|
||||
}
|
||||
|
||||
color_maybe (MU_COLOR_DEFAULT);
|
||||
fputs ("\n", stdout);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
show_parts (const char* path, MuConfig *opts, GError **err)
|
||||
{
|
||||
MuMsg* msg;
|
||||
|
||||
msg = mu_msg_new_from_file (path, NULL, err);
|
||||
if (!msg)
|
||||
return FALSE;
|
||||
|
||||
g_print ("MIME-parts in this message:\n");
|
||||
|
||||
mu_msg_part_foreach
|
||||
(msg, FALSE, (MuMsgPartForeachFunc)each_part_show,
|
||||
GUINT_TO_POINTER(!opts->nocolor));
|
||||
|
||||
mu_msg_unref (msg);
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
check_params (MuConfig *opts)
|
||||
{
|
||||
size_t param_num;
|
||||
|
||||
param_num = mu_config_param_num (opts);
|
||||
|
||||
if (param_num < 2) {
|
||||
g_warning ("usage: mu extract [options] <file> [<pattern>]");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->save_attachments || opts->save_all)
|
||||
if (opts->parts || param_num == 3) {
|
||||
g_warning ("--save-attachments --save-all don't accept "
|
||||
"a filename pattern or --parts");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->save_attachments && opts->save_all) {
|
||||
g_warning ("only one of --save-attachments and"
|
||||
" --save-all is allowed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
MuError
|
||||
mu_cmd_extract (MuConfig *opts, GError **err)
|
||||
{
|
||||
int rv;
|
||||
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_EXTRACT,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
if (!check_params (opts)) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"error in parameters");
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!opts->params[2] && !opts->parts &&
|
||||
!opts->save_attachments && !opts->save_all)
|
||||
rv = show_parts (opts->params[1], opts, err); /* show, don't save */
|
||||
else {
|
||||
rv = mu_util_check_dir(opts->targetdir, FALSE, TRUE);
|
||||
if (!rv)
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_WRITE,
|
||||
"target '%s' is not a writable directory",
|
||||
opts->targetdir);
|
||||
else
|
||||
rv = save_parts (opts->params[1],
|
||||
opts->params[2],
|
||||
opts); /* save */
|
||||
}
|
||||
|
||||
return rv ? MU_OK : MU_ERROR;
|
||||
}
|
||||
881
mu/mu-cmd-find.c
Normal file
881
mu/mu-cmd-find.c
Normal file
@ -0,0 +1,881 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "mu-msg.h"
|
||||
#include "mu-str.h"
|
||||
#include "mu-date.h"
|
||||
#include "mu-maildir.h"
|
||||
#include "mu-index.h"
|
||||
#include "mu-query.h"
|
||||
#include "mu-msg-iter.h"
|
||||
#include "mu-bookmarks.h"
|
||||
#include "mu-runtime.h"
|
||||
|
||||
|
||||
#include "mu-util.h"
|
||||
#include "mu-cmd.h"
|
||||
#include "mu-threader.h"
|
||||
|
||||
|
||||
static gboolean output_links (MuMsgIter *iter, const char* linksdir,
|
||||
gboolean clearlinks, GError **err);
|
||||
static gboolean output_sexp (MuMsgIter *iter, gboolean threads,
|
||||
gboolean include_unreadable, GError **err);
|
||||
static gboolean output_xml (MuMsgIter *iter,gboolean include_unreadable,
|
||||
GError **err);
|
||||
static gboolean output_plain (MuMsgIter *iter, const char *fields,
|
||||
int summary_len, gboolean threads,
|
||||
gboolean color, gboolean include_unreadable,
|
||||
GError **err);
|
||||
|
||||
static gboolean
|
||||
print_xapian_query (MuQuery *xapian, const gchar *query, GError **err)
|
||||
{
|
||||
char *querystr;
|
||||
|
||||
querystr = mu_query_as_string (xapian, query, err);
|
||||
if (!querystr)
|
||||
return FALSE;
|
||||
|
||||
g_print ("%s\n", querystr);
|
||||
g_free (querystr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* returns MU_MSG_FIELD_ID_NONE if there is an error */
|
||||
static MuMsgFieldId
|
||||
sort_field_from_string (const char* fieldstr, GError **err)
|
||||
{
|
||||
MuMsgFieldId mfid;
|
||||
|
||||
mfid = mu_msg_field_id_from_name (fieldstr, FALSE);
|
||||
|
||||
/* not found? try a shortcut */
|
||||
if (mfid == MU_MSG_FIELD_ID_NONE &&
|
||||
strlen(fieldstr) == 1)
|
||||
mfid = mu_msg_field_id_from_shortcut(fieldstr[0],
|
||||
FALSE);
|
||||
if (mfid == MU_MSG_FIELD_ID_NONE)
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"not a valid sort field: '%s'\n", fieldstr);
|
||||
return mfid;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
output_query_results (MuMsgIter *iter, MuConfig *opts, GError **err)
|
||||
{
|
||||
switch (opts->format) {
|
||||
case MU_CONFIG_FORMAT_LINKS:
|
||||
return output_links (iter, opts->linksdir, opts->clearlinks, err);
|
||||
case MU_CONFIG_FORMAT_PLAIN:
|
||||
return output_plain (iter, opts->fields,
|
||||
opts->summary ? opts->summary_len : 0,
|
||||
opts->threads, !opts->nocolor,
|
||||
opts->include_unreadable, err);
|
||||
case MU_CONFIG_FORMAT_XML:
|
||||
return output_xml (iter, opts->include_unreadable, err);
|
||||
case MU_CONFIG_FORMAT_SEXP:
|
||||
return output_sexp (iter, opts->threads,
|
||||
opts->include_unreadable, err);
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static MuMsgIter*
|
||||
run_query (MuQuery *xapian, const gchar *query, MuConfig *opts,
|
||||
GError **err)
|
||||
{
|
||||
MuMsgIter *iter;
|
||||
MuMsgFieldId sortid;
|
||||
|
||||
sortid = MU_MSG_FIELD_ID_NONE;
|
||||
if (opts->sortfield) {
|
||||
sortid = sort_field_from_string (opts->sortfield, err);
|
||||
if (sortid == MU_MSG_FIELD_ID_NONE) /* error occured? */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
iter = mu_query_run (xapian, query, opts->threads, sortid,
|
||||
opts->reverse, -1, err);
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
process_query (MuQuery *xapian, const gchar *query, MuConfig *opts, GError **err)
|
||||
{
|
||||
MuMsgIter *iter;
|
||||
gboolean rv;
|
||||
|
||||
iter = run_query (xapian, query, opts, err);
|
||||
if (!iter)
|
||||
return FALSE;
|
||||
|
||||
rv = output_query_results (iter, opts, err);
|
||||
mu_msg_iter_destroy (iter);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
exec_cmd (const char *path, const char *cmd, GError **err)
|
||||
{
|
||||
gint status;
|
||||
char *cmdline, *escpath;
|
||||
gboolean rv;
|
||||
|
||||
if (access (path, R_OK) != 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_READ,
|
||||
"cannot read %s: %s", path, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
escpath = g_strescape (path, NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s %s", cmd, escpath);
|
||||
err = NULL;
|
||||
rv = g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
&status, err);
|
||||
g_free (cmdline);
|
||||
g_free (escpath);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
exec_cmd_on_query (MuQuery *xapian, const gchar *query, MuConfig *opts,
|
||||
GError **err)
|
||||
{
|
||||
MuMsgIter *iter;
|
||||
gboolean rv;
|
||||
size_t count;
|
||||
|
||||
if (!(iter = run_query (xapian, query, opts, err)))
|
||||
return FALSE;
|
||||
|
||||
for (rv = TRUE, count = 0; !mu_msg_iter_is_done (iter);
|
||||
mu_msg_iter_next(iter)) {
|
||||
const char *path;
|
||||
MuMsg *msg;
|
||||
|
||||
msg = mu_msg_iter_get_msg_floating (iter);
|
||||
if (!msg)
|
||||
continue;
|
||||
|
||||
path = mu_msg_get_path (msg);
|
||||
if (!msg) {
|
||||
g_warning ("cannot get path for msg");
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = exec_cmd (path, opts->exec, err);
|
||||
if (rv)
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"no matches for search expression");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mu_msg_iter_destroy (iter);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
format_params_valid (MuConfig *opts, GError **err)
|
||||
{
|
||||
switch (opts->format) {
|
||||
case MU_CONFIG_FORMAT_PLAIN:
|
||||
case MU_CONFIG_FORMAT_SEXP:
|
||||
case MU_CONFIG_FORMAT_LINKS:
|
||||
case MU_CONFIG_FORMAT_XML:
|
||||
case MU_CONFIG_FORMAT_XQUERY:
|
||||
break;
|
||||
default:
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"invalid output format %s",
|
||||
opts->formatstr ? opts->formatstr : "<none>");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->format == MU_CONFIG_FORMAT_LINKS && !opts->linksdir) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"missing --linksdir argument");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->linksdir && opts->format != MU_CONFIG_FORMAT_LINKS) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"--linksdir is only valid with --format=links");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
query_params_valid (MuConfig *opts, GError **err)
|
||||
{
|
||||
const gchar *xpath;
|
||||
|
||||
xpath = mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB);
|
||||
|
||||
if (mu_util_check_dir (xpath, TRUE, FALSE))
|
||||
return TRUE;
|
||||
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_READ,
|
||||
"'%s' is not a readable Xapian directory", xpath);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gchar*
|
||||
resolve_bookmark (MuConfig *opts, GError **err)
|
||||
{
|
||||
MuBookmarks *bm;
|
||||
char* val;
|
||||
const gchar *bmfile;
|
||||
|
||||
bmfile = mu_runtime_path (MU_RUNTIME_PATH_BOOKMARKS);
|
||||
bm = mu_bookmarks_new (bmfile);
|
||||
if (!bm) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_OPEN,
|
||||
"failed to open bookmarks file '%s'", bmfile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
val = (gchar*)mu_bookmarks_lookup (bm, opts->bookmark);
|
||||
if (!val)
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"bookmark '%s' not found", opts->bookmark);
|
||||
else
|
||||
val = g_strdup (val);
|
||||
|
||||
mu_bookmarks_destroy (bm);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gchar*
|
||||
str_quoted_from_strv (const gchar **params)
|
||||
{
|
||||
GString *str;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (params, NULL);
|
||||
|
||||
if (!params[0])
|
||||
return g_strdup ("");
|
||||
|
||||
str = g_string_sized_new (64); /* just a guess */
|
||||
|
||||
for (i = 0; params[i]; ++i) {
|
||||
|
||||
if (i > 0)
|
||||
g_string_append_c (str, ' ');
|
||||
|
||||
g_string_append_c (str, '"');
|
||||
g_string_append (str, params[i]);
|
||||
g_string_append_c (str, '"');
|
||||
}
|
||||
|
||||
return g_string_free (str, FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gchar*
|
||||
get_query (MuConfig *opts, GError **err)
|
||||
{
|
||||
gchar *query, *bookmarkval;
|
||||
|
||||
/* params[0] is 'find', actual search params start with [1] */
|
||||
if (!opts->bookmark && !opts->params[1]) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"error in parameters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bookmarkval = NULL;
|
||||
if (opts->bookmark) {
|
||||
bookmarkval = resolve_bookmark (opts, err);
|
||||
if (!bookmarkval)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
query = str_quoted_from_strv ((const gchar**)&opts->params[1]);
|
||||
if (bookmarkval) {
|
||||
gchar *tmp;
|
||||
tmp = g_strdup_printf ("%s %s", bookmarkval, query);
|
||||
g_free (query);
|
||||
query = tmp;
|
||||
}
|
||||
|
||||
g_free (bookmarkval);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
static MuQuery*
|
||||
get_query_obj (MuStore *store, GError **err)
|
||||
{
|
||||
MuQuery *mquery;
|
||||
unsigned count;
|
||||
|
||||
count = mu_store_count (store, err);
|
||||
|
||||
if (count == (unsigned)-1)
|
||||
return NULL;
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_IS_EMPTY,
|
||||
"the database is empty");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mu_store_needs_upgrade (store)) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_NOT_UP_TO_DATE,
|
||||
"the database is not up-to-date");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mquery = mu_query_new (store, err);
|
||||
if (!mquery)
|
||||
return NULL;
|
||||
|
||||
return mquery;
|
||||
}
|
||||
|
||||
/* create a linksdir if it not exist yet; if it already existed,
|
||||
* remove old links if opts->clearlinks was specified */
|
||||
static gboolean
|
||||
create_linksdir_maybe (const char *linksdir, gboolean clearlinks)
|
||||
{
|
||||
GError *err;
|
||||
|
||||
err = NULL;
|
||||
/* note, mu_maildir_mkdir simply ignores whatever part of the
|
||||
* mail dir already exists */
|
||||
if (!mu_maildir_mkdir (linksdir, 0700, TRUE, &err))
|
||||
goto fail;
|
||||
|
||||
if (clearlinks && !mu_maildir_clear_links (linksdir, &err))
|
||||
goto fail;
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
g_warning ("%s", err->message ? err->message : "unknown error");
|
||||
g_clear_error (&err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
link_message (const char *src, const char *destdir)
|
||||
{
|
||||
GError *err;
|
||||
|
||||
if (access (src, R_OK) != 0) {
|
||||
if (errno == ENOENT)
|
||||
g_warning ("cannot find source message %s", src);
|
||||
else
|
||||
g_warning ("cannot read source message %s: %s", src,
|
||||
strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
err = NULL;
|
||||
if (!mu_maildir_link (src, destdir, &err)) {
|
||||
g_warning ("%s", err->message ? err->message :
|
||||
"unknown error");
|
||||
g_clear_error (&err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
output_links (MuMsgIter *iter, const char* linksdir, gboolean clearlinks,
|
||||
GError **err)
|
||||
{
|
||||
size_t count, errcount;
|
||||
MuMsgIter *myiter;
|
||||
|
||||
g_return_val_if_fail (iter, FALSE);
|
||||
g_return_val_if_fail (linksdir, FALSE);
|
||||
|
||||
/* note: we create the linksdir even if there are no search results */
|
||||
if (!create_linksdir_maybe (linksdir, clearlinks))
|
||||
return FALSE;
|
||||
|
||||
for (myiter = iter, count = errcount = 0; !mu_msg_iter_is_done (myiter);
|
||||
mu_msg_iter_next (myiter)) {
|
||||
|
||||
const char* path;
|
||||
MuMsg *msg;
|
||||
|
||||
msg = mu_msg_iter_get_msg_floating (iter);
|
||||
if (!msg)
|
||||
continue;
|
||||
|
||||
path = mu_msg_get_path (msg);
|
||||
if (access (path, R_OK) == 0) /* only link to readable */
|
||||
link_message (path, linksdir) ? ++count : ++errcount;
|
||||
}
|
||||
|
||||
if (errcount > 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_LINK,
|
||||
"error linking %u message(s)", (unsigned)errcount);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"no matches for search expression");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
ansi_color_maybe (MuMsgFieldId mfid, gboolean color)
|
||||
{
|
||||
const char* ansi;
|
||||
|
||||
if (!color)
|
||||
return; /* nothing to do */
|
||||
|
||||
switch (mfid) {
|
||||
|
||||
case MU_MSG_FIELD_ID_FROM:
|
||||
ansi = MU_COLOR_CYAN; break;
|
||||
|
||||
case MU_MSG_FIELD_ID_TO:
|
||||
case MU_MSG_FIELD_ID_CC:
|
||||
case MU_MSG_FIELD_ID_BCC:
|
||||
ansi = MU_COLOR_BLUE; break;
|
||||
|
||||
case MU_MSG_FIELD_ID_SUBJECT:
|
||||
ansi = MU_COLOR_GREEN; break;
|
||||
|
||||
case MU_MSG_FIELD_ID_DATE:
|
||||
ansi = MU_COLOR_MAGENTA; break;
|
||||
|
||||
default:
|
||||
if (mu_msg_field_type(mfid) == MU_MSG_FIELD_TYPE_STRING)
|
||||
ansi = MU_COLOR_YELLOW;
|
||||
else
|
||||
ansi = MU_COLOR_RED;
|
||||
}
|
||||
|
||||
fputs (ansi, stdout);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ansi_reset_maybe (MuMsgFieldId mfid, gboolean color)
|
||||
{
|
||||
if (!color)
|
||||
return; /* nothing to do */
|
||||
|
||||
fputs (MU_COLOR_DEFAULT, stdout);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static const char*
|
||||
display_field (MuMsg *msg, MuMsgFieldId mfid)
|
||||
{
|
||||
gint64 val;
|
||||
|
||||
switch (mu_msg_field_type(mfid)) {
|
||||
case MU_MSG_FIELD_TYPE_STRING: {
|
||||
const gchar *str;
|
||||
str = mu_msg_get_field_string (msg, mfid);
|
||||
return str ? str : "";
|
||||
}
|
||||
case MU_MSG_FIELD_TYPE_INT:
|
||||
|
||||
if (mfid == MU_MSG_FIELD_ID_PRIO) {
|
||||
val = mu_msg_get_field_numeric (msg, mfid);
|
||||
return mu_msg_prio_name ((MuMsgPrio)val);
|
||||
} else if (mfid == MU_MSG_FIELD_ID_FLAGS) {
|
||||
val = mu_msg_get_field_numeric (msg, mfid);
|
||||
return mu_str_flags_s ((MuFlags)val);
|
||||
} else /* as string */
|
||||
return mu_msg_get_field_string (msg, mfid);
|
||||
|
||||
case MU_MSG_FIELD_TYPE_TIME_T:
|
||||
val = mu_msg_get_field_numeric (msg, mfid);
|
||||
return mu_date_str_s ("%c", (time_t)val);
|
||||
|
||||
case MU_MSG_FIELD_TYPE_BYTESIZE:
|
||||
val = mu_msg_get_field_numeric (msg, mfid);
|
||||
return mu_str_size_s ((unsigned)val);
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_summary (MuMsg *msg, int summary_len)
|
||||
{
|
||||
const char* body;
|
||||
char *summ;
|
||||
|
||||
body = mu_msg_get_body_text(msg);
|
||||
|
||||
summ = body ? mu_str_summarize (body, (unsigned)summary_len) : NULL;
|
||||
|
||||
g_print ("Summary: ");
|
||||
mu_util_fputs_encoded (summ ? summ : "<none>", stdout);
|
||||
g_print ("\n");
|
||||
|
||||
g_free (summ);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
thread_indent (MuMsgIter *iter)
|
||||
{
|
||||
const MuMsgIterThreadInfo *ti;
|
||||
const char* threadpath;
|
||||
int i;
|
||||
gboolean is_root, first_child, empty_parent, is_dup;
|
||||
|
||||
ti = mu_msg_iter_get_thread_info (iter);
|
||||
if (!ti) {
|
||||
g_warning ("cannot get thread-info for message %u",
|
||||
mu_msg_iter_get_docid (iter));
|
||||
return;
|
||||
}
|
||||
|
||||
threadpath = ti->threadpath;
|
||||
/* fputs (threadpath, stdout); */
|
||||
/* fputs (" ", stdout); */
|
||||
|
||||
is_root = ti->prop & MU_MSG_ITER_THREAD_PROP_ROOT;
|
||||
first_child = ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD;
|
||||
empty_parent = ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT;
|
||||
is_dup = ti->prop & MU_MSG_ITER_THREAD_PROP_DUP;
|
||||
|
||||
/* FIXME: count the colons... */
|
||||
for (i = 0; *threadpath; ++threadpath)
|
||||
i += (*threadpath == ':') ? 1 : 0;
|
||||
|
||||
/* indent */
|
||||
while (i --> 0)
|
||||
fputs (" ", stdout);
|
||||
|
||||
if (!is_root) {
|
||||
fputs (first_child ? "`" : "|", stdout);
|
||||
fputs (empty_parent ? "*> " : is_dup ? "=> " : "-> ", stdout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
output_plain_fields (MuMsg *msg, const char *fields,
|
||||
gboolean color, gboolean threads)
|
||||
{
|
||||
const char* myfields;
|
||||
int nonempty;
|
||||
|
||||
for (myfields = fields, nonempty = 0; *myfields; ++myfields) {
|
||||
|
||||
MuMsgFieldId mfid;
|
||||
mfid = mu_msg_field_id_from_shortcut (*myfields, FALSE);
|
||||
|
||||
if (mfid == MU_MSG_FIELD_ID_NONE ||
|
||||
(!mu_msg_field_xapian_value (mfid) &&
|
||||
!mu_msg_field_xapian_contact (mfid)))
|
||||
nonempty += printf ("%c", *myfields);
|
||||
|
||||
else {
|
||||
ansi_color_maybe (mfid, color);
|
||||
nonempty += mu_util_fputs_encoded
|
||||
(display_field (msg, mfid), stdout);
|
||||
ansi_reset_maybe (mfid, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (nonempty)
|
||||
fputs ("\n", stdout);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
output_plain (MuMsgIter *iter, const char *fields, int summary_len,
|
||||
gboolean threads, gboolean color, gboolean include_unreadable, GError **err)
|
||||
{
|
||||
MuMsgIter *myiter;
|
||||
size_t count;
|
||||
|
||||
g_return_val_if_fail (iter, FALSE);
|
||||
g_return_val_if_fail (fields, FALSE);
|
||||
|
||||
for (myiter = iter, count = 0; !mu_msg_iter_is_done (myiter);
|
||||
mu_msg_iter_next (myiter)) {
|
||||
|
||||
MuMsg *msg;
|
||||
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
|
||||
if (!msg)
|
||||
continue;
|
||||
|
||||
/* only return messages if they're actually
|
||||
* readable (ie, live also outside the database) */
|
||||
if (!include_unreadable && !mu_msg_is_readable (msg))
|
||||
continue;
|
||||
|
||||
/* we reuse the color (whatever that may be)
|
||||
* for message-priority for threads, too */
|
||||
ansi_color_maybe (MU_MSG_FIELD_ID_PRIO, color);
|
||||
if (threads)
|
||||
thread_indent (iter);
|
||||
|
||||
output_plain_fields (msg, fields, color, threads);
|
||||
|
||||
if (summary_len > 0)
|
||||
print_summary (msg, summary_len);
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"no existing matches for search expression");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
print_attr_xml (const char* elm, const char *str)
|
||||
{
|
||||
gchar *esc;
|
||||
|
||||
if (mu_str_is_empty(str))
|
||||
return; /* empty: don't include */
|
||||
|
||||
esc = g_markup_escape_text (str, -1);
|
||||
g_print ("\t\t<%s>%s</%s>\n", elm, esc, elm);
|
||||
g_free (esc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
output_sexp (MuMsgIter *iter, gboolean threads,
|
||||
gboolean include_unreadable, GError **err)
|
||||
{
|
||||
MuMsgIter *myiter;
|
||||
size_t count;
|
||||
|
||||
g_return_val_if_fail (iter, FALSE);
|
||||
|
||||
for (myiter = iter, count = 0; !mu_msg_iter_is_done (myiter);
|
||||
mu_msg_iter_next (myiter)) {
|
||||
|
||||
MuMsg *msg;
|
||||
char *sexp;
|
||||
const MuMsgIterThreadInfo *ti;
|
||||
|
||||
msg = mu_msg_iter_get_msg_floating (iter);
|
||||
if (!msg)
|
||||
return FALSE;
|
||||
|
||||
/* only return messages if they're actually
|
||||
* readable (ie, live also outside the database) */
|
||||
if (!include_unreadable && !mu_msg_is_readable (msg))
|
||||
continue;
|
||||
|
||||
ti = threads ? mu_msg_iter_get_thread_info (iter) : NULL;
|
||||
sexp = mu_msg_to_sexp (msg,
|
||||
mu_msg_iter_get_docid (iter),
|
||||
ti, TRUE, FALSE);
|
||||
|
||||
fputs (sexp, stdout);
|
||||
g_free (sexp);
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"no existing matches for search expression");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
output_xml_msg (MuMsg *msg)
|
||||
{
|
||||
g_print ("\t<message>\n");
|
||||
print_attr_xml ("from", mu_msg_get_from (msg));
|
||||
print_attr_xml ("to", mu_msg_get_to (msg));
|
||||
print_attr_xml ("cc", mu_msg_get_cc (msg));
|
||||
print_attr_xml ("subject", mu_msg_get_subject (msg));
|
||||
g_print ("\t\t<date>%u</date>\n",
|
||||
(unsigned)mu_msg_get_date (msg));
|
||||
g_print ("\t\t<size>%u</size>\n", (unsigned)mu_msg_get_size (msg));
|
||||
print_attr_xml ("msgid", mu_msg_get_msgid (msg));
|
||||
print_attr_xml ("path", mu_msg_get_path (msg));
|
||||
print_attr_xml ("maildir", mu_msg_get_maildir (msg));
|
||||
g_print ("\t</message>\n");
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
output_xml (MuMsgIter *iter, gboolean include_unreadable, GError **err)
|
||||
{
|
||||
MuMsgIter *myiter;
|
||||
size_t count;
|
||||
|
||||
g_return_val_if_fail (iter, FALSE);
|
||||
|
||||
g_print ("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
|
||||
g_print ("<messages>\n");
|
||||
|
||||
for (myiter = iter, count = 0; !mu_msg_iter_is_done (myiter);
|
||||
mu_msg_iter_next (myiter)) {
|
||||
MuMsg *msg;
|
||||
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
|
||||
if (!msg)
|
||||
return FALSE;
|
||||
|
||||
/* only return messages if they're actually
|
||||
* readable (ie, live also outside the database) */
|
||||
if (!include_unreadable && !mu_msg_is_readable (msg))
|
||||
continue;
|
||||
|
||||
output_xml_msg (msg);
|
||||
++count;
|
||||
|
||||
}
|
||||
g_print ("</messages>\n");
|
||||
|
||||
if (count == 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES,
|
||||
"no existing matches for search expression");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
execute_find (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
char *query_str;
|
||||
MuQuery *oracle;
|
||||
gboolean rv;
|
||||
|
||||
oracle = get_query_obj(store, err);
|
||||
if (!oracle)
|
||||
return FALSE;
|
||||
|
||||
query_str = get_query (opts, err);
|
||||
if (!query_str) {
|
||||
mu_query_destroy (oracle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->format == MU_CONFIG_FORMAT_XQUERY)
|
||||
rv = print_xapian_query (oracle, query_str, err);
|
||||
else if (opts->exec)
|
||||
rv = exec_cmd_on_query (oracle, query_str, opts, err);
|
||||
else
|
||||
rv = process_query (oracle, query_str, opts, err);
|
||||
|
||||
mu_query_destroy (oracle);
|
||||
g_free (query_str);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
show_usage (void)
|
||||
{
|
||||
const char *usage_str =
|
||||
"usage: mu find [options] <search expression>\n";
|
||||
g_message ("%s", usage_str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_find (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_FIND,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
if (!query_params_valid (opts, err) || !format_params_valid(opts, err)) {
|
||||
|
||||
if (MU_G_ERROR_CODE(err) == MU_ERROR_IN_PARAMETERS)
|
||||
show_usage ();
|
||||
|
||||
return MU_G_ERROR_CODE (err);
|
||||
}
|
||||
|
||||
if (!execute_find (store, opts, err))
|
||||
return MU_G_ERROR_CODE(err);
|
||||
else
|
||||
return MU_OK;
|
||||
}
|
||||
410
mu/mu-cmd-index.c
Normal file
410
mu/mu-cmd-index.c
Normal file
@ -0,0 +1,410 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include "mu-cmd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mu-util.h"
|
||||
#include "mu-msg.h"
|
||||
#include "mu-index.h"
|
||||
#include "mu-store.h"
|
||||
#include "mu-runtime.h"
|
||||
|
||||
static gboolean MU_CAUGHT_SIGNAL;
|
||||
|
||||
static void
|
||||
sig_handler (int sig)
|
||||
{
|
||||
if (!MU_CAUGHT_SIGNAL && sig == SIGINT) { /* Ctrl-C */
|
||||
g_print ("\n");
|
||||
g_warning ("shutting down gracefully, "
|
||||
"press again to kill immediately");
|
||||
}
|
||||
|
||||
MU_CAUGHT_SIGNAL = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
install_sig_handler (void)
|
||||
{
|
||||
struct sigaction action;
|
||||
int i, sigs[] = { SIGINT, SIGHUP, SIGTERM };
|
||||
|
||||
MU_CAUGHT_SIGNAL = FALSE;
|
||||
|
||||
action.sa_handler = sig_handler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = SA_RESETHAND;
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(sigs); ++i)
|
||||
if (sigaction (sigs[i], &action, NULL) != 0)
|
||||
g_critical ("set sigaction for %d failed: %s",
|
||||
sigs[i], strerror (errno));;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
check_params (MuConfig *opts, GError **err)
|
||||
{
|
||||
/* param[0] == 'index' there should be no param[1] */
|
||||
if (opts->params[1]) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"unexpected parameter");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->xbatchsize < 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"the batch size must be non-negative");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opts->max_msg_size < 0) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"the maximum message size must be non-negative");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_maildir (const char *maildir, GError **err)
|
||||
{
|
||||
if (!maildir) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"no maildir to work on; use --maildir=");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_path_is_absolute (maildir)) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"maildir path '%s' is not absolute",
|
||||
maildir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!mu_util_check_dir (maildir, TRUE, FALSE)) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"not a valid Maildir: %s", maildir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static MuError
|
||||
index_msg_silent_cb (MuIndexStats* stats, void *user_data)
|
||||
{
|
||||
return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
backspace (unsigned u)
|
||||
{
|
||||
static gboolean init = FALSE;
|
||||
static char backspace[80];
|
||||
|
||||
if (G_UNLIKELY(!init)) {
|
||||
/* fill with backspaces */
|
||||
int i;
|
||||
for (i = 0; i != sizeof(backspace); ++i)
|
||||
backspace[i] = '\b';
|
||||
init = TRUE;
|
||||
}
|
||||
|
||||
backspace[MIN(u,sizeof(backspace))] = '\0';
|
||||
fputs (backspace, stdout);
|
||||
backspace[u] = '\b';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
print_stats (MuIndexStats* stats, gboolean clear, gboolean color)
|
||||
{
|
||||
const char *kars="-\\|/";
|
||||
char output[120];
|
||||
|
||||
static unsigned i = 0, len = 0;
|
||||
|
||||
if (clear)
|
||||
backspace (len);
|
||||
|
||||
if (color)
|
||||
len = (unsigned)snprintf
|
||||
(output, sizeof(output),
|
||||
MU_COLOR_YELLOW "%c " MU_COLOR_DEFAULT
|
||||
"processing mail; "
|
||||
"processed: " MU_COLOR_GREEN "%u; " MU_COLOR_DEFAULT
|
||||
"updated/new: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT
|
||||
", cleaned-up: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT,
|
||||
(unsigned)kars[++i % 4],
|
||||
(unsigned)stats->_processed,
|
||||
(unsigned)stats->_updated,
|
||||
(unsigned)stats->_cleaned_up);
|
||||
else
|
||||
len = (unsigned)snprintf
|
||||
(output, sizeof(output),
|
||||
"%c processing mail; processed: %u; "
|
||||
"updated/new: %u, cleaned-up: %u",
|
||||
(unsigned)kars[++i % 4],
|
||||
(unsigned)stats->_processed,
|
||||
(unsigned)stats->_updated,
|
||||
(unsigned)stats->_cleaned_up);
|
||||
|
||||
fputs (output, stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
|
||||
struct _IndexData {
|
||||
gboolean color;
|
||||
};
|
||||
typedef struct _IndexData IndexData;
|
||||
|
||||
|
||||
static MuError
|
||||
index_msg_cb (MuIndexStats* stats, IndexData *idata)
|
||||
{
|
||||
if (stats->_processed % 25)
|
||||
return MU_OK;
|
||||
|
||||
print_stats (stats, TRUE, idata->color);
|
||||
|
||||
return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
database_version_check_and_update (MuStore *store, MuConfig *opts,
|
||||
GError **err)
|
||||
{
|
||||
if (mu_store_count (store, err) == 0)
|
||||
return TRUE;
|
||||
|
||||
/* when rebuilding, we empty the database before doing
|
||||
* anything */
|
||||
if (opts->rebuild) {
|
||||
opts->reindex = TRUE;
|
||||
g_debug ("clearing database");
|
||||
g_debug ("clearing contacts-cache");
|
||||
return mu_store_clear (store, err);
|
||||
}
|
||||
|
||||
if (!mu_store_needs_upgrade (store))
|
||||
return TRUE; /* ok, nothing to do */
|
||||
|
||||
/* ok, database is not up to date */
|
||||
if (opts->autoupgrade) {
|
||||
opts->reindex = TRUE;
|
||||
g_debug ("auto-upgrade: clearing old database and cache");
|
||||
return mu_store_clear (store, err);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_time (unsigned t, unsigned processed, gboolean color)
|
||||
{
|
||||
|
||||
if (color) {
|
||||
if (t)
|
||||
g_message ("elapsed: "
|
||||
MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT
|
||||
" second(s), ~ "
|
||||
MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT
|
||||
" msg/s",
|
||||
t, processed/t);
|
||||
else
|
||||
g_message ("elapsed: "
|
||||
MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT
|
||||
" second(s)", t);
|
||||
} else {
|
||||
if (t)
|
||||
g_message ("elapsed: %u second(s), ~ %u msg/s",
|
||||
t, processed/t);
|
||||
else
|
||||
g_message ("elapsed: %u second(s)", t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static MuError
|
||||
cleanup_missing (MuIndex *midx, MuConfig *opts, MuIndexStats *stats,
|
||||
gboolean show_progress, GError **err)
|
||||
{
|
||||
MuError rv;
|
||||
time_t t;
|
||||
IndexData idata;
|
||||
|
||||
g_message ("cleaning up messages [%s]",
|
||||
mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB));
|
||||
|
||||
mu_index_stats_clear (stats);
|
||||
|
||||
t = time (NULL);
|
||||
idata.color = !opts->nocolor;
|
||||
rv = mu_index_cleanup
|
||||
(midx, stats,
|
||||
show_progress ?
|
||||
(MuIndexCleanupDeleteCallback)index_msg_cb :
|
||||
(MuIndexCleanupDeleteCallback)index_msg_silent_cb,
|
||||
&idata, err);
|
||||
|
||||
if (!opts->quiet) {
|
||||
print_stats (stats, TRUE, !opts->nocolor);
|
||||
g_print ("\n");
|
||||
show_time ((unsigned)(time(NULL)-t),stats->_processed,
|
||||
!opts->nocolor);
|
||||
}
|
||||
|
||||
return (rv == MU_OK || rv == MU_STOP) ? MU_OK: MU_G_ERROR_CODE(err);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
index_title (const char* maildir, const char* xapiandir, gboolean color)
|
||||
{
|
||||
if (color)
|
||||
g_message ("indexing messages under "
|
||||
MU_COLOR_BLUE "%s" MU_COLOR_DEFAULT
|
||||
" ["
|
||||
MU_COLOR_BLUE "%s" MU_COLOR_DEFAULT
|
||||
"]", maildir, xapiandir);
|
||||
else
|
||||
g_message ("indexing messages under %s [%s]",
|
||||
maildir, xapiandir);
|
||||
}
|
||||
|
||||
|
||||
static MuError
|
||||
cmd_index (MuIndex *midx, MuConfig *opts, MuIndexStats *stats,
|
||||
gboolean show_progress, GError **err)
|
||||
{
|
||||
IndexData idata;
|
||||
MuError rv;
|
||||
time_t t;
|
||||
|
||||
t = time (NULL);
|
||||
|
||||
index_title (opts->maildir, mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
|
||||
!opts->nocolor);
|
||||
|
||||
idata.color = !opts->nocolor;
|
||||
rv = mu_index_run (midx, opts->maildir, opts->reindex, stats,
|
||||
show_progress ?
|
||||
(MuIndexMsgCallback)index_msg_cb :
|
||||
(MuIndexMsgCallback)index_msg_silent_cb,
|
||||
NULL, &idata);
|
||||
|
||||
if (!opts->quiet) {
|
||||
print_stats (stats, TRUE, !opts->nocolor);
|
||||
g_print ("\n");
|
||||
show_time ((unsigned)(time(NULL)-t),
|
||||
stats->_processed, !opts->nocolor);
|
||||
}
|
||||
|
||||
if (rv == MU_OK || rv == MU_STOP) {
|
||||
MU_WRITE_LOG ("index: processed: %u; updated/new: %u",
|
||||
stats->_processed, stats->_updated);
|
||||
if (rv == MU_OK && !opts->nocleanup)
|
||||
rv = cleanup_missing (midx, opts, stats, show_progress, err);
|
||||
if (rv == MU_STOP)
|
||||
rv = MU_OK;
|
||||
} else
|
||||
g_set_error (err, MU_ERROR_DOMAIN, rv, "error while indexing");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static MuIndex*
|
||||
init_mu_index (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
MuIndex *midx;
|
||||
|
||||
if (!check_params (opts, err))
|
||||
return NULL;
|
||||
|
||||
if (!database_version_check_and_update(store, opts, err))
|
||||
return NULL;
|
||||
|
||||
|
||||
if (!check_maildir (opts->maildir, err))
|
||||
return NULL;
|
||||
|
||||
midx = mu_index_new (store, err);
|
||||
if (!midx)
|
||||
return NULL;
|
||||
|
||||
mu_index_set_max_msg_size (midx, opts->max_msg_size);
|
||||
mu_index_set_xbatch_size (midx, opts->xbatchsize);
|
||||
|
||||
return midx;
|
||||
}
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_index (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
MuIndex *midx;
|
||||
MuIndexStats stats;
|
||||
gboolean rv, show_progress;
|
||||
|
||||
g_return_val_if_fail (opts, FALSE);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_INDEX,
|
||||
FALSE);
|
||||
|
||||
/* create, and do error handling if needed */
|
||||
midx = init_mu_index (store, opts, err);
|
||||
if (!midx)
|
||||
return MU_G_ERROR_CODE(err);
|
||||
|
||||
/* note, 'opts->quiet' already cause g_message output not to
|
||||
* be shown; here, we make sure we only print progress info if
|
||||
* opts->quiet is false case and when stdout is a tty */
|
||||
show_progress = !opts->quiet && isatty(fileno(stdout));
|
||||
|
||||
mu_index_stats_clear (&stats);
|
||||
install_sig_handler ();
|
||||
|
||||
rv = cmd_index (midx, opts, &stats, show_progress, err);
|
||||
mu_index_destroy (midx);
|
||||
|
||||
return rv;
|
||||
}
|
||||
1254
mu/mu-cmd-server.c
Normal file
1254
mu/mu-cmd-server.c
Normal file
File diff suppressed because it is too large
Load Diff
487
mu/mu-cmd.c
Normal file
487
mu/mu-cmd.c
Normal file
@ -0,0 +1,487 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
/*
|
||||
** Copyright (C) 2010-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "mu-msg.h"
|
||||
#include "mu-msg-part.h"
|
||||
#include "mu-cmd.h"
|
||||
#include "mu-util.h"
|
||||
#include "mu-str.h"
|
||||
#include "mu-date.h"
|
||||
#include "mu-maildir.h"
|
||||
#include "mu-contacts.h"
|
||||
#include "mu-runtime.h"
|
||||
#include "mu-flags.h"
|
||||
#include "mu-store.h"
|
||||
|
||||
#define VIEW_TERMINATOR '\f' /* form-feed */
|
||||
|
||||
|
||||
static gboolean
|
||||
view_msg_sexp (MuMsg *msg)
|
||||
{
|
||||
char *sexp;
|
||||
|
||||
sexp = mu_msg_to_sexp (msg, 0, NULL, FALSE, FALSE);
|
||||
fputs (sexp, stdout);
|
||||
g_free (sexp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_part (MuMsg *msg, MuMsgPart *part, gchar **attach)
|
||||
{
|
||||
if (mu_msg_part_looks_like_attachment (part, TRUE) &&
|
||||
(part->file_name)) {
|
||||
|
||||
char *tmp = *attach;
|
||||
|
||||
*attach = g_strdup_printf ("%s%s'%s'",
|
||||
*attach ? *attach : "",
|
||||
*attach ? ", " : "",
|
||||
part->file_name);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* return comma-sep'd list of attachments */
|
||||
static gchar *
|
||||
get_attach_str (MuMsg *msg)
|
||||
{
|
||||
gchar *attach;
|
||||
|
||||
attach = NULL;
|
||||
mu_msg_part_foreach (msg, FALSE,
|
||||
(MuMsgPartForeachFunc)each_part, &attach);
|
||||
|
||||
return attach;
|
||||
}
|
||||
|
||||
#define color_maybe(C) do{ if (color) fputs ((C),stdout);}while(0)
|
||||
|
||||
static void
|
||||
print_field (const char* field, const char *val, gboolean color)
|
||||
{
|
||||
if (!val)
|
||||
return;
|
||||
|
||||
color_maybe (MU_COLOR_MAGENTA);
|
||||
mu_util_fputs_encoded (field, stdout);
|
||||
color_maybe (MU_COLOR_DEFAULT);
|
||||
fputs (": ", stdout);
|
||||
|
||||
if (val) {
|
||||
color_maybe (MU_COLOR_GREEN);
|
||||
mu_util_fputs_encoded (val, stdout);
|
||||
}
|
||||
|
||||
color_maybe (MU_COLOR_DEFAULT);
|
||||
fputs ("\n", stdout);
|
||||
}
|
||||
|
||||
|
||||
/* a summary_len of 0 mean 'don't show summary, show body */
|
||||
static void
|
||||
body_or_summary (MuMsg *msg, unsigned summary_len, gboolean color)
|
||||
{
|
||||
const char* field;
|
||||
|
||||
field = mu_msg_get_body_text (msg);
|
||||
if (!field)
|
||||
return; /* no body -- nothing more to do */
|
||||
|
||||
if (summary_len != 0) {
|
||||
gchar *summ;
|
||||
summ = mu_str_summarize (field, summary_len);
|
||||
print_field ("Summary", summ, color);
|
||||
g_free (summ);
|
||||
} else {
|
||||
color_maybe (MU_COLOR_YELLOW);
|
||||
mu_util_print_encoded ("\n%s\n", field);
|
||||
color_maybe (MU_COLOR_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* we ignore fields for now */
|
||||
/* summary_len == 0 means "no summary */
|
||||
static gboolean
|
||||
view_msg_plain (MuMsg *msg, const gchar *fields, unsigned summary_len,
|
||||
gboolean color)
|
||||
{
|
||||
gchar *attachs;
|
||||
time_t date;
|
||||
const GSList *lst;
|
||||
|
||||
print_field ("From", mu_msg_get_from (msg), color);
|
||||
print_field ("To", mu_msg_get_to (msg), color);
|
||||
print_field ("Cc", mu_msg_get_cc (msg), color);
|
||||
print_field ("Bcc", mu_msg_get_bcc (msg), color);
|
||||
print_field ("Subject", mu_msg_get_subject (msg), color);
|
||||
|
||||
if ((date = mu_msg_get_date (msg)))
|
||||
print_field ("Date", mu_date_str_s ("%c", date),
|
||||
color);
|
||||
|
||||
if ((lst = mu_msg_get_tags (msg))) {
|
||||
gchar *tags;
|
||||
tags = mu_str_from_list (lst,',');
|
||||
print_field ("Tags", tags, color);
|
||||
g_free (tags);
|
||||
}
|
||||
|
||||
if ((attachs = get_attach_str (msg))) {
|
||||
print_field ("Attachments", attachs, color);
|
||||
g_free (attachs);
|
||||
}
|
||||
|
||||
body_or_summary (msg, summary_len, color);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
handle_msg (const char *fname, MuConfig *opts, GError **err)
|
||||
{
|
||||
MuMsg *msg;
|
||||
gboolean rv;
|
||||
|
||||
err = NULL;
|
||||
msg = mu_msg_new_from_file (fname, NULL, err);
|
||||
if (!msg)
|
||||
return FALSE;
|
||||
|
||||
switch (opts->format) {
|
||||
case MU_CONFIG_FORMAT_PLAIN:
|
||||
rv = view_msg_plain
|
||||
(msg, NULL,
|
||||
opts->summary ? opts->summary_len : 0,
|
||||
!opts->nocolor);
|
||||
break;
|
||||
case MU_CONFIG_FORMAT_SEXP:
|
||||
rv = view_msg_sexp (msg);
|
||||
break;
|
||||
default:
|
||||
g_critical ("bug: should not be reached");
|
||||
rv = FALSE;
|
||||
}
|
||||
|
||||
mu_msg_unref (msg);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
view_params_valid (MuConfig *opts, GError **err)
|
||||
{
|
||||
/* note: params[0] will be 'view' */
|
||||
if (!opts->params[0] || !opts->params[1]) {
|
||||
mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS,
|
||||
"error in parameters");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (opts->format) {
|
||||
case MU_CONFIG_FORMAT_PLAIN:
|
||||
case MU_CONFIG_FORMAT_SEXP:
|
||||
break;
|
||||
default:
|
||||
mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS,
|
||||
"invalid output format");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_view (MuConfig *opts, GError **err)
|
||||
{
|
||||
int i;
|
||||
gboolean rv;
|
||||
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VIEW,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
rv = view_params_valid(opts, err);
|
||||
if (!rv)
|
||||
goto leave;
|
||||
|
||||
for (i = 1; opts->params[i]; ++i) {
|
||||
|
||||
rv = handle_msg (opts->params[i], opts, err);
|
||||
if (!rv)
|
||||
break;
|
||||
|
||||
/* add a separator between two messages? */
|
||||
if (opts->terminator)
|
||||
g_print ("%c", VIEW_TERMINATOR);
|
||||
}
|
||||
|
||||
leave:
|
||||
if (!rv)
|
||||
return err && *err ? (*err)->code : MU_ERROR;
|
||||
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_mkdir (MuConfig *opts, GError **err)
|
||||
{
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_MKDIR,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
if (!opts->params[1]) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"missing directory parameter");
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
|
||||
for (i = 1; opts->params[i]; ++i)
|
||||
if (!mu_maildir_mkdir (opts->params[i], opts->dirmode,
|
||||
FALSE, err))
|
||||
return err && *err ? (*err)->code :
|
||||
MU_ERROR_FILE_CANNOT_MKDIR;
|
||||
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
check_file_okay (const char *path, gboolean cmd_add)
|
||||
{
|
||||
if (!g_path_is_absolute (path)) {
|
||||
g_warning ("path is not absolute: %s", path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (cmd_add && access(path, R_OK) != 0) {
|
||||
g_warning ("path is not readable: %s: %s",
|
||||
path, strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
check_add_params (MuConfig *opts, GError **err)
|
||||
{
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_add (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
gboolean allok;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (store, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_ADD,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
/* note: params[0] will be 'add' */
|
||||
if (!opts->params[0] || !opts->params[1]) {
|
||||
g_message ("usage: mu add <file> [<files>]");
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"missing source and/or target");
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
|
||||
for (i = 1, allok = TRUE; opts->params[i]; ++i) {
|
||||
|
||||
const char* src;
|
||||
src = opts->params[i];
|
||||
|
||||
if (!check_file_okay (src, TRUE) ||
|
||||
mu_store_add_path (store, src, NULL, err) ==
|
||||
MU_STORE_INVALID_DOCID) {
|
||||
MU_WRITE_LOG ("failed to add %s", src);
|
||||
allok = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allok) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_STORE_FAILED,
|
||||
"store failed for some message(s)");
|
||||
return MU_ERROR_XAPIAN_STORE_FAILED;
|
||||
}
|
||||
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err)
|
||||
{
|
||||
gboolean allok;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_REMOVE,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
/* note: params[0] will be 'add' */
|
||||
if (!opts->params[0] || !opts->params[1]) {
|
||||
g_warning ("usage: mu remove <file> [<files>]");
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"missing source and/or target");
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
|
||||
for (i = 1, allok = TRUE; opts->params[i]; ++i) {
|
||||
|
||||
const char* src;
|
||||
src = opts->params[i];
|
||||
|
||||
if (!check_file_okay (src, FALSE) ||
|
||||
!mu_store_remove_path (store, src)) {
|
||||
allok = FALSE;
|
||||
MU_WRITE_LOG ("failed to remove %s", src);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allok) {
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_STORE_FAILED,
|
||||
"remove failed for some message(s)");
|
||||
return MU_ERROR_XAPIAN_REMOVE_FAILED;
|
||||
}
|
||||
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_usage (void)
|
||||
{
|
||||
g_message ("usage: mu command [options] [parameters]");
|
||||
g_message ("where command is one of index, find, cfind, view, mkdir, "
|
||||
"extract, add, remove or server");
|
||||
g_message ("see the mu, mu-<command> or mu-easy manpages for "
|
||||
"more information");
|
||||
}
|
||||
|
||||
static void
|
||||
show_version (void)
|
||||
{
|
||||
g_print ("mu (mail indexer/searcher) version " VERSION "\n"
|
||||
"Copyright (C) 2008-2011 Dirk-Jan C. Binnema (GPLv3+)\n");
|
||||
}
|
||||
|
||||
typedef MuError (*store_func) (MuStore *, MuConfig *, GError **err);
|
||||
|
||||
MuError
|
||||
with_store (store_func func, MuConfig *opts, gboolean read_only,
|
||||
GError **err)
|
||||
{
|
||||
MuStore *store;
|
||||
|
||||
if (read_only)
|
||||
store = mu_store_new_read_only
|
||||
(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
|
||||
err);
|
||||
else
|
||||
store = mu_store_new_writable
|
||||
(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
|
||||
mu_runtime_path(MU_RUNTIME_PATH_CONTACTS),
|
||||
opts->rebuild, err);
|
||||
if (!store)
|
||||
return MU_G_ERROR_CODE(err);
|
||||
else {
|
||||
MuError merr;
|
||||
merr = func (store, opts, err);
|
||||
mu_store_unref (store);
|
||||
return merr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
check_params (MuConfig *opts, GError **err)
|
||||
{
|
||||
if (!opts->params||!opts->params[0]) {/* no command? */
|
||||
show_usage ();
|
||||
mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS,
|
||||
"error in parameters");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
MuError
|
||||
mu_cmd_execute (MuConfig *opts, GError **err)
|
||||
{
|
||||
g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
|
||||
|
||||
if (opts->version) {
|
||||
show_version ();
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
if (!check_params(opts, err))
|
||||
return MU_G_ERROR_CODE(err);
|
||||
|
||||
switch (opts->cmd) {
|
||||
case MU_CONFIG_CMD_CFIND: return mu_cmd_cfind (opts, err);
|
||||
case MU_CONFIG_CMD_MKDIR: return mu_cmd_mkdir (opts, err);
|
||||
case MU_CONFIG_CMD_VIEW: return mu_cmd_view (opts, err);
|
||||
case MU_CONFIG_CMD_EXTRACT: return mu_cmd_extract (opts, err);
|
||||
|
||||
case MU_CONFIG_CMD_FIND:
|
||||
return with_store (mu_cmd_find, opts, TRUE, err);
|
||||
case MU_CONFIG_CMD_INDEX:
|
||||
return with_store (mu_cmd_index, opts, FALSE, err);
|
||||
case MU_CONFIG_CMD_ADD:
|
||||
return with_store (mu_cmd_add, opts, FALSE, err);
|
||||
case MU_CONFIG_CMD_REMOVE:
|
||||
return with_store (mu_cmd_remove, opts, FALSE, err);
|
||||
case MU_CONFIG_CMD_SERVER:
|
||||
return with_store (mu_cmd_server, opts, FALSE, err);
|
||||
default:
|
||||
show_usage ();
|
||||
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
|
||||
"unknown command '%s'", opts->cmdstr);
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
}
|
||||
}
|
||||
165
mu/mu-cmd.h
Normal file
165
mu/mu-cmd.h
Normal file
@ -0,0 +1,165 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __MU_CMD_H__
|
||||
#define __MU_CMD_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <mu-config.h>
|
||||
#include <mu-store.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* execute the 'mkdir' command
|
||||
*
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeded,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_mkdir (MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the 'view' command
|
||||
*
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeded,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_view (MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the 'index' command
|
||||
*
|
||||
* @param store store object to use
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeded,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_index (MuStore *store, MuConfig *opt, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the 'find' command
|
||||
*
|
||||
* @param store store object to use
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds and
|
||||
* >MU_OK (0) results, MU_EXITCODE_NO_MATCHES if the command
|
||||
* succeeds but there no matches, some error code for all other errors
|
||||
*/
|
||||
MuError mu_cmd_find (MuStore *store, MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the 'extract' command
|
||||
*
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_extract (MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the 'mv' command
|
||||
*
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_mv (MuConfig *opts, GError **err);
|
||||
|
||||
/**
|
||||
* execute the cfind command
|
||||
*
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_cfind (MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the add command
|
||||
*
|
||||
* @param store store object to use
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_add (MuStore *store, MuConfig *opts, GError **err);
|
||||
|
||||
/**
|
||||
* execute the remove command
|
||||
*
|
||||
* @param store store object to use
|
||||
* @param opts configuration options
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err);
|
||||
|
||||
|
||||
/**
|
||||
* execute the server command
|
||||
* @param store store object to use
|
||||
* @param opts configuration options
|
||||
*
|
||||
* @return MU_OK (0) if the command succeeds,
|
||||
* some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_server (MuStore *store, MuConfig *opts,GError**/*unused*/);
|
||||
|
||||
/**
|
||||
* execute some mu command, based on 'opts'
|
||||
*
|
||||
* @param opts configuration option
|
||||
* @param err receives error information, or NULL
|
||||
*
|
||||
* @return MU_OK if all went wall, some error code otherwise
|
||||
*/
|
||||
MuError mu_cmd_execute (MuConfig *opts, GError **err);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /*__MU_CMD_H__*/
|
||||
536
mu/mu-config.c
Normal file
536
mu/mu-config.c
Normal file
@ -0,0 +1,536 @@
|
||||
/* -*-Mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h> /* memset */
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mu-util.h"
|
||||
#include "mu-config.h"
|
||||
#include "mu-cmd.h"
|
||||
|
||||
|
||||
static MuConfig MU_CONFIG;
|
||||
|
||||
#define DEFAULT_SUMMARY_LEN 5
|
||||
|
||||
|
||||
static MuConfigFormat
|
||||
get_output_format (const char *formatstr)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char* name;
|
||||
MuConfigFormat format;
|
||||
} formats [] = {
|
||||
{"mutt-alias", MU_CONFIG_FORMAT_MUTT_ALIAS},
|
||||
{"mutt-ab", MU_CONFIG_FORMAT_MUTT_AB},
|
||||
{"wl", MU_CONFIG_FORMAT_WL},
|
||||
{"csv", MU_CONFIG_FORMAT_CSV},
|
||||
{"org-contact", MU_CONFIG_FORMAT_ORG_CONTACT},
|
||||
{"bbdb", MU_CONFIG_FORMAT_BBDB},
|
||||
{"links", MU_CONFIG_FORMAT_LINKS},
|
||||
{"plain", MU_CONFIG_FORMAT_PLAIN},
|
||||
{"sexp", MU_CONFIG_FORMAT_SEXP},
|
||||
{"xml", MU_CONFIG_FORMAT_XML},
|
||||
{"xquery", MU_CONFIG_FORMAT_XQUERY}
|
||||
};
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(formats); i++)
|
||||
if (strcmp (formats[i].name, formatstr) == 0)
|
||||
return formats[i].format;
|
||||
|
||||
return MU_CONFIG_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_group_mu_defaults (void)
|
||||
{
|
||||
gchar *exp;
|
||||
|
||||
if (!MU_CONFIG.muhome)
|
||||
MU_CONFIG.muhome = mu_util_guess_mu_homedir();
|
||||
|
||||
exp = mu_util_dir_expand(MU_CONFIG.muhome);
|
||||
if (exp) {
|
||||
g_free(MU_CONFIG.muhome);
|
||||
MU_CONFIG.muhome = exp;
|
||||
}
|
||||
|
||||
/* check for the MU_NOCOLOR env var; but in any case don't
|
||||
* use colors unless we're writing to a tty */
|
||||
if (g_getenv (MU_NOCOLOR) != NULL)
|
||||
MU_CONFIG.nocolor = TRUE;
|
||||
|
||||
if (!isatty(fileno(stdout)))
|
||||
MU_CONFIG.nocolor = TRUE;
|
||||
|
||||
}
|
||||
|
||||
static GOptionGroup*
|
||||
config_options_group_mu (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"debug", 'd', 0, G_OPTION_ARG_NONE, &MU_CONFIG.debug,
|
||||
"print debug output to standard error (false)", NULL},
|
||||
{"quiet", 'q', 0, G_OPTION_ARG_NONE, &MU_CONFIG.quiet,
|
||||
"don't give any progress information (false)", NULL},
|
||||
{"version", 'v', 0, G_OPTION_ARG_NONE, &MU_CONFIG.version,
|
||||
"display version and copyright information (false)", NULL},
|
||||
{"muhome", 0, 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.muhome,
|
||||
"specify an alternative mu directory", NULL},
|
||||
{"log-stderr", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.log_stderr,
|
||||
"log to standard error (false)", NULL},
|
||||
{"nocolor", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.nocolor,
|
||||
"don't use ANSI-colors in some output (false)", NULL},
|
||||
|
||||
{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY,
|
||||
&MU_CONFIG.params, "parameters", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("mu", "general mu options", "", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
static void
|
||||
set_group_index_defaults (void)
|
||||
{
|
||||
char *exp;
|
||||
|
||||
if (!MU_CONFIG.maildir)
|
||||
MU_CONFIG.maildir = mu_util_guess_maildir ();
|
||||
|
||||
if (MU_CONFIG.maildir) {
|
||||
exp = mu_util_dir_expand(MU_CONFIG.maildir);
|
||||
if (exp) {
|
||||
g_free(MU_CONFIG.maildir);
|
||||
MU_CONFIG.maildir = exp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GOptionGroup*
|
||||
config_options_group_index (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir,
|
||||
"top of the maildir", NULL},
|
||||
{"reindex", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.reindex,
|
||||
"index even already indexed messages (false)", NULL},
|
||||
{"rebuild", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.rebuild,
|
||||
"rebuild the database from scratch (false)", NULL},
|
||||
{"autoupgrade", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.autoupgrade,
|
||||
"auto-upgrade the database with new mu versions (false)",
|
||||
NULL},
|
||||
{"nocleanup", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.nocleanup,
|
||||
"don't clean up the database after indexing (false)", NULL},
|
||||
{"xbatchsize", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.xbatchsize,
|
||||
"set transaction batchsize for xapian commits (0)", NULL},
|
||||
{"max-msg-size", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.max_msg_size,
|
||||
"set the maximum size for message files", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("index",
|
||||
"options for the 'index' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
static void
|
||||
set_group_find_defaults (void)
|
||||
{
|
||||
/* note, when no fields are specified, we use
|
||||
* date-from-subject, and sort descending by date. If fields
|
||||
* *are* specified, we sort in ascending order. */
|
||||
if (!MU_CONFIG.fields) {
|
||||
MU_CONFIG.fields = "d f s";
|
||||
if (!MU_CONFIG.sortfield)
|
||||
MU_CONFIG.sortfield = "d";
|
||||
}
|
||||
|
||||
if (!MU_CONFIG.formatstr) /* by default, use plain output */
|
||||
MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN;
|
||||
else
|
||||
MU_CONFIG.format =
|
||||
get_output_format (MU_CONFIG.formatstr);
|
||||
|
||||
if (MU_CONFIG.linksdir) {
|
||||
gchar *old = MU_CONFIG.linksdir;
|
||||
MU_CONFIG.linksdir = mu_util_dir_expand(MU_CONFIG.linksdir);
|
||||
if (!MU_CONFIG.linksdir) /* we'll check the dir later */
|
||||
MU_CONFIG.linksdir = old;
|
||||
else
|
||||
g_free(old);
|
||||
}
|
||||
|
||||
if ((MU_CONFIG.summary && !MU_CONFIG.summary_len)||
|
||||
(MU_CONFIG.summary_len < 1))
|
||||
MU_CONFIG.summary_len = DEFAULT_SUMMARY_LEN;
|
||||
}
|
||||
|
||||
static GOptionGroup*
|
||||
config_options_group_find (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"fields", 'f', 0, G_OPTION_ARG_STRING, &MU_CONFIG.fields,
|
||||
"fields to display in the output", NULL},
|
||||
{"sortfield", 's', 0, G_OPTION_ARG_STRING, &MU_CONFIG.sortfield,
|
||||
"field to sort on", NULL},
|
||||
{"threads", 't', 0, G_OPTION_ARG_NONE, &MU_CONFIG.threads,
|
||||
"show message threads", NULL},
|
||||
{"bookmark", 'b', 0, G_OPTION_ARG_STRING, &MU_CONFIG.bookmark,
|
||||
"use a bookmarked query", NULL},
|
||||
{"reverse", 'z', 0, G_OPTION_ARG_NONE, &MU_CONFIG.reverse,
|
||||
"sort in reverse (descending) order (z -> a)", NULL},
|
||||
{"summary", 'k', 0, G_OPTION_ARG_NONE, &MU_CONFIG.summary,
|
||||
"include a short summary of the message (false)", NULL},
|
||||
{"summary-len", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.summary_len,
|
||||
"use up to <n> lines for the summary (5)", NULL},
|
||||
{"linksdir", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.linksdir,
|
||||
"output as symbolic links to a target maildir", NULL},
|
||||
{"clearlinks", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.clearlinks,
|
||||
"clear old links before filling a linksdir (false)", NULL},
|
||||
{"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr,
|
||||
"output format ('plain'(*), 'links', 'xml',"
|
||||
"'sexp', 'xquery')", NULL},
|
||||
{"exec", 'e', 0, G_OPTION_ARG_STRING, &MU_CONFIG.exec,
|
||||
"execute command on each match message", NULL},
|
||||
{"include-unreable", 0, 0, G_OPTION_ARG_NONE,
|
||||
&MU_CONFIG.include_unreadable,
|
||||
"don't ignore messages without a disk file (false)", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("find",
|
||||
"options for the 'find' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
static GOptionGroup *
|
||||
config_options_group_mkdir (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"mode", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.dirmode,
|
||||
"set the mode (as in chmod), in octal notation", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* set dirmode before, because '0000' is a valid mode */
|
||||
MU_CONFIG.dirmode = 0755;
|
||||
|
||||
og = g_option_group_new("mkdir", "options for the 'mkdir' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_group_cfind_defaults (void)
|
||||
{
|
||||
if (!MU_CONFIG.formatstr) /* by default, use plain output */
|
||||
MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN;
|
||||
else
|
||||
MU_CONFIG.format = get_output_format (MU_CONFIG.formatstr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static GOptionGroup *
|
||||
config_options_group_cfind (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr,
|
||||
"output format ('plain'(*), 'mutt', 'wanderlust',"
|
||||
"'org-contact', 'csv')", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("cfind", "options for the 'cfind' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_group_view_defaults (void)
|
||||
{
|
||||
if (!MU_CONFIG.formatstr) /* by default, use plain output */
|
||||
MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN;
|
||||
else
|
||||
MU_CONFIG.format = get_output_format (MU_CONFIG.formatstr);
|
||||
|
||||
if ((MU_CONFIG.summary && !MU_CONFIG.summary_len)||
|
||||
(MU_CONFIG.summary_len < 1))
|
||||
MU_CONFIG.summary_len = DEFAULT_SUMMARY_LEN;
|
||||
}
|
||||
|
||||
static GOptionGroup *
|
||||
config_options_group_view (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"summary", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.summary,
|
||||
"only show a short summary of the message (false)", NULL},
|
||||
{"summary-len", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.summary_len,
|
||||
"use up to <n> lines for the summary (5)", NULL},
|
||||
{"terminate", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.terminator,
|
||||
"terminate messages with ascii-0x07 (\\f, form-feed)", NULL},
|
||||
{"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr,
|
||||
"output format ('plain'(*), 'sexp')", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("view", "options for the 'view' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GOptionGroup*
|
||||
config_options_group_extract (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"save-attachments", 'a', 0, G_OPTION_ARG_NONE,
|
||||
&MU_CONFIG.save_attachments,
|
||||
"save all attachments (false)", NULL},
|
||||
{"save-all", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.save_all,
|
||||
"save all parts (incl. non-attachments) (false)", NULL},
|
||||
{"parts", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.parts,
|
||||
"save specific parts (comma-separated list)", NULL},
|
||||
{"target-dir", 0, 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.targetdir,
|
||||
"target directory for saving", NULL},
|
||||
{"overwrite", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.overwrite,
|
||||
"overwrite existing files (false)", NULL},
|
||||
{"play", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.play,
|
||||
"try to 'play' (open) the extracted parts", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
MU_CONFIG.targetdir = g_strdup("."); /* default is the current dir */
|
||||
|
||||
og = g_option_group_new("extract",
|
||||
"options for the 'extract' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
|
||||
static GOptionGroup*
|
||||
config_options_group_server (void)
|
||||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir,
|
||||
"top of the maildir", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("server",
|
||||
"options for the 'server' command",
|
||||
"", NULL, NULL);
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
parse_cmd (int *argcp, char ***argvp)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const gchar* _name;
|
||||
MuConfigCmd _cmd;
|
||||
} cmd_map[] = {
|
||||
{ "cfind", MU_CONFIG_CMD_CFIND },
|
||||
{ "extract", MU_CONFIG_CMD_EXTRACT },
|
||||
{ "find", MU_CONFIG_CMD_FIND },
|
||||
{ "index", MU_CONFIG_CMD_INDEX },
|
||||
{ "mkdir", MU_CONFIG_CMD_MKDIR },
|
||||
{ "view", MU_CONFIG_CMD_VIEW },
|
||||
{ "add", MU_CONFIG_CMD_ADD },
|
||||
{ "remove", MU_CONFIG_CMD_REMOVE },
|
||||
{ "server", MU_CONFIG_CMD_SERVER }
|
||||
};
|
||||
|
||||
MU_CONFIG.cmd = MU_CONFIG_CMD_NONE;
|
||||
MU_CONFIG.cmdstr = NULL;
|
||||
|
||||
if (*argcp < 2) /* no command found at all */
|
||||
return TRUE;
|
||||
else if ((**argvp)[1] == '-')
|
||||
/* if the first param starts with '-', there is no
|
||||
* command, just some option (like --version, --help
|
||||
* etc.)*/
|
||||
return TRUE;
|
||||
|
||||
MU_CONFIG.cmd = MU_CONFIG_CMD_UNKNOWN;
|
||||
MU_CONFIG.cmdstr = (*argvp)[1];
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(cmd_map); ++i)
|
||||
if (strcmp (MU_CONFIG.cmdstr, cmd_map[i]._name) == 0)
|
||||
MU_CONFIG.cmd = cmd_map[i]._cmd;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_context_group (GOptionContext *context)
|
||||
{
|
||||
GOptionGroup *group;
|
||||
|
||||
switch (MU_CONFIG.cmd) {
|
||||
case MU_CONFIG_CMD_INDEX:
|
||||
group = config_options_group_index();
|
||||
break;
|
||||
case MU_CONFIG_CMD_FIND:
|
||||
group = config_options_group_find();
|
||||
break;
|
||||
case MU_CONFIG_CMD_MKDIR:
|
||||
group = config_options_group_mkdir();
|
||||
break;
|
||||
case MU_CONFIG_CMD_EXTRACT:
|
||||
group = config_options_group_extract();
|
||||
break;
|
||||
case MU_CONFIG_CMD_CFIND:
|
||||
group = config_options_group_cfind();
|
||||
break;
|
||||
case MU_CONFIG_CMD_VIEW:
|
||||
group = config_options_group_view();
|
||||
break;
|
||||
case MU_CONFIG_CMD_SERVER:
|
||||
group = config_options_group_server();
|
||||
break;
|
||||
default:
|
||||
return; /* no group to add */
|
||||
}
|
||||
|
||||
g_option_context_add_group(context, group);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
parse_params (int *argcp, char ***argvp)
|
||||
{
|
||||
GError *err = NULL;
|
||||
GOptionContext *context;
|
||||
gboolean rv;
|
||||
|
||||
context = g_option_context_new("- mu general option");
|
||||
g_option_context_set_main_group(context, config_options_group_mu());
|
||||
|
||||
add_context_group (context);
|
||||
|
||||
rv = g_option_context_parse (context, argcp, argvp, &err);
|
||||
g_option_context_free (context);
|
||||
if (!rv) {
|
||||
g_printerr ("mu: error in options: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
MuConfig*
|
||||
mu_config_init (int *argcp, char ***argvp)
|
||||
{
|
||||
g_return_val_if_fail (argcp && argvp, NULL);
|
||||
|
||||
memset (&MU_CONFIG, 0, sizeof(MU_CONFIG));
|
||||
|
||||
if (!parse_cmd (argcp, argvp) ||
|
||||
!parse_params(argcp, argvp)) {
|
||||
mu_config_uninit (&MU_CONFIG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* fill in the defaults if user did not specify */
|
||||
set_group_mu_defaults();
|
||||
set_group_index_defaults();
|
||||
set_group_find_defaults();
|
||||
set_group_cfind_defaults();
|
||||
set_group_view_defaults();
|
||||
/* set_group_mkdir_defaults (config); */
|
||||
|
||||
return &MU_CONFIG;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mu_config_uninit (MuConfig *opts)
|
||||
{
|
||||
if (!opts)
|
||||
return;
|
||||
|
||||
g_free (opts->muhome);
|
||||
g_free (opts->maildir);
|
||||
g_free (opts->linksdir);
|
||||
g_free (opts->targetdir);
|
||||
|
||||
g_strfreev (opts->params);
|
||||
|
||||
memset (opts, 0, sizeof(MU_CONFIG));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
mu_config_param_num (MuConfig *opts)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
g_return_val_if_fail (opts && opts->params, 0);
|
||||
for (n = 0; opts->params[n]; ++n);
|
||||
|
||||
return n;
|
||||
}
|
||||
201
mu/mu-config.h
Normal file
201
mu/mu-config.h
Normal file
@ -0,0 +1,201 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __MU_CONFIG_H__
|
||||
#define __MU_CONFIG_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/types.h> /* for mode_t */
|
||||
#include <mu-msg-fields.h>
|
||||
#include <mu-util.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* env var; if non-empty, color are disabled */
|
||||
#define MU_NOCOLOR "MU_NOCOLOR"
|
||||
|
||||
|
||||
enum _MuConfigFormat {
|
||||
MU_CONFIG_FORMAT_UNKNOWN = 0,
|
||||
|
||||
/* for cfind, find, view */
|
||||
MU_CONFIG_FORMAT_PLAIN, /* plain output */
|
||||
|
||||
/* for cfind */
|
||||
MU_CONFIG_FORMAT_MUTT_ALIAS, /* mutt alias style */
|
||||
MU_CONFIG_FORMAT_MUTT_AB, /* mutt ext abook */
|
||||
MU_CONFIG_FORMAT_WL, /* Wanderlust abook */
|
||||
MU_CONFIG_FORMAT_CSV, /* comma-sep'd values */
|
||||
MU_CONFIG_FORMAT_ORG_CONTACT, /* org-contact */
|
||||
MU_CONFIG_FORMAT_BBDB, /* BBDB */
|
||||
|
||||
/* for find, view */
|
||||
MU_CONFIG_FORMAT_SEXP, /* output sexps (emacs) */
|
||||
|
||||
/* for find */
|
||||
MU_CONFIG_FORMAT_LINKS, /* output as symlinks */
|
||||
MU_CONFIG_FORMAT_XML, /* output xml */
|
||||
MU_CONFIG_FORMAT_XQUERY /* output the xapian query */
|
||||
};
|
||||
typedef enum _MuConfigFormat MuConfigFormat;
|
||||
|
||||
|
||||
enum _MuConfigCmd {
|
||||
MU_CONFIG_CMD_UNKNOWN = 0,
|
||||
|
||||
MU_CONFIG_CMD_INDEX,
|
||||
MU_CONFIG_CMD_FIND,
|
||||
MU_CONFIG_CMD_MKDIR,
|
||||
MU_CONFIG_CMD_VIEW,
|
||||
MU_CONFIG_CMD_EXTRACT,
|
||||
MU_CONFIG_CMD_CFIND,
|
||||
MU_CONFIG_CMD_ADD,
|
||||
MU_CONFIG_CMD_REMOVE,
|
||||
MU_CONFIG_CMD_SERVER,
|
||||
|
||||
MU_CONFIG_CMD_NONE
|
||||
};
|
||||
typedef enum _MuConfigCmd MuConfigCmd;
|
||||
|
||||
|
||||
/* struct with all configuration options for mu; it will be filled
|
||||
* from the config file, and/or command line arguments */
|
||||
|
||||
struct _MuConfig {
|
||||
|
||||
MuConfigCmd cmd; /* the command, or
|
||||
* MU_CONFIG_CMD_NONE */
|
||||
const char *cmdstr; /* cmd string, for user
|
||||
* info */
|
||||
|
||||
/* general options */
|
||||
gboolean quiet; /* don't give any output */
|
||||
gboolean debug; /* spew out debug info */
|
||||
char *muhome; /* the House of Mu */
|
||||
gboolean version; /* request mu version */
|
||||
gboolean log_stderr; /* log to stderr (not logfile) */
|
||||
gchar** params; /* parameters (for querying) */
|
||||
gboolean nocolor; /* don't use use ansi-colors
|
||||
* in some output */
|
||||
|
||||
/* options for indexing */
|
||||
char *maildir; /* where the mails are */
|
||||
gboolean nocleanup; /* don't cleanup del'd mails from db */
|
||||
gboolean reindex; /* re-index existing mails */
|
||||
gboolean rebuild; /* empty the database before indexing */
|
||||
gboolean autoupgrade; /* automatically upgrade db
|
||||
* when needed */
|
||||
int xbatchsize; /* batchsize for xapian
|
||||
* commits, or 0 for
|
||||
* default */
|
||||
int max_msg_size; /* maximum size for message files */
|
||||
|
||||
/* options for querying 'find' (and view-> 'summary') */
|
||||
char *fields; /* fields to show in output */
|
||||
char *sortfield; /* field to sort by (string) */
|
||||
gboolean reverse; /* sort in revers order (z->a) */
|
||||
gboolean threads; /* show message threads */
|
||||
|
||||
gboolean summary; /* include a summary? */
|
||||
int summary_len; /* max # of lines for summary */
|
||||
|
||||
char *bookmark; /* use bookmark */
|
||||
char *formatstr; /* output type for find
|
||||
* (plain,links,xml,json,sexp)
|
||||
* and view (plain, sexp) and cfind
|
||||
*/
|
||||
MuConfigFormat format; /* the decoded formatstr */
|
||||
char *exec; /* command to execute on the
|
||||
* files for the matched
|
||||
* messages */
|
||||
gboolean include_unreadable; /* don't ignore messages
|
||||
* without a disk file */
|
||||
|
||||
/* options for view */
|
||||
gboolean terminator; /* add separator \f between
|
||||
* multiple messages in mu
|
||||
* view */
|
||||
|
||||
/* output to a maildir with symlinks */
|
||||
char *linksdir; /* maildir to output symlinks */
|
||||
gboolean clearlinks; /* clear a linksdir before filling */
|
||||
mode_t dirmode; /* mode for the created maildir */
|
||||
|
||||
/* options for extracting parts */
|
||||
gboolean *save_all; /* extract all parts */
|
||||
gboolean *save_attachments; /* extract all attachment parts */
|
||||
gchar *parts; /* comma-sep'd list of parts
|
||||
* to save / open */
|
||||
char *targetdir; /* where to save the attachments */
|
||||
gboolean overwrite; /* should we overwrite same-named files */
|
||||
gboolean play; /* after saving, try to 'play'
|
||||
* (open) the attmnt using xdgopen */
|
||||
};
|
||||
typedef struct _MuConfig MuConfig;
|
||||
|
||||
/**
|
||||
* initialize a mu config object
|
||||
*
|
||||
* set default values for the configuration options; when you call
|
||||
* mu_config_init, you should also call mu_config_uninit when the data
|
||||
* is no longer needed.
|
||||
*
|
||||
* Note that is _static_ data, ie., mu_config_init will always return
|
||||
* the same pointer
|
||||
*
|
||||
* @param opts options
|
||||
*/
|
||||
MuConfig *mu_config_init (int *argcp, char ***argvp)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* free the MuConfig structure
|
||||
*
|
||||
* @param opts a MuConfig struct, or NULL
|
||||
*/
|
||||
void mu_config_uninit (MuConfig *conf);
|
||||
|
||||
|
||||
/**
|
||||
* execute the command / options in this config
|
||||
*
|
||||
* @param opts a MuConfig struct
|
||||
*
|
||||
* @return a value denoting the success/failure of the execution;
|
||||
* MU_ERROR_NONE (0) for success, non-zero for a failure. This is to used for
|
||||
* the exit code of the process
|
||||
*
|
||||
*/
|
||||
MuError mu_config_execute (MuConfig *conf);
|
||||
|
||||
|
||||
/**
|
||||
* count the number of non-option parameters
|
||||
*
|
||||
* @param opts a MuConfig struct
|
||||
*
|
||||
* @return the number of non-option parameters, or 0 in case of error
|
||||
*/
|
||||
size_t mu_config_param_num (MuConfig *conf);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /*__MU_CONFIG_H__*/
|
||||
98
mu/mu.cc
Normal file
98
mu/mu.cc
Normal file
@ -0,0 +1,98 @@
|
||||
/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "mu-config.h"
|
||||
#include "mu-cmd.h"
|
||||
#include "mu-runtime.h"
|
||||
|
||||
|
||||
static void
|
||||
handle_error (GError *err)
|
||||
{
|
||||
const char *advise;
|
||||
|
||||
if (!err)
|
||||
return; /* nothing to do */
|
||||
|
||||
advise = NULL;
|
||||
|
||||
switch (err->code) {
|
||||
|
||||
case MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK:
|
||||
advise = "maybe mu is already running?";
|
||||
break;
|
||||
|
||||
case MU_ERROR_XAPIAN_CORRUPTION:
|
||||
case MU_ERROR_XAPIAN_NOT_UP_TO_DATE:
|
||||
advise = "please try 'mu index --rebuild'";
|
||||
break;
|
||||
case MU_ERROR_XAPIAN_IS_EMPTY:
|
||||
advise = "please try 'mu index'";
|
||||
break;
|
||||
default:
|
||||
break; /* nothing to do */
|
||||
}
|
||||
|
||||
g_warning ("%s", err->message);
|
||||
if (advise)
|
||||
g_message ("%s", advise);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GError *err;
|
||||
MuError rv;
|
||||
MuConfig *conf;
|
||||
|
||||
setlocale (LC_ALL, "");
|
||||
g_type_init ();
|
||||
|
||||
conf = mu_config_init (&argc, &argv);
|
||||
if (!conf)
|
||||
return 1;
|
||||
|
||||
if (!mu_runtime_init (conf->muhome, PACKAGE_NAME)) {
|
||||
mu_config_uninit (conf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = NULL;
|
||||
rv = mu_cmd_execute (conf, &err);
|
||||
|
||||
handle_error (err);
|
||||
g_clear_error (&err);
|
||||
|
||||
|
||||
mu_config_uninit (conf);
|
||||
mu_runtime_uninit ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
387
mu/tests/test-mu-cmd-cfind.c
Normal file
387
mu/tests/test-mu-cmd-cfind.c
Normal file
@ -0,0 +1,387 @@
|
||||
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
**
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "../mu-query.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "mu-store.h"
|
||||
|
||||
static gchar*
|
||||
fill_contacts_cache (void)
|
||||
{
|
||||
gchar *cmdline, *tmpdir;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s"
|
||||
" --quiet",
|
||||
MU_PROGRAM, tmpdir, MU_TESTMAILDIR);
|
||||
if (g_test_verbose())
|
||||
g_print ("%s\n", cmdline);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
NULL, NULL));
|
||||
g_free (cmdline);
|
||||
|
||||
return tmpdir;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_cfind_plain (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=plain "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
if (g_test_verbose())
|
||||
g_print ("%s\n", cmdline);
|
||||
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
/* note, output order is unspecified */
|
||||
g_assert (output);
|
||||
if (output[0] == 'H')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Helmut Kröger hk@testmu.xxx\n"
|
||||
"Mü testmu@testmu.xx\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Mü testmu@testmu.xx\n"
|
||||
"Helmut Kröger hk@testmu.xxx\n");
|
||||
|
||||
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_cfind_bbdb (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput, *expected;
|
||||
gchar today[12];
|
||||
const char* frm1;
|
||||
const char *frm2;
|
||||
struct tm *tmtoday;
|
||||
time_t now;
|
||||
const char *old_tz;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
old_tz = set_tz ("Europe/Helsinki");
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=bbdb "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
frm1 = ";; -*-coding: utf-8-emacs;-*-\n"
|
||||
";;; file-version: 6\n"
|
||||
"[\"Helmut\" \"Kröger\" nil nil nil nil (\"hk@testmu.xxx\") "
|
||||
"((creation-date . \"%s\") "
|
||||
"(time-stamp . \"1970-01-01\")) nil]\n"
|
||||
"[\"Mü\" \"\" nil nil nil nil (\"testmu@testmu.xx\") "
|
||||
"((creation-date . \"%s\") "
|
||||
"(time-stamp . \"1970-01-01\")) nil]\n";
|
||||
|
||||
|
||||
frm2 = ";; -*-coding: utf-8-emacs;-*-\n"
|
||||
";;; file-version: 6\n"
|
||||
"[\"Mü\" \"\" nil nil nil nil (\"testmu@testmu.xx\") "
|
||||
"((creation-date . \"%s\") "
|
||||
"(time-stamp . \"1970-01-01\")) nil]\n"
|
||||
"[\"Helmut\" \"Kröger\" nil nil nil nil (\"hk@testmu.xxx\") "
|
||||
"((creation-date . \"%s\") "
|
||||
"(time-stamp . \"1970-01-01\")) nil]\n";
|
||||
|
||||
g_assert (output);
|
||||
|
||||
now = time(NULL);
|
||||
tmtoday = localtime(&now);
|
||||
strftime(today,sizeof(today),"%Y-%m-%d", tmtoday);
|
||||
|
||||
expected = g_strdup_printf (output[52] == 'H' ? frm1 : frm2,
|
||||
today, today);
|
||||
|
||||
/* g_print ("\n%s\n", output); */
|
||||
|
||||
g_assert_cmpstr (output, ==, expected);
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
g_free (expected);
|
||||
|
||||
set_tz (old_tz);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_cfind_wl (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=wl "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
g_assert (output);
|
||||
if (output[0] == 'h')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"hk@testmu.xxx \"HelmutK\" \"Helmut Kröger\"\n"
|
||||
"testmu@testmu.xx \"Mü\" \"Mü\"\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"testmu@testmu.xx \"Mü\" \"Mü\"\n"
|
||||
"hk@testmu.xxx \"HelmutK\" \"Helmut Kröger\"\n");
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_cfind_mutt_alias (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=mutt-alias "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
/* both orders are possible... */
|
||||
g_assert (output);
|
||||
|
||||
if (output[6] == 'H')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"alias HelmutK Helmut Kröger <hk@testmu.xxx>\n"
|
||||
"alias Mü Mü <testmu@testmu.xx>\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"alias Mü Mü <testmu@testmu.xx>\n"
|
||||
"alias HelmutK Helmut Kröger <hk@testmu.xxx>\n");
|
||||
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_cfind_mutt_ab (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=mutt-ab "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
g_assert (output);
|
||||
if (output[39] == 'h')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Matching addresses in the mu database:\n"
|
||||
"hk@testmu.xxx\tHelmut Kröger\t\n"
|
||||
"testmu@testmu.xx\tMü\t\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Matching addresses in the mu database:\n"
|
||||
"testmu@testmu.xx\tMü\t\n"
|
||||
"hk@testmu.xxx\tHelmut Kröger\t\n");
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_cfind_org_contact (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=org-contact "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
g_assert (output);
|
||||
|
||||
if (output[2] == 'H')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"* Helmut Kröger\n"
|
||||
":PROPERTIES:\n"
|
||||
":EMAIL: hk@testmu.xxx\n"
|
||||
":END:\n\n"
|
||||
"* Mü\n"
|
||||
":PROPERTIES:\n"
|
||||
":EMAIL: testmu@testmu.xx\n"
|
||||
":END:\n\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"* Mü\n"
|
||||
":PROPERTIES:\n"
|
||||
":EMAIL: testmu@testmu.xx\n"
|
||||
":END:\n\n"
|
||||
"* Helmut Kröger\n"
|
||||
":PROPERTIES:\n"
|
||||
":EMAIL: hk@testmu.xxx\n"
|
||||
":END:\n\n");
|
||||
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_cfind_csv (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_contacts_cache ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
cmdline = g_strdup_printf ("%s cfind --muhome=%s --format=csv "
|
||||
"'testmu\\.xxx?'",
|
||||
MU_PROGRAM, muhome);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
g_assert (output);
|
||||
if (output[0] == 'H')
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Helmut Kröger,hk@testmu.xxx\n"
|
||||
"Mü,testmu@testmu.xx\n");
|
||||
else
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"Mü,testmu@testmu.xx\n"
|
||||
"Helmut Kröger,hk@testmu.xxx\n");
|
||||
|
||||
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int rv;
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
if (!set_en_us_utf8_locale())
|
||||
return 0; /* don't error out... */
|
||||
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-plain", test_mu_cfind_plain);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-bbdb", test_mu_cfind_bbdb);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-wl", test_mu_cfind_wl);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-mutt-alias",
|
||||
test_mu_cfind_mutt_alias);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-mutt-ab",
|
||||
test_mu_cfind_mutt_ab);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-org-contact",
|
||||
test_mu_cfind_org_contact);
|
||||
g_test_add_func ("/mu-cmd-cfind/test-mu-cfind-csv",
|
||||
test_mu_cfind_csv);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING|
|
||||
G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
|
||||
rv = g_test_run ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
803
mu/tests/test-mu-cmd.c
Normal file
803
mu/tests/test-mu-cmd.c
Normal file
@ -0,0 +1,803 @@
|
||||
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
**
|
||||
** Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "../mu-query.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "mu-store.h"
|
||||
|
||||
|
||||
/* tests for the command line interface, uses testdir2 */
|
||||
|
||||
static gchar*
|
||||
fill_database (void)
|
||||
{
|
||||
gchar *cmdline, *tmpdir;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s"
|
||||
" --quiet",
|
||||
MU_PROGRAM,
|
||||
tmpdir, MU_TESTMAILDIR2);
|
||||
if (g_test_verbose())
|
||||
g_print ("%s\n", cmdline);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
NULL, NULL));
|
||||
g_free (cmdline);
|
||||
|
||||
return tmpdir;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
newlines_in_output (const char* str)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
|
||||
while (str && *str) {
|
||||
if (*str == '\n')
|
||||
++count;
|
||||
++str;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
search (const char* query, unsigned expected)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput;
|
||||
|
||||
muhome = fill_database ();
|
||||
g_assert (muhome);
|
||||
|
||||
cmdline = g_strdup_printf ("%s find --muhome=%s %s",
|
||||
MU_PROGRAM, muhome, query);
|
||||
|
||||
if (g_test_verbose())
|
||||
g_printerr ("%s\n", cmdline);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline,
|
||||
&output, &erroutput,
|
||||
NULL, NULL));
|
||||
if (g_test_verbose())
|
||||
g_print ("\nOutput:\n%s", output);
|
||||
|
||||
g_assert_cmpuint (newlines_in_output(output),==,expected);
|
||||
|
||||
|
||||
/* we expect zero lines of error output if there is a match;
|
||||
* otherwise there should be one line 'No matches found' */
|
||||
/* g_assert_cmpuint (newlines_in_output(erroutput),==, */
|
||||
/* expected == 0 ? 1 : 0); */
|
||||
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
}
|
||||
|
||||
/* index testdir2, and make sure it adds two documents */
|
||||
static void
|
||||
test_mu_index (void)
|
||||
{
|
||||
MuStore *store;
|
||||
gchar *muhome, *xpath;
|
||||
|
||||
muhome = fill_database ();
|
||||
g_assert (muhome != NULL);
|
||||
|
||||
xpath = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, "xapian");
|
||||
|
||||
store = mu_store_new_read_only (xpath, NULL);
|
||||
g_assert (store);
|
||||
|
||||
g_assert_cmpuint (mu_store_count (store, NULL), ==, 12);
|
||||
mu_store_unref (store);
|
||||
|
||||
g_free (muhome);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_empty_query (void)
|
||||
{
|
||||
search ("\"\"", 12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_01 (void)
|
||||
{
|
||||
search ("f:john fruit", 1);
|
||||
search ("f:soc@example.com", 1);
|
||||
search ("t:alki@example.com", 1);
|
||||
search ("t:alcibiades", 1);
|
||||
search ("f:soc@example.com OR f:john", 2);
|
||||
search ("f:soc@example.com OR f:john OR t:edmond", 3);
|
||||
search ("t:julius", 1);
|
||||
search ("s:dude", 1);
|
||||
search ("t:dantès", 1);
|
||||
}
|
||||
|
||||
|
||||
/* index testdir2, and make sure it adds two documents */
|
||||
static void
|
||||
test_mu_find_02 (void)
|
||||
{
|
||||
search ("bull", 1);
|
||||
search ("bull m:foo", 0);
|
||||
search ("bull m:/foo", 1);
|
||||
search ("bull m:/Foo", 1);
|
||||
search ("bull flag:a", 1);
|
||||
search ("g:x", 0);
|
||||
search ("flag:encrypted", 0);
|
||||
search ("flag:attach", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_file (void)
|
||||
{
|
||||
search ("file:sittingbull.jpg", 1);
|
||||
search ("file:custer.jpg", 1);
|
||||
search ("file:custer.*", 1);
|
||||
search ("j:sit*", 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_mime (void)
|
||||
{
|
||||
search ("mime:image/jpeg", 1);
|
||||
search ("mime:text/plain", 12);
|
||||
search ("y:text*", 12);
|
||||
search ("y:image*", 1);
|
||||
search ("mime:message/rfc822", 2);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_text_in_rfc822 (void)
|
||||
{
|
||||
search ("embed:dancing", 1);
|
||||
search ("e:curious", 1);
|
||||
search ("embed:with", 2);
|
||||
search ("e:karjala", 0);
|
||||
search ("embed:navigation", 1);
|
||||
}
|
||||
|
||||
|
||||
/* some more tests */
|
||||
static void
|
||||
test_mu_find_03 (void)
|
||||
{
|
||||
search ("bull", 1);
|
||||
search ("bull m:foo", 0);
|
||||
search ("bull m:/foo", 1);
|
||||
search ("i:3BE9E6535E0D852173@emss35m06.us.lmco.com", 1);
|
||||
}
|
||||
|
||||
|
||||
static void /* error cases */
|
||||
test_mu_find_04 (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *erroutput;
|
||||
|
||||
muhome = fill_database ();
|
||||
g_assert (muhome);
|
||||
|
||||
cmdline = g_strdup_printf ("%s --muhome=%cfoo%cbar%cnonexistent "
|
||||
"find f:socrates",
|
||||
MU_PROGRAM,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, &erroutput,
|
||||
NULL, NULL));
|
||||
|
||||
/* we expect multiple lines of error output */
|
||||
g_assert_cmpuint (newlines_in_output(erroutput),>=,1);
|
||||
|
||||
g_free (erroutput);
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_find_links (void)
|
||||
{
|
||||
gchar *muhome, *cmdline, *output, *erroutput, *tmpdir;
|
||||
|
||||
|
||||
muhome = fill_database ();
|
||||
g_assert (muhome);
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
|
||||
cmdline = g_strdup_printf (
|
||||
"%s find --muhome=%s --format=links --linksdir=%s "
|
||||
"mime:message/rfc822", MU_PROGRAM, muhome, tmpdir);
|
||||
|
||||
if (g_test_verbose())
|
||||
g_printerr ("%s\n", cmdline);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline,
|
||||
&output, &erroutput,
|
||||
NULL, NULL));
|
||||
if (g_test_verbose())
|
||||
g_print ("\nOutput:\n%s", output);
|
||||
|
||||
/* there should be no errors */
|
||||
g_assert_cmpuint (newlines_in_output(output),==,0);
|
||||
g_assert_cmpuint (newlines_in_output(erroutput),==,0);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
|
||||
|
||||
|
||||
/* now we try again, we should get 2 + 1 lines of error output,
|
||||
* because the target files already exist */
|
||||
g_assert (g_spawn_command_line_sync (cmdline,
|
||||
&output, &erroutput,
|
||||
NULL, NULL));
|
||||
if (g_test_verbose())
|
||||
g_print ("\nOutput:\n%s", output);
|
||||
|
||||
g_assert_cmpuint (newlines_in_output(output),==,0);
|
||||
g_assert_cmpuint (newlines_in_output(erroutput),==,3);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
|
||||
/* now we try again with --clearlinks, and the we should be
|
||||
* back to 0 errors */
|
||||
g_free (cmdline);
|
||||
cmdline = g_strdup_printf (
|
||||
"%s find --muhome=%s --format=links --linksdir=%s --clearlinks "
|
||||
"mime:message/rfc822", MU_PROGRAM, muhome, tmpdir);
|
||||
g_assert (g_spawn_command_line_sync (cmdline,
|
||||
&output, &erroutput,
|
||||
NULL, NULL));
|
||||
if (g_test_verbose())
|
||||
g_print ("\nOutput:\n%s", output);
|
||||
g_assert_cmpuint (newlines_in_output(output),==,0);
|
||||
g_assert_cmpuint (newlines_in_output(erroutput),==,0);
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (muhome);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* some more tests */
|
||||
static void
|
||||
test_mu_find_maildir_special (void)
|
||||
{
|
||||
/* ensure that maldirs with spaces in their names work... */
|
||||
search ("\"maildir:/wom bat\" subject:atoms", 1);
|
||||
search ("\"maildir:/wOm_bàT\"", 3);
|
||||
search ("\"maildir:/wOm*\"", 3);
|
||||
search ("\"maildir:/wOm *\"", 3);
|
||||
search ("\"maildir:wom bat\"", 0);
|
||||
search ("\"maildir:/wombat\"", 0);
|
||||
search ("subject:atoms", 1);
|
||||
}
|
||||
|
||||
|
||||
/* static void */
|
||||
/* test_mu_find_mime_types (void) */
|
||||
/* { */
|
||||
/* /\* ensure that maldirs with spaces in their names work... *\/ */
|
||||
/* search ("\"maildir:/wom bat\" subject:atoms", 1); */
|
||||
/* search ("\"maildir:/wOm_bàT\"", 3); */
|
||||
/* search ("subject:atoms", 1); */
|
||||
/* } */
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_extract_01 (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 --muhome=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
|
||||
if (g_test_verbose())
|
||||
g_print ("cmd: %s\n", cmdline);
|
||||
|
||||
output = erroutput = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, &erroutput,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output,
|
||||
==,
|
||||
"MIME-parts in this message:\n"
|
||||
" 0 <none> text/plain [<none>] (0.0 kB)\n"
|
||||
" 1 sittingbull.jpg image/jpeg [inline] (23.9 kB)\n"
|
||||
" 2 custer.jpg image/jpeg [inline] (21.6 kB)\n");
|
||||
|
||||
/* we expect zero lines of error output */
|
||||
g_assert_cmpuint (newlines_in_output(erroutput),==,0);
|
||||
|
||||
g_free (output);
|
||||
g_free (erroutput);
|
||||
g_free (cmdline);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
static gint64
|
||||
get_file_size (const char* path)
|
||||
{
|
||||
int rv;
|
||||
struct stat statbuf;
|
||||
|
||||
rv = stat (path, &statbuf);
|
||||
if (rv != 0)
|
||||
return -1;
|
||||
|
||||
return (gint64)statbuf.st_size;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_extract_02 (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
gchar *att1, *att2;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s extract --muhome=%s -a "
|
||||
"--target-dir=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, ==, "");
|
||||
|
||||
att1 = g_strdup_printf ("%s%ccuster.jpg", tmpdir, G_DIR_SEPARATOR);
|
||||
att2 = g_strdup_printf ("%s%csittingbull.jpg", tmpdir, G_DIR_SEPARATOR);
|
||||
|
||||
g_assert_cmpint (get_file_size(att1),==,15960);
|
||||
g_assert_cmpint (get_file_size(att2),==,17674);
|
||||
|
||||
g_free (output);
|
||||
g_free (tmpdir);
|
||||
g_free (cmdline);
|
||||
g_free (att1);
|
||||
g_free (att2);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_extract_03 (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
gchar *att1, *att2;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s extract --muhome=%s --parts 2 "
|
||||
"--target-dir=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, ==, "");
|
||||
|
||||
att1 = g_strdup_printf ("%s%ccuster.jpg", tmpdir, G_DIR_SEPARATOR);
|
||||
att2 = g_strdup_printf ("%s%csittingbull.jpg", tmpdir, G_DIR_SEPARATOR);
|
||||
|
||||
g_assert_cmpint (get_file_size(att1),==,15960); /* should not exist */
|
||||
g_assert_cmpint (get_file_size(att2),==,-1);
|
||||
|
||||
g_free (output);
|
||||
g_free (tmpdir);
|
||||
g_free (cmdline);
|
||||
g_free (att1);
|
||||
g_free (att2);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_extract_overwrite (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 --muhome=%s -a "
|
||||
"--target-dir=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM, tmpdir, 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 --muhome=%s -a --overwrite "
|
||||
"--target-dir=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM, tmpdir, 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
|
||||
test_mu_extract_by_name (void)
|
||||
{
|
||||
gchar *cmdline, *output, *erroutput, *tmpdir, *path;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s extract --muhome=%s "
|
||||
"--target-dir=%s %s%cFoo%ccur%cmail5 "
|
||||
"sittingbull.jpg",
|
||||
MU_PROGRAM, tmpdir, 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, ==, "");
|
||||
path = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR,
|
||||
"sittingbull.jpg");
|
||||
g_assert (access (path, F_OK) == 0);
|
||||
g_free (path);
|
||||
|
||||
g_free (erroutput);
|
||||
g_free (output);
|
||||
|
||||
g_free (tmpdir);
|
||||
g_free (cmdline);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_view_01 (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
int len;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s view --muhome=%s %s%cbar%ccur%cmail4",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, !=, NULL);
|
||||
|
||||
/*
|
||||
* note: there are two possibilities here; older versions of
|
||||
* GMime will produce:
|
||||
*
|
||||
* From: "=?iso-8859-1?Q? =F6tzi ?=" <oetzi@web.de>
|
||||
*
|
||||
* while newer ones return something like:
|
||||
*
|
||||
* From: ?tzi <oetzi@web.de>
|
||||
*
|
||||
* or even
|
||||
*
|
||||
* From: \xc3\xb6tzi <oetzi@web.de>
|
||||
*
|
||||
* both are 'okay' from mu's perspective; it'd be even better
|
||||
* to have some #ifdefs for the GMime versions, but this
|
||||
* should work for now
|
||||
*
|
||||
* Added 350 as 'okay', which comes with gmime 2.4.24 (ubuntu 10.04)
|
||||
*/
|
||||
len = strlen(output);
|
||||
/* g_print ("\n[%s] (%d)\n", output, len); */
|
||||
g_assert (len > 349);
|
||||
|
||||
g_free (output);
|
||||
g_free (cmdline);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_view_multi (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
int len;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s view --muhome=%s "
|
||||
"%s%cbar%ccur%cmail5 "
|
||||
"%s%cbar%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, !=, NULL);
|
||||
|
||||
len = strlen(output);
|
||||
/* g_print ("\n[%s](%u)\n", output, len); */
|
||||
g_assert_cmpuint (len,>,150);
|
||||
|
||||
g_free (output);
|
||||
g_free (cmdline);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_view_multi_separate (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
int len;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s view --terminate --muhome=%s "
|
||||
"%s%cbar%ccur%cmail5 "
|
||||
"%s%cbar%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, !=, NULL);
|
||||
|
||||
len = strlen(output);
|
||||
/* g_print ("\n[%s](%u)\n", output, len); */
|
||||
g_assert_cmpuint (len,>,150);
|
||||
|
||||
g_free (output);
|
||||
g_free (cmdline);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_view_attach (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
int len;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s view --muhome=%s %s%cFoo%ccur%cmail5",
|
||||
MU_PROGRAM,
|
||||
tmpdir,
|
||||
MU_TESTMAILDIR2,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, !=, NULL);
|
||||
|
||||
len = strlen(output);
|
||||
/* g_print ("\n[%s] (%d)\n", output, len);*/
|
||||
g_assert (len == 170 || len == 168);
|
||||
|
||||
g_free (output);
|
||||
g_free (cmdline);
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_mkdir_01 (void)
|
||||
{
|
||||
gchar *cmdline, *output, *tmpdir;
|
||||
gchar *dir;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0);
|
||||
|
||||
cmdline = g_strdup_printf ("%s mkdir --muhome=%s %s%ctest1 %s%ctest2",
|
||||
MU_PROGRAM,tmpdir,
|
||||
tmpdir, G_DIR_SEPARATOR,
|
||||
tmpdir, G_DIR_SEPARATOR);
|
||||
|
||||
output = NULL;
|
||||
g_assert (g_spawn_command_line_sync (cmdline, &output, NULL,
|
||||
NULL, NULL));
|
||||
g_assert_cmpstr (output, ==, "");
|
||||
|
||||
dir = g_strdup_printf ("%s%ctest1%ccur", tmpdir, G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
g_assert (access (dir, F_OK) == 0);
|
||||
g_free (dir);
|
||||
|
||||
dir = g_strdup_printf ("%s%ctest2%ctmp", tmpdir, G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
g_assert (access (dir, F_OK) == 0);
|
||||
g_free (dir);
|
||||
|
||||
dir = g_strdup_printf ("%s%ctest1%cnew", tmpdir, G_DIR_SEPARATOR,
|
||||
G_DIR_SEPARATOR);
|
||||
g_assert (access (dir, F_OK) == 0);
|
||||
g_free (dir);
|
||||
|
||||
g_free (output);
|
||||
g_free (tmpdir);
|
||||
g_free (cmdline);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int rv;
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
if (!set_en_us_utf8_locale())
|
||||
return 0; /* don't error out... */
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-index", test_mu_index);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-empty-query",
|
||||
test_mu_find_empty_query);
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-01", test_mu_find_01);
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-02", test_mu_find_02);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-file", test_mu_find_file);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-mime", test_mu_find_mime);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-links", test_mu_find_links);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-text-in-rfc822",
|
||||
test_mu_find_text_in_rfc822);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-03", test_mu_find_03);
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-04", test_mu_find_04);
|
||||
g_test_add_func ("/mu-cmd/test-mu-find-maildir-special",
|
||||
test_mu_find_maildir_special);
|
||||
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-03", test_mu_extract_03);
|
||||
g_test_add_func ("/mu-cmd/test-mu-extract-overwrite",
|
||||
test_mu_extract_overwrite);
|
||||
g_test_add_func ("/mu-cmd/test-mu-extract-by-name",
|
||||
test_mu_extract_by_name);
|
||||
|
||||
g_test_add_func ("/mu-cmd/test-mu-view-01", test_mu_view_01);
|
||||
g_test_add_func ("/mu-cmd/test-mu-view-multi",
|
||||
test_mu_view_multi);
|
||||
g_test_add_func ("/mu-cmd/test-mu-view-multi-separate",
|
||||
test_mu_view_multi_separate);
|
||||
g_test_add_func ("/mu-cmd/test-mu-view-attach", test_mu_view_attach);
|
||||
g_test_add_func ("/mu-cmd/test-mu-mkdir-01", test_mu_mkdir_01);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING|
|
||||
G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
rv = g_test_run ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
184
mu/tests/test-mu-contacts.c
Normal file
184
mu/tests/test-mu-contacts.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "mu-contacts.h"
|
||||
|
||||
static gchar*
|
||||
fill_database (void)
|
||||
{
|
||||
gchar *cmdline, *tmpdir;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s"
|
||||
" --quiet",
|
||||
MU_PROGRAM, tmpdir, MU_TESTMAILDIR);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
NULL, NULL));
|
||||
g_free (cmdline);
|
||||
|
||||
return tmpdir;
|
||||
}
|
||||
|
||||
|
||||
struct _Contact {
|
||||
char *name, *email;
|
||||
time_t tstamp;
|
||||
};
|
||||
typedef struct _Contact Contact;
|
||||
|
||||
static Contact*
|
||||
contact_new (const char *email, const char *name, size_t tstamp)
|
||||
{
|
||||
Contact *contact;
|
||||
|
||||
contact = g_slice_new (Contact);
|
||||
contact->name = name ? g_strdup (name) :NULL;
|
||||
contact->email = email ? g_strdup (email) : NULL;
|
||||
contact->tstamp = tstamp;
|
||||
|
||||
return contact;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
contact_destroy (Contact *contact)
|
||||
{
|
||||
if (contact) {
|
||||
g_free (contact->name);
|
||||
g_free (contact->email);
|
||||
g_slice_free (Contact, contact);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_contact (const char *email, const char *name, time_t tstamp,
|
||||
GSList **lst)
|
||||
{
|
||||
Contact *contact;
|
||||
|
||||
/* g_print ("[n:%s, e:%s]\n", name, email); */
|
||||
|
||||
contact = contact_new (email, name, tstamp);
|
||||
*lst = g_slist_prepend (*lst, contact);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
has_contact (GSList *lst, const char* name_or_email, gboolean use_name)
|
||||
{
|
||||
while (lst) {
|
||||
Contact *c;
|
||||
c = (Contact*)lst->data;
|
||||
|
||||
/* g_print ("{n:%s,e:%s}\n", c->name, c->email); */
|
||||
|
||||
if (use_name && g_strcmp0(name_or_email, c->name) == 0)
|
||||
return TRUE;
|
||||
if (g_strcmp0 (name_or_email, c->email) == 0)
|
||||
return TRUE;
|
||||
|
||||
lst = g_slist_next (lst);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GSList*
|
||||
accumulate_contacts (MuContacts *contacts, const gchar *pattern)
|
||||
{
|
||||
GSList *lst;
|
||||
|
||||
lst = NULL;
|
||||
g_assert (mu_contacts_foreach (contacts,
|
||||
(MuContactsForeachFunc)each_contact,
|
||||
&lst, pattern, NULL));
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_contacts_01 (void)
|
||||
{
|
||||
MuContacts *contacts;
|
||||
gchar *muhome, *contactsfile;
|
||||
GSList *clist;
|
||||
|
||||
muhome = fill_database ();
|
||||
g_assert (muhome != NULL);
|
||||
contactsfile = g_strdup_printf ("%s%ccache%ccontacts",
|
||||
muhome, G_DIR_SEPARATOR, G_DIR_SEPARATOR);
|
||||
/* g_print ("[%s]\n", contactsfile); */
|
||||
|
||||
contacts = mu_contacts_new (contactsfile);
|
||||
g_assert (contacts);
|
||||
|
||||
clist = accumulate_contacts (contacts, "Mü");
|
||||
g_assert_cmpint (g_slist_length (clist), ==, 1);
|
||||
g_assert (has_contact (clist, "Mü", TRUE));
|
||||
g_assert (has_contact (clist, "testmu@testmu.xx", FALSE));
|
||||
g_slist_foreach (clist, (GFunc)contact_destroy, NULL);
|
||||
g_slist_free (clist);
|
||||
|
||||
clist = accumulate_contacts (contacts, "testmu\\.xxx?");
|
||||
g_assert_cmpint (g_slist_length (clist), ==, 2);
|
||||
g_assert (has_contact (clist, "Mü", TRUE));
|
||||
g_assert (has_contact (clist, "testmu@testmu.xx", FALSE));
|
||||
g_assert (has_contact (clist, "Helmut Kröger", TRUE));
|
||||
g_assert (has_contact (clist, "hk@testmu.xxx", FALSE));
|
||||
|
||||
g_slist_foreach (clist, (GFunc)contact_destroy, NULL);
|
||||
g_slist_free (clist);
|
||||
|
||||
mu_contacts_destroy (contacts);
|
||||
|
||||
g_free (contactsfile);
|
||||
g_free (muhome);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int rv;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/mu-contacts/test-mu-contacts-01", test_mu_contacts_01);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
rv = g_test_run ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
690
mu/tests/test-mu-query.c
Normal file
690
mu/tests/test-mu-query.c
Normal file
@ -0,0 +1,690 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "mu-query.h"
|
||||
#include "mu-str.h"
|
||||
#include "mu-store.h"
|
||||
|
||||
static gchar*
|
||||
fill_database (const char *testdir)
|
||||
{
|
||||
gchar *cmdline, *tmpdir, *xpath;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s"
|
||||
" --quiet",
|
||||
MU_PROGRAM, tmpdir, testdir);
|
||||
|
||||
if (g_test_verbose())
|
||||
g_printerr ("\n%s\n", cmdline);
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
NULL, NULL));
|
||||
g_free (cmdline);
|
||||
|
||||
xpath= g_strdup_printf ("%s%c%s", tmpdir,
|
||||
G_DIR_SEPARATOR, "xapian");
|
||||
g_free (tmpdir);
|
||||
|
||||
return xpath;
|
||||
}
|
||||
|
||||
static void
|
||||
assert_no_dups (MuMsgIter *iter)
|
||||
{
|
||||
GHashTable *hash;
|
||||
|
||||
hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)g_free, NULL);
|
||||
|
||||
mu_msg_iter_reset (iter);
|
||||
while (!mu_msg_iter_is_done(iter)) {
|
||||
MuMsg *msg;
|
||||
msg = mu_msg_iter_get_msg_floating (iter);
|
||||
/* make sure there are no duplicates */
|
||||
g_assert (!g_hash_table_lookup (hash, mu_msg_get_path (msg)));
|
||||
g_hash_table_insert (hash, g_strdup (mu_msg_get_path(msg)),
|
||||
GUINT_TO_POINTER(TRUE));
|
||||
mu_msg_iter_next (iter);
|
||||
}
|
||||
mu_msg_iter_reset (iter);
|
||||
g_hash_table_destroy (hash);
|
||||
}
|
||||
|
||||
|
||||
/* note: this also *moves the iter* */
|
||||
static guint
|
||||
run_and_count_matches (const char *xpath, const char *query)
|
||||
{
|
||||
MuQuery *mquery;
|
||||
MuMsgIter *iter;
|
||||
MuStore *store;
|
||||
guint count1, count2;
|
||||
|
||||
store = mu_store_new_read_only (xpath, NULL);
|
||||
g_assert (store);
|
||||
|
||||
mquery = mu_query_new (store, NULL);
|
||||
g_assert (query);
|
||||
|
||||
mu_store_unref (store);
|
||||
|
||||
if (g_test_verbose()) {
|
||||
char *xs;
|
||||
g_print ("\n==> query: %s\n", query);
|
||||
xs = mu_query_preprocess (query, NULL);
|
||||
g_print ("==> preproc: '%s'\n", xs);
|
||||
g_free (xs);
|
||||
xs = mu_query_as_string (mquery, query, NULL);
|
||||
g_print ("==> xquery: '%s'\n", xs);
|
||||
g_free (xs);
|
||||
}
|
||||
|
||||
|
||||
iter = mu_query_run (mquery, query, FALSE, MU_MSG_FIELD_ID_NONE,
|
||||
FALSE, -1, NULL);
|
||||
mu_query_destroy (mquery);
|
||||
g_assert (iter);
|
||||
|
||||
assert_no_dups (iter);
|
||||
|
||||
/* run query twice, to test mu_msg_iter_reset */
|
||||
for (count1 = 0; !mu_msg_iter_is_done(iter);
|
||||
mu_msg_iter_next(iter), ++count1);
|
||||
|
||||
mu_msg_iter_reset (iter);
|
||||
|
||||
assert_no_dups (iter);
|
||||
|
||||
for (count2 = 0; !mu_msg_iter_is_done(iter);
|
||||
mu_msg_iter_next(iter), ++count2);
|
||||
|
||||
mu_msg_iter_destroy (iter);
|
||||
|
||||
g_assert_cmpuint (count1, ==, count2);
|
||||
|
||||
return count1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *query;
|
||||
size_t count; /* expected number of matches */
|
||||
} QResults;
|
||||
|
||||
static void
|
||||
test_mu_query_01 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "basic", 3 },
|
||||
{ "question", 5 },
|
||||
{ "thanks", 2 },
|
||||
{ "html", 4 },
|
||||
{ "subject:elisp", 1 },
|
||||
{ "html AND contains", 1 },
|
||||
{ "html and contains", 1 },
|
||||
{ "from:pepernoot", 0 },
|
||||
{ "foo:pepernoot", 0 },
|
||||
{ "funky", 1 },
|
||||
{ "fünkÿ", 1 },
|
||||
{ "", 17 }
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_query_02 (void)
|
||||
{
|
||||
const char* q;
|
||||
gchar *xpath;
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath);
|
||||
|
||||
q = "i:f7ccd24b0808061357t453f5962w8b61f9a453b684d0@mail.gmail.com";
|
||||
|
||||
g_assert_cmpuint (run_and_count_matches(xpath, q), ==, 1);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_03 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "ploughed", 1},
|
||||
{ "i:3BE9E6535E3029448670913581E7A1A20D852173@"
|
||||
"emss35m06.us.lmco.com", 1},
|
||||
|
||||
/* subsets of the words in the subject should match */
|
||||
{ "s:gcc include search order" , 1},
|
||||
{ "s:gcc include search" , 1},
|
||||
{ "s:search order" , 1},
|
||||
{ "s:include" , 1},
|
||||
|
||||
{ "s:lisp", 1},
|
||||
{ "s:LISP", 1},
|
||||
|
||||
{ "s:\"Re: Learning LISP; Scheme vs elisp.\"", 1},
|
||||
{ "subject:Re: Learning LISP; Scheme vs elisp.", 0},
|
||||
{ "subject:\"Re: Learning LISP; Scheme vs elisp.\"", 1},
|
||||
{ "to:help-gnu-emacs@gnu.org", 4},
|
||||
{ "t:help-gnu-emacs", 0},
|
||||
{ "flag:flagged", 1}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_04 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "frodo@example.com", 1},
|
||||
{ "f:frodo@example.com", 1},
|
||||
{ "f:Frodo Baggins", 1},
|
||||
{ "bilbo@anotherexample.com", 1},
|
||||
{ "t:bilbo@anotherexample.com", 1},
|
||||
{ "t:bilbo", 1},
|
||||
{ "f:bilbo", 0},
|
||||
{ "baggins", 1},
|
||||
{ "prio:h", 1},
|
||||
{ "prio:high", 1},
|
||||
{ "prio:normal", 9},
|
||||
{ "prio:l", 7},
|
||||
{ "not prio:l", 10},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_logic (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "subject:gcc" , 1},
|
||||
{ "subject:lisp" , 1},
|
||||
{ "subject:gcc OR subject:lisp" , 2},
|
||||
{ "subject:gcc or subject:lisp" , 2},
|
||||
{ "subject:gcc AND subject:lisp" , 0},
|
||||
|
||||
{ "subject:gcc OR (subject:scheme AND subject:elisp)" , 2},
|
||||
{ "(subject:gcc OR subject:scheme) AND subject:elisp" , 1}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_accented_chars_01 (void)
|
||||
{
|
||||
MuQuery *query;
|
||||
MuMsgIter *iter;
|
||||
MuMsg *msg;
|
||||
MuStore *store;
|
||||
gchar *xpath;
|
||||
GError *err;
|
||||
gchar *summ;
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
store = mu_store_new_read_only (xpath, NULL);
|
||||
g_assert (store);
|
||||
|
||||
query = mu_query_new (store, NULL);
|
||||
mu_store_unref (store);
|
||||
|
||||
iter = mu_query_run (query, "fünkÿ", FALSE, MU_MSG_FIELD_ID_NONE,
|
||||
FALSE, -1, NULL);
|
||||
err = NULL;
|
||||
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
|
||||
if (!msg) {
|
||||
g_warning ("error getting message: %s", err->message);
|
||||
g_error_free (err);
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
g_assert_cmpstr (mu_msg_get_subject(msg),==,
|
||||
"Greetings from Lothlórien");
|
||||
/* TODO: fix this again */
|
||||
|
||||
summ = mu_str_summarize (mu_msg_get_body_text(msg), 5);
|
||||
g_assert_cmpstr (summ,==, "Let's write some fünkÿ text using umlauts. Foo.");
|
||||
g_free (summ);
|
||||
|
||||
mu_msg_iter_destroy (iter);
|
||||
mu_query_destroy (query);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_query_accented_chars_02 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "f:mü", 1},
|
||||
{ "s:motörhead", 1},
|
||||
{ "t:Helmut", 1},
|
||||
{ "t:Kröger", 1},
|
||||
{ "s:MotorHeäD", 1},
|
||||
{ "queensryche", 1},
|
||||
{ "Queensrÿche", 1},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
g_free (xpath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_wildcards (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "f:mü", 1},
|
||||
{ "s:mo*", 1},
|
||||
{ "t:Helm*", 1},
|
||||
{ "queensryche", 1},
|
||||
{ "Queen*", 1},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_dates_helsinki (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
const char *old_tz;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "date:20080731..20080804", 5},
|
||||
/* { "date:20080804..20080731", 5}, */
|
||||
{ "date:20080731..20080804", 5},
|
||||
{ "date:20080731..20080804 s:gcc", 1},
|
||||
{ "date:200808110803..now", 5},
|
||||
{ "date:200808110803..today", 5},
|
||||
/* { "date:now..2008-08-11-08-03", 1}, */
|
||||
/* { "date:today..2008-08-11-08-03", 1}, */
|
||||
{ "date:200808110801..now", 5},
|
||||
};
|
||||
|
||||
old_tz = set_tz ("Europe/Helsinki");
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
set_tz (old_tz);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_query_dates_sydney (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
const char *old_tz;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "date:20080731..20080804", 5},
|
||||
/* { "date:20080804..20080731", 5}, */
|
||||
{ "date:20080731..20080804", 5},
|
||||
{ "date:20080731..20080804 s:gcc", 1},
|
||||
{ "date:200808110803..now", 5},
|
||||
{ "date:200808110803..today", 5},
|
||||
/* { "date:now..2008-08-11-08-03", 1}, */
|
||||
/* { "date:today..2008-08-11-08-03", 1}, */
|
||||
{ "date:200808110801..now", 5},
|
||||
};
|
||||
|
||||
old_tz = set_tz ("Australia/Sydney");
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
set_tz (old_tz);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_query_dates_la (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
const char *old_tz;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "date:20080731..20080804", 5},
|
||||
/* { "date:20080804..20080731", 5}, */
|
||||
{ "date:20080731..20080804", 5},
|
||||
{ "date:20080731..20080804 s:gcc", 1},
|
||||
{ "date:200808110803..now", 4},
|
||||
{ "date:200808110803..today", 4},
|
||||
/* { "date:now..2008-08-11-08-03", 1}, */
|
||||
/* { "date:today..2008-08-11-08-03", 1}, */
|
||||
{ "date:200808110801..now", 4}, /* does not match in LA */
|
||||
};
|
||||
|
||||
old_tz = set_tz ("America/Los_Angeles");
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
set_tz (old_tz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_sizes (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "size:0b..2m", 17},
|
||||
{ "size:2k..4k", 4},
|
||||
{ "size:2m..0b", 17}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_attach (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "j:sittingbull.jpg", 1},
|
||||
{ "file:custer", 0},
|
||||
{ "file:custer.jpg", 1}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR2);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
/* g_print ("(%s)\n", xpath); */
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_tags (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "x:paradise", 1},
|
||||
{ "tag:lost", 1},
|
||||
{ "tag:lost tag:paradise", 1},
|
||||
{ "tag:lost tag:horizon", 0},
|
||||
{ "tag:lost OR tag:horizon", 1},
|
||||
{ "x:paradise,lost", 0},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR2);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
/* g_print ("(%s)\n", xpath); */
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_signed_encrypted (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "flag:encrypted", 2},
|
||||
{ "flag:signed", 2},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
/* g_print ("(%s)\n", xpath); */
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i)
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_tags_02 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
int i;
|
||||
|
||||
QResults queries[] = {
|
||||
{ "x:paradise", 1},
|
||||
{ "tag:@NextActions", 1},
|
||||
{ "x:queensrÿche", 1},
|
||||
{ "tag:lost OR tag:operation*", 2},
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR2);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
/* g_print ("(%s)\n", xpath); */
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(queries); ++i) {
|
||||
/* g_print ("%s\n", queries[i].query); */
|
||||
g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query),
|
||||
==, queries[i].count);
|
||||
}
|
||||
|
||||
g_free (xpath);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_query_preprocess (void)
|
||||
{
|
||||
unsigned u;
|
||||
struct {
|
||||
const gchar *expr, *expected;
|
||||
} testcases [] = {
|
||||
{ "hello", "hello" },
|
||||
{ "/[Gmail].Sent Mail", "__gmail__sent mail" }
|
||||
/* add more */
|
||||
};
|
||||
|
||||
|
||||
for (u = 0; u != G_N_ELEMENTS(testcases); ++u) {
|
||||
gchar *prep;
|
||||
prep = mu_query_preprocess (testcases[u].expr, NULL);
|
||||
g_assert_cmpstr (prep, ==, testcases[u].expected);
|
||||
g_free (prep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int rv;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-preprocess",
|
||||
test_mu_query_preprocess);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-01", test_mu_query_01);
|
||||
g_test_add_func ("/mu-query/test-mu-query-02", test_mu_query_02);
|
||||
g_test_add_func ("/mu-query/test-mu-query-03", test_mu_query_03);
|
||||
g_test_add_func ("/mu-query/test-mu-query-04", test_mu_query_04);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-signed-encrypted",
|
||||
test_mu_query_signed_encrypted);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-logic", test_mu_query_logic);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-accented-chars-1",
|
||||
test_mu_query_accented_chars_01);
|
||||
g_test_add_func ("/mu-query/test-mu-query-accented-chars-2",
|
||||
test_mu_query_accented_chars_02);
|
||||
g_test_add_func ("/mu-query/test-mu-query-wildcards",
|
||||
test_mu_query_wildcards);
|
||||
g_test_add_func ("/mu-query/test-mu-query-sizes",
|
||||
test_mu_query_sizes);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-dates-helsinki",
|
||||
test_mu_query_dates_helsinki); /* finland */
|
||||
g_test_add_func ("/mu-query/test-mu-query-dates-sydney",
|
||||
test_mu_query_dates_sydney);
|
||||
g_test_add_func ("/mu-query/test-mu-query-dates-la",
|
||||
test_mu_query_dates_la);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-query-attach",
|
||||
test_mu_query_attach);
|
||||
g_test_add_func ("/mu-query/test-mu-query-tags",
|
||||
test_mu_query_tags);
|
||||
g_test_add_func ("/mu-query/test-mu-query-tags_02",
|
||||
test_mu_query_tags_02);
|
||||
|
||||
if (!g_test_verbose())
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
|
||||
rv = g_test_run ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
98
mu/tests/test-mu-runtime.c
Normal file
98
mu/tests/test-mu-runtime.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
** Copyright (C) 2008-2010 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "src/mu-runtime.h"
|
||||
|
||||
static void
|
||||
test_mu_runtime_init (void)
|
||||
{
|
||||
gchar* tmpdir;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (tmpdir);
|
||||
g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE);
|
||||
mu_runtime_uninit ();
|
||||
|
||||
g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE);
|
||||
mu_runtime_uninit ();
|
||||
|
||||
g_free (tmpdir);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_runtime_data (void)
|
||||
{
|
||||
gchar *homedir, *xdir, *bmfile;
|
||||
|
||||
homedir = test_mu_common_get_random_tmpdir();
|
||||
g_assert (homedir);
|
||||
|
||||
xdir = g_strdup_printf ("%s%c%s", homedir,
|
||||
G_DIR_SEPARATOR, "xapian" );
|
||||
|
||||
bmfile = g_strdup_printf ("%s%c%s", homedir,
|
||||
G_DIR_SEPARATOR, "bookmarks");
|
||||
|
||||
g_assert (mu_runtime_init (homedir, "test-mu-runtime") == TRUE);
|
||||
|
||||
g_assert_cmpstr (homedir, ==, mu_runtime_path (MU_RUNTIME_PATH_MUHOME));
|
||||
g_assert_cmpstr (xdir, ==, mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB));
|
||||
g_assert_cmpstr (bmfile, ==, mu_runtime_path (MU_RUNTIME_PATH_BOOKMARKS));
|
||||
|
||||
mu_runtime_uninit ();
|
||||
|
||||
g_free (homedir);
|
||||
g_free (xdir);
|
||||
g_free (bmfile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
/* mu_runtime_init/uninit */
|
||||
g_test_add_func ("/mu-runtime/mu-runtime-init",
|
||||
test_mu_runtime_init);
|
||||
g_test_add_func ("/mu-runtime/mu-runtime-data",
|
||||
test_mu_runtime_data);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
|
||||
246
mu/tests/test-mu-threads.c
Normal file
246
mu/tests/test-mu-threads.c
Normal file
@ -0,0 +1,246 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test-mu-common.h"
|
||||
#include "mu-query.h"
|
||||
#include "mu-str.h"
|
||||
|
||||
static gchar*
|
||||
fill_database (const char *testdir)
|
||||
{
|
||||
gchar *cmdline, *tmpdir, *xpath;
|
||||
|
||||
tmpdir = test_mu_common_get_random_tmpdir();
|
||||
cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s"
|
||||
" --quiet",
|
||||
MU_PROGRAM, tmpdir, testdir);
|
||||
/* g_print ("%s\n", cmdline); */
|
||||
|
||||
g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL,
|
||||
NULL, NULL));
|
||||
g_free (cmdline);
|
||||
xpath= g_strdup_printf ("%s%c%s", tmpdir,
|
||||
G_DIR_SEPARATOR, "xapian");
|
||||
g_free (tmpdir);
|
||||
|
||||
return xpath;
|
||||
}
|
||||
|
||||
/* note: this also *moves the iter* */
|
||||
static MuMsgIter*
|
||||
run_and_get_iter (const char *xpath, const char *query)
|
||||
{
|
||||
MuQuery *mquery;
|
||||
MuStore *store;
|
||||
MuMsgIter *iter;
|
||||
|
||||
store = mu_store_new_read_only (xpath, NULL);
|
||||
g_assert (store);
|
||||
|
||||
mquery = mu_query_new (store, NULL);
|
||||
mu_store_unref (store);
|
||||
g_assert (query);
|
||||
|
||||
iter = mu_query_run (mquery, query, TRUE, MU_MSG_FIELD_ID_DATE,
|
||||
FALSE, -1, NULL);
|
||||
mu_query_destroy (mquery);
|
||||
g_assert (iter);
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_mu_threads_01 (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
MuMsgIter *iter;
|
||||
unsigned u;
|
||||
|
||||
struct {
|
||||
const char* threadpath;
|
||||
const char *msgid;
|
||||
const char* subject;
|
||||
} items [] = {
|
||||
{"0", "root0@msg.id", "root0"},
|
||||
{"0:0", "child0.0@msg.id", "Re: child 0.0"},
|
||||
{"0:1", "child0.1@msg.id", "Re: child 0.1"},
|
||||
{"0:1:0", "child0.1.0@msg.id", "Re: child 0.1.0"},
|
||||
{"1", "root1@msg.id", "root1"},
|
||||
{"2", "root2@msg.id", "root2"},
|
||||
/* next one's been promoted 2.0.0 => 2.0 */
|
||||
{"2:0", "child2.0.0@msg.id", "Re: child 2.0.0"},
|
||||
/* next one's been promoted 3.0.0.0.0 => 3 */
|
||||
{"3", "child3.0.0.0.0@msg.id", "Re: child 3.0.0.0"},
|
||||
|
||||
/* two children of absent root 4.0 */
|
||||
{"4:0", "child4.0@msg.id", "Re: child 4.0"},
|
||||
{"4:1", "child4.1@msg.id", "Re: child 4.1"}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR3);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
iter = run_and_get_iter (xpath, "abc");
|
||||
g_assert (iter);
|
||||
g_assert (!mu_msg_iter_is_done(iter));
|
||||
|
||||
u = 0;
|
||||
while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items)) {
|
||||
MuMsg *msg;
|
||||
const MuMsgIterThreadInfo *ti;
|
||||
|
||||
ti = mu_msg_iter_get_thread_info (iter);
|
||||
if (!ti)
|
||||
g_print ("%s: thread info not found for %u\n",
|
||||
__FUNCTION__, (unsigned)mu_msg_iter_get_docid(iter));
|
||||
g_assert(ti);
|
||||
|
||||
msg = mu_msg_iter_get_msg_floating (iter);
|
||||
g_assert (msg);
|
||||
/* g_print ("%s %s %s\n", ti->threadpath, */
|
||||
/* mu_msg_get_msgid(msg), */
|
||||
/* mu_msg_get_path (msg) */
|
||||
/* ); */
|
||||
|
||||
g_assert (u < G_N_ELEMENTS(items));
|
||||
|
||||
g_assert_cmpstr (ti->threadpath,==,items[u].threadpath);
|
||||
g_assert_cmpstr (mu_msg_get_subject(msg),==,items[u].subject);
|
||||
g_assert_cmpstr (mu_msg_get_msgid(msg),==,items[u].msgid);
|
||||
|
||||
++u;
|
||||
mu_msg_iter_next (iter);
|
||||
}
|
||||
g_assert (u == G_N_ELEMENTS(items));
|
||||
|
||||
g_free (xpath);
|
||||
mu_msg_iter_destroy (iter);
|
||||
}
|
||||
|
||||
|
||||
struct _tinfo {
|
||||
const char* threadpath;
|
||||
const char *msgid;
|
||||
const char* subject;
|
||||
};
|
||||
typedef struct _tinfo tinfo;
|
||||
|
||||
static void
|
||||
test_mu_threads_rogue (void)
|
||||
{
|
||||
gchar *xpath;
|
||||
MuMsgIter *iter;
|
||||
unsigned u;
|
||||
tinfo *items;
|
||||
|
||||
tinfo items1 [] = {
|
||||
{"0", "cycle0@msg.id", "cycle0"},
|
||||
{"0:0", "cycle0.0@msg.id", "cycle0.0"},
|
||||
{"0:0:0", "cycle0.0.0@msg.id", "cycle0.0.0"},
|
||||
{"0:1", "rogue0@msg.id", "rogue0"},
|
||||
};
|
||||
|
||||
tinfo items2 [] = {
|
||||
{"0", "cycle0.0@msg.id", "cycle0.0"},
|
||||
{"0:0", "cycle0@msg.id", "cycle0"},
|
||||
{"0:0:0", "rogue0@msg.id", "rogue0" },
|
||||
{"0:1", "cycle0.0.0@msg.id", "cycle0.0.0"}
|
||||
};
|
||||
|
||||
xpath = fill_database (MU_TESTMAILDIR3);
|
||||
g_assert (xpath != NULL);
|
||||
|
||||
iter = run_and_get_iter (xpath, "def");
|
||||
g_assert (iter);
|
||||
g_assert (!mu_msg_iter_is_done(iter));
|
||||
|
||||
/* due to the random order in files can be indexed, there are two possible ways
|
||||
* for the threads to be built-up; both are okay */
|
||||
if (g_strcmp0 (mu_msg_get_msgid(mu_msg_iter_get_msg_floating (iter)),
|
||||
"cycle0@msg.id") == 0)
|
||||
items = items1;
|
||||
else
|
||||
items = items2;
|
||||
|
||||
u = 0;
|
||||
while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items1)) {
|
||||
MuMsg *msg;
|
||||
const MuMsgIterThreadInfo *ti;
|
||||
|
||||
ti = mu_msg_iter_get_thread_info (iter);
|
||||
if (!ti)
|
||||
g_print ("%s: thread info not found\n",
|
||||
mu_msg_get_msgid(mu_msg_iter_get_msg_floating (iter)));
|
||||
g_assert(ti);
|
||||
|
||||
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
|
||||
/* g_print ("%s %s %s\n", ti->threadpath, */
|
||||
/* mu_msg_get_msgid(msg), */
|
||||
/* mu_msg_get_path (msg) */
|
||||
/* ); */
|
||||
|
||||
g_assert (u < G_N_ELEMENTS(items1));
|
||||
|
||||
g_assert_cmpstr (ti->threadpath,==,(items)[u].threadpath);
|
||||
g_assert_cmpstr (mu_msg_get_subject(msg),==,(items)[u].subject);
|
||||
g_assert_cmpstr (mu_msg_get_msgid(msg),==,(items)[u].msgid);
|
||||
|
||||
++u;
|
||||
mu_msg_iter_next (iter);
|
||||
}
|
||||
g_assert (u == G_N_ELEMENTS(items1));
|
||||
|
||||
g_free (xpath);
|
||||
mu_msg_iter_destroy (iter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int rv;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/mu-query/test-mu-threads-01", test_mu_threads_01);
|
||||
g_test_add_func ("/mu-query/test-mu-threads-rogue", test_mu_threads_rogue);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
||||
(GLogFunc)black_hole, NULL);
|
||||
|
||||
rv = g_test_run ();
|
||||
|
||||
return rv;
|
||||
}
|
||||
Reference in New Issue
Block a user