/* stdcapa.c -- Standard CLI capabilities for GNU Mailutils
Copyright (C) 2016-2021 Free Software Foundation, Inc.
GNU Mailutils 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.
GNU Mailutils 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 GNU Mailutils. If not, see .
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* *************************************************************************
* Logging section
* ************************************************************************* */
static void
cli_log_facility (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
if (mu_string_to_syslog_facility (arg, &mu_log_facility))
mu_parseopt_error (po, _("unknown syslog facility `%s'"), arg);
}
static int
cb_facility (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_string_to_syslog_facility (val->v.string, &mu_log_facility))
{
mu_error (_("unknown syslog facility `%s'"), val->v.string);
return 1;
}
return 0;
}
static int
cb_severity (void *data, mu_config_value_t *val)
{
unsigned n;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_severity_from_string (val->v.string, &n))
{
mu_error (_("unknown severity `%s'"), val->v.string);
return 1;
}
mu_log_severity_threshold = n;
return 0;
}
static struct mu_cfg_param logging_cfg[] = {
{ "syslog", mu_c_bool, &mu_log_syslog, 0, NULL,
N_("Send diagnostics to syslog.") },
{ "print-severity", mu_c_bool, &mu_log_print_severity, 0, NULL,
N_("Print message severity levels.") },
{ "severity", mu_cfg_callback, NULL, 0, cb_severity,
N_("Output only messages with a severity equal to or greater than "
"this one."),
/* TRANSLATORS: Translate only arg:, rest are keywords */
N_("arg: debug|info|notice|warning|error|crit|alert|emerg")},
{ "facility", mu_cfg_callback, NULL, 0, cb_facility,
N_("Set syslog facility. Arg is one of the following: user, daemon, "
"auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
"or a facility number."),
/* TRANSLATORS: Translate only arg: and , rest are keywords */
N_("arg: auth|authpriv|mail|local0-local7|") },
{ "session-id", mu_c_bool, &mu_log_session_id, 0, NULL,
N_("Log session ID") },
{ "tag", mu_c_string, &mu_log_tag, 0, NULL,
N_("Tag syslog messages with this string.") },
{ NULL }
};
static struct mu_option logging_option[] = {
{ "log-facility", 0, N_("FACILITY"), MU_OPTION_DEFAULT,
N_("output logs to syslog FACILITY"),
mu_c_int, &mu_log_facility, cli_log_facility },
MU_OPTION_END
};
static void
logging_commit (void *unused)
{
if (mu_log_syslog >= 0)
mu_stdstream_strerr_setup (mu_log_syslog ?
MU_STRERR_SYSLOG : MU_STRERR_STDERR);
}
/* *************************************************************************
* Mailer
* ************************************************************************* */
static void
cli_mailer (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
int rc = mu_mailer_set_url_default (arg);
if (rc != 0)
mu_parseopt_error (po, _("invalid mailer URL `%s': %s"),
arg, mu_strerror (rc));
}
static struct mu_option mailer_option[] = {
{ "mailer", 'M', N_("MAILER"), MU_OPTION_DEFAULT,
N_("use specified URL as the default mailer"),
mu_c_string, NULL, cli_mailer },
MU_OPTION_END
};
static int
cb_mailer (void *data, mu_config_value_t *val)
{
int rc;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_mailer_set_url_default (val->v.string);
if (rc != 0)
mu_error (_("%s: invalid mailer URL: %s"),
val->v.string, mu_strerror (rc));
return rc;
}
static struct mu_cfg_param mailer_cfg[] = {
{ "url", mu_cfg_callback, NULL, 0, cb_mailer,
N_("Use this URL as the default mailer"),
N_("url: string") },
{ NULL }
};
/* *************************************************************************
* Debugging
* ************************************************************************* */
static void
cli_debug_level (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
mu_debug_clear_all ();
mu_debug_parse_spec (arg);
/* FIXME: Error handling */
}
static struct mu_option debug_option[] = {
MU_OPTION_GROUP (N_("Global debugging settings")),
{ "debug-level", 0, N_("LEVEL"), MU_OPTION_DEFAULT,
N_("set Mailutils debugging level"),
mu_c_string, NULL, cli_debug_level },
{ "debug-line-info", 0, NULL, MU_OPTION_DEFAULT,
N_("show source info with debugging messages"),
mu_c_bool, &mu_debug_line_info },
MU_OPTION_END
};
static int
cb_debug_level (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
mu_debug_parse_spec (val->v.string);
return 0;
}
static struct mu_cfg_param debug_cfg[] = {
{ "level", mu_cfg_callback, NULL, 0, &cb_debug_level,
N_("Set Mailutils debugging level. Argument is a semicolon-separated list "
"of debugging specifications. A simplified specification syntax is:\n"
" [!][.,...]\n"
"For details, please see the section 3.3 \"Debugging\" of the GNU Mailutils\n"
"manual, or visit ."),
N_("arg: string") },
{ "line-info", mu_c_bool, &mu_debug_line_info, 0, NULL,
N_("Prefix debug messages with Mailutils source locations.") },
{ NULL }
};
/* ************************************************************************* *
* Mailbox *
* ************************************************************************* */
static int
cb_mail_spool (void *data, mu_config_value_t *val)
{
int rc;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_set_mail_directory (val->v.string);
if (rc)
mu_error (_("cannot set mail directory name to `%s': %s"),
val->v.string, mu_strerror (rc));
return rc;
}
static int
cb_mailbox_pattern (void *data, mu_config_value_t *val)
{
int rc;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_set_mailbox_pattern (val->v.string);
if (rc)
mu_error (_("cannot set mailbox pattern to `%s': %s"),
val->v.string, mu_strerror (rc));
return rc;
}
static int
cb_mailbox_type (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_registrar_set_default_scheme (val->v.string))
mu_error (_("invalid mailbox type: %s"), val->v.string);
return 0;
}
static int
cb_folder (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
mu_set_folder_directory (val->v.string);
return 0;
}
static int
cb_autodetect_accuracy (void *data, mu_config_value_t *val)
{
int v;
char *errmsg;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (strcmp (val->v.string, "auto") == 0)
v = MU_AUTODETECT_ACCURACY_AUTO;
else if (strcmp (val->v.string, "fast") == 0)
v = MU_AUTODETECT_ACCURACY_FAST;
else if (strcmp (val->v.string, "minimal") == 0
|| strcmp (val->v.string, "default") == 0)
v = MU_AUTODETECT_ACCURACY_DEFAULT;
else
{
int rc = mu_str_to_c (val->v.string, mu_c_int, &v, &errmsg);
if (rc)
{
mu_error (_("conversion failed: %s"),
errmsg ? errmsg : mu_strerror (rc));
free (errmsg);
}
else
mu_set_autodetect_accuracy (v);
}
return 0;
}
static struct mu_cfg_param mailbox_cfg[] = {
{ "mail-spool", mu_cfg_callback, NULL, 0, cb_mail_spool,
N_("Use specified URL as a mailspool directory."),
N_("url: string") },
{ "mailbox-pattern", mu_cfg_callback, NULL, 0, cb_mailbox_pattern,
N_("Create mailbox URL using ."),
N_("pattern: string") },
{ "mailbox-type", mu_cfg_callback, NULL, 0, cb_mailbox_type,
N_("Default mailbox type."),
N_("protocol: string") },
{ "folder", mu_cfg_callback, NULL, 0, cb_folder,
N_("Default user mail folder"),
N_("dir: string") },
{ "autodetect-accuracy", mu_cfg_callback, NULL, 0, cb_autodetect_accuracy,
N_("Accuracy level of mailbox format autodetection. Argument is either a"
" decimal number or any of the following constants:\n"
" auto - set accuracy level from the environment variable\n"
" MU_AUTODETECT_ACCURACY (default)\n"
" fast - do only a rough estimation of the mailbox format: fastest,\n"
" but possibly inaccurate\n"
" minimal - good balance between speed and accuracy"),
N_("n: number") },
{ NULL }
};
/* ************************************************************************* *
* Locking *
* ************************************************************************* */
static int
cb_locker_flags (void *data, mu_config_value_t *val)
{
char const *s;
static struct mu_kwd flag_tab[] = {
{ "type external", 'E' },
{ "retry-count", 'R' },
{ "expire-timeout", 'T' },
{ "pid-check", 'P' },
{ NULL }
};
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
for (s = val->v.string; *s; s++)
{
char const *kw;
if (mu_kwd_xlat_tok (flag_tab, *s, &kw))
{
mu_error (_("invalid lock flag `%c'"), *s);
}
else if (*s == 'P')
{
/* TRANSLATORS: %c is replaced with the flag letter, and %s - with
the corresponding keyword. */
mu_diag_output (MU_DIAG_WARNING,
_("applying legacy flag %c, use %s instead"),
*s, kw);
mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
}
else
{
/* TRANSLATORS: %c is replaced with the flag letter, and %s - with
the corresponding keyword. */
mu_diag_output (MU_DIAG_WARNING,
_("ignoring legacy flag %c, use %s instead"),
*s, kw);
}
}
return 0;
}
static int
cb_locker_retry_sleep (void *data, mu_config_value_t *val)
{
int rc;
time_t t;
char *errmsg;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_str_to_c (val->v.string, mu_c_time, &t, &errmsg);
if (rc)
{
mu_error (_("conversion failed: %s"), errmsg ? errmsg :
mu_strerror (rc));
free (errmsg);
}
else
{
mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
mu_locker_defaults.retry_sleep = t;
}
return 0;
}
static int
cb_locker_retry_timeout (void *data, mu_config_value_t *val)
{
mu_diag_output (MU_DIAG_WARNING,
_("%s is deprecated, please use %s instead"),
"retry-timeout", "retry-sleep");
return cb_locker_retry_sleep (data, val);
}
static int
cb_locker_retry_count (void *data, mu_config_value_t *val)
{
int rc;
size_t n;
char *errmsg;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_str_to_c (val->v.string, mu_c_size, &n, &errmsg);
if (rc)
{
mu_error (_("conversion failed: %s"), errmsg ? errmsg :
mu_strerror (rc));
free (errmsg);
}
else if (n == 0)
mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_RETRY;
else
{
mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
mu_locker_defaults.retry_count = n;
}
return 0;
}
static int
cb_locker_expire_timeout (void *data, mu_config_value_t *val)
{
int rc;
time_t t;
char *errmsg;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_str_to_c (val->v.string, mu_c_time, &t, &errmsg);
if (rc)
{
mu_error (_("conversion failed: %s"), errmsg ? errmsg :
mu_strerror (rc));
free (errmsg);
}
else if (t == 0)
mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_EXPIRE_TIME;
else
{
mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
mu_locker_defaults.expire_time = t;
}
return 0;
}
static int
cb_locker_type (void *data, mu_config_value_t *val)
{
int t;
static struct mu_kwd ltab[] = {
{ "dotlock", MU_LOCKER_TYPE_DOTLOCK },
{ "default", MU_LOCKER_TYPE_DEFAULT },
{ "external", MU_LOCKER_TYPE_EXTERNAL },
{ "kernel", MU_LOCKER_TYPE_KERNEL },
{ "null", MU_LOCKER_TYPE_NULL },
{ NULL }
};
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_kwd_xlat_name (ltab, val->v.string, &t))
{
mu_error (_("unrecognized locker type: %s"), val->v.string);
return 1;
}
free (mu_locker_defaults.ext_locker);
mu_locker_defaults.ext_locker = NULL;
mu_locker_defaults.type = t;
mu_locker_defaults.flags |= MU_LOCKER_FLAG_TYPE;
return 0;
}
static int
cb_locker_external (void *data, mu_config_value_t *val)
{
int t;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
free (mu_locker_defaults.ext_locker);
mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXT_LOCKER;
mu_locker_defaults.ext_locker = strdup (val->v.string);
return 0;
}
static int
cb_locker_pid_check (void *data, mu_config_value_t *val)
{
int t;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_str_to_c (val->v.string, mu_c_bool, &t, NULL))
{
mu_error ("%s", _("not a boolean"));
return 1;
}
if (t)
mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
else
mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_CHECK_PID;
return 0;
}
static struct mu_cfg_param locking_cfg[] = {
{ "type", mu_cfg_callback, NULL, 0, cb_locker_type,
N_("Set locker type."),
N_("type: default | dotlock | external | kernel | null") },
{ "retry-count", mu_cfg_callback, NULL, 0, cb_locker_retry_count,
N_("Set the maximum number of times to retry acquiring the lock."),
N_("arg: integer") },
{ "retry-sleep", mu_cfg_callback, NULL, 0, cb_locker_retry_sleep,
N_("Set the delay between two successive locking attempts."),
N_("arg: interval")},
{ "retry-timeout", mu_cfg_callback, NULL, 0, cb_locker_retry_timeout,
N_("Deprecated alias of retry-sleep. Retained for backward compatibility."),
N_("arg: interval")},
{ "expire-timeout", mu_cfg_callback, NULL, 0, cb_locker_expire_timeout,
N_("Expire locks older than this amount of time."),
N_("arg: interval")},
{ "external-locker", mu_cfg_callback, NULL, 0, cb_locker_external,
N_("Use external locker program."),
N_("prog: string") },
{ "pid-check", mu_cfg_callback, NULL, 0, cb_locker_pid_check,
N_("Check if PID of the lock owner is active."),
N_("arg: bool") },
{ "flags", mu_cfg_callback, NULL, 0, cb_locker_flags,
N_("Deprecated. Retained for backward compatibility."),
N_("arg: string") },
{ NULL, }
};
/* ************************************************************************* *
* Address *
* ************************************************************************* */
static int
cb_email_addr (void *data, mu_config_value_t *val)
{
int rc;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_set_user_email (val->v.string);
if (rc)
mu_error (_("invalid email address `%s': %s"),
val->v.string, mu_strerror (rc));
return 0;
}
static int
cb_email_domain (void *data, mu_config_value_t *val)
{
int rc;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
rc = mu_set_user_email_domain (val->v.string);
if (rc)
mu_error (_("invalid email domain `%s': %s"),
val->v.string, mu_strerror (rc));
return 0;
}
static struct mu_cfg_param address_cfg[] = {
{ "email-addr", mu_cfg_callback, NULL, 0, cb_email_addr,
N_("Set the current user email address (default is "
"loginname@defaultdomain)."),
N_("email: address") },
{ "email-domain", mu_cfg_callback, NULL, 0, cb_email_domain,
N_("Set e-mail domain for unqualified user names (default is this host)"),
N_("domain: string") },
{ NULL }
};
/* ************************************************************************* *
* Authentication & Authorization *
* ************************************************************************* */
static int
cb_authentication (void *data, mu_config_value_t *val)
{
if (val->type == MU_CFG_STRING)
{
if (strcmp (val->v.string, "clear") == 0)
mu_authentication_clear_list ();
else
/*FIXME: use err for error reporting*/
mu_authentication_add_module_list (val->v.string);
}
else if (val->type == MU_CFG_LIST)
{
int i;
for (i = 0; i < val->v.arg.c; i++)
{
if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING))
return 1;
if (strcmp (val->v.arg.v[i].v.string, "clear") == 0)
mu_authentication_clear_list ();
else
mu_authentication_add_module (val->v.arg.v[i].v.string);
}
}
else
{
mu_error (_("expected string value"));
return 1;
}
return 0;
}
static int
cb_authorization (void *data, mu_config_value_t *val)
{
if (val->type == MU_CFG_STRING)
{
if (strcmp (val->v.string, "clear") == 0)
mu_authorization_clear_list ();
else
/*FIXME: use err for error reporting*/
mu_authorization_add_module_list (val->v.string);
}
else if (val->type == MU_CFG_LIST)
{
int i;
for (i = 0; i < val->v.arg.c; i++)
{
if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING))
return 1;
if (strcmp (val->v.arg.v[i].v.string, "clear") == 0)
mu_authorization_clear_list ();
else
mu_authorization_add_module (val->v.arg.v[i].v.string);
}
}
else
{
mu_error (_("expected string value"));
return 1;
}
return 0;
}
static struct mu_cfg_param mu_auth_param[] = {
{ "authentication", mu_cfg_callback, NULL, 0, cb_authentication,
/* FIXME: The description is incomplete. MU-list is also allowed as
argument */
N_("Set a list of modules for authentication. Modlist is a "
"colon-separated list of module names or a word `clear' to "
"clear the previously set up values."),
N_("modlist") },
{ "authorization", mu_cfg_callback, NULL, 0, cb_authorization,
N_("Set a list of modules for authorization. Modlist is a "
"colon-separated list of module names or a word `clear' to "
"clear the previously set up values."),
N_("modlist") },
{ NULL }
};
int
mu_auth_section_parser
(enum mu_cfg_section_stage stage, const mu_cfg_node_t *node,
const char *section_label, void **section_data, void *call_data,
mu_cfg_tree_t *tree)
{
switch (stage)
{
case mu_cfg_section_start:
break;
case mu_cfg_section_end:
mu_auth_finish_setup ();
}
return 0;
}
/* ************************************************************************* *
* Registry of standard mailutils' capabilities *
* ************************************************************************* */
struct mu_cli_capa mu_cli_std_capa[] = {
{ "mailutils" }, /* Dummy */
{ "logging", logging_option, logging_cfg, NULL, logging_commit },
{ "mailer", mailer_option, mailer_cfg, NULL, NULL },
{ "debug", debug_option, debug_cfg, NULL, NULL },
{ "mailbox", NULL, mailbox_cfg, NULL, NULL },
{ "locking", NULL, locking_cfg, NULL, NULL },
{ "address", NULL, address_cfg, NULL, NULL },
{ "auth", NULL, mu_auth_param, mu_auth_section_parser },
{ NULL }
};
void
mu_cli_capa_init (void)
{
size_t i;
for (i = 0; mu_cli_std_capa[i].name; i++)
mu_cli_capa_register (&mu_cli_std_capa[i]);
}