summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-08-20 23:39:45 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-08-20 23:39:45 +0300
commitb9aaf724c6428fb4e0d01950d4d7113671e048e5 (patch)
tree50ac09e516ee6723e8352a819a44e4408d113a97
parent811b38863c06207ab3012c586176a113c1f8def1 (diff)
downloadmailutils-b9aaf724c6428fb4e0d01950d4d7113671e048e5.tar.gz
mailutils-b9aaf724c6428fb4e0d01950d4d7113671e048e5.tar.bz2
Movemail: allow to copy mailbox ownership when run as root.
* movemail/movemail.c: Implement new configuration keyword "mailbox-ownership" (and the --owner command line option). * doc/texinfo/programs.texi: Document new movemail features. (Ownership): New subsection stub. * NEWS: Update
-rw-r--r--NEWS6
-rw-r--r--doc/texinfo/programs.texi119
-rw-r--r--movemail/movemail.c369
3 files changed, 360 insertions, 134 deletions
diff --git a/NEWS b/NEWS
index ed4a90e40..43b4403b9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU mailutils NEWS -- history of user-visible changes. 2009-08-14
+GNU mailutils NEWS -- history of user-visible changes. 2009-08-20
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
2008, 2009 Free Software Foundation, Inc.
See the end of file for copying conditions.
@@ -27,6 +27,10 @@ exists in the destination mailbox.
The `--verbose' command line option enables outputting additional
information.
+The `--owner' command line option (and the corresponding
+`mailbox-ownership' configuration file statement) copy mailbox
+ownership, if the utility is run with root privileges.
+
* Mail
** The -f option
diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi
index 4135911a8..5cf7ef23d 100644
--- a/doc/texinfo/programs.texi
+++ b/doc/texinfo/programs.texi
@@ -4371,13 +4371,15 @@ description of @code{Rmail} interface.
Mailutils version of @command{movemail} is completely
backward-compatible with its Emacs predecessor, so it should run
-flawlessly with older versions of Emacs. Emacs version 21.4, which is
-being developed at the time of this writing, will contain improved
-@code{Rmail} interface for work with mailutils @command{movemail}.
+flawlessly with older versions of Emacs. Emacs versions
+starting from 22.1 contain improved @code{Rmail} interface and
+are able to take advantage of all new features mailutils
+@command{movemail} provides.
@menu
* Movemail Configuration::
* Movemail Options:: Description of the Available Options
+* Ownership:: Setting Destination Mailbox Ownership
* Summary:: Short Movemail Invocation Summary
@end menu
@@ -4408,6 +4410,31 @@ exists in the destination mailbox.
Set verbosity level.
@end deffn
+@deffn {Movemail Config} mailbox-ownership @var{method-list}
+Define list of methods for setting ownership of the destination
+mailbox. The @var{method-list} argument can contain the following
+elements:
+
+@anchor{mailbox-ownership-methods}
+@table @asis
+@item copy-id
+Copy owner UID and GID from the source mailbox. This method works only
+with local mailboxes, i.e.: @samp{mbox} (UNIX mailbox), @samp{maildir}
+and @samp{mh}.
+
+@item copy-name
+Get owner name from the source mailbox URL and obtain UID and GID for
+this user using mailutils authorization methods.
+
+@item set-id=@var{uid}[:@var{gid}]
+Set supplied @var{uid} and @var{gid}. If @var{gid} is not supplied,
+it is read from the @file{/etc/passwd} record for this UID.
+
+@item set-name=@var{user}
+Make destination mailbox owned by @var{user}.
+@end table
+@end deffn
+
@multitable @columnfractions 0.3 0.6
@headitem Statement @tab Reference
@item debug @tab @xref{Debug Statement}.
@@ -4439,58 +4466,13 @@ If the remote server supports @acronym{TLS} encryption, use
@option{--tls} to instruct @command{movemail} to initiate encrypted
connection.
-Quite a few options control how @command{movemail} handles mail
-locking (a way of preventing simultaneous access to the source
-mailbox). By default, before accessing mailbox @var{file},
-@command{movemail} will first see if the file named
-@file{@var{file}.lock} (so called @dfn{lock file}) exists. If so, it
-will assume that the mailbox is being used by another program and will
-sleep one second. If @file{@var{file}.lock} file disappears after this
-wait period, the program will proceed. Otherwise, it will repeat this
-action ten times. If after ten wait periods the lock file does not
-disappear, @command{movemail} gives up and exits.
-
-If the lock file does not exist, @command{movemail} will create it,
-thereby indicating to other programs that the mailbox is being used,
-and will proceed to copying messages to the destination file. When
-finished, @command{movemail} closes the mailbox and removes the lock
-file.
-
-Several options control this behavior. To change the default sleep period
-use @option{--lock-retry-timeout}. Its argument is the timeout value
-in seconds.
-
-To change number of retries, use @option{--lock-retry-count}. For
-example, setting @code{rmail-movemail-flags} to
-
-@smallexample
---lock-retry-timeout=2 --lock-retry-count=5
-@end smallexample
-
-@noindent
-instructs @command{movemail} to make five attempts to acquire the lock
-file, with two-second intervals between the attempts.
-
-You may also force @command{movemail} to remove the lock file if it is
-older than a given amount of time (a so called @dfn{stale lock
-file}). To do so, use the following option:
-
-@smallexample
---lock-expire-timeout=@var{seconds}
-@end smallexample
-
-The @option{--lock-expire-timeout} sets the number of seconds after
-which a lock file is considered stale.
+@node Ownership
+@subsection Setting Destination Mailbox Ownership
+@UNREVISED
-There are special programs that can be used to lock and unlock
-mailboxes. A common example of such programs is @command{dotlock}. If
-you wish to use such @dfn{external locking program} instead of the
-default mailutils locking mechanism, use option
-@option{--external-locker}. Argument to this option specifies the full
-name of the external program to use.
@node Summary
-@subsection Summary of Movemail Usage
+@subsection Movemail Usage Summary
@smallexample
movemail [@var{option}...] @var{inbox} @var{destfile} [@var{remote-password}]
@@ -4500,7 +4482,7 @@ The first argument, @var{inbox}, is the @acronym{url} (@pxref{URL}) of
the source mailbox. The second argument, @var{destfile}, traditionally
means destination file, i.e. the UNIX mailbox to copy messages
to. However, mailutils @command{movemail} extends the meaning of this
-parameter. You may actually specify any valid @acronym{url} as
+parameter. You may actually specify any valid @acronym{URL} as
@var{destfile} parameter.@footnote{Rmail does not use this
feature}. Finally, optional third argument is a traditional way of
specifying user passwords for remote (@acronym{POP} or @acronym{IMAP})
@@ -4521,30 +4503,6 @@ Preserve the source mailbox
@itemx --reverse
Reverse the sorting order
-@item --external-locker=@var{program}
-Use given @var{program} as the external locker program.
-
-@item --lock-expire-timeout=@var{seconds}
-Set number of seconds after which the lock expires
-
-@item --lock-flags=@var{flags}
-Set locker flags. @var{flags} is composed of the following letters:
-@samp{E} -- use external locker program @command{dotlock},
-@samp{R} -- retry 10 times if acquiring of the lock failed (see also
-@option{--lock-retry-count} below), @samp{T} -- remove stale locks
-after 10 minutes (see also @option{--lock-expire-timeout},
-and @samp{P} -- write process @acronym{PID} to the lock file.
-
-@item --lock-retry-count=@var{number}
-Set the maximum number of times to retry acquiring the lockfile
-
-@item --lock-retry-timeout=@var{seconds}
-Set timeout for acquiring the lockfile
-
-@item -m @var{url}
-@itemx --mail-spool @var{URL}
-Use specified URL as a mailspool directory
-
@item --tls[=@var{bool}]
Enable (default) or disable TLS support
@@ -4552,6 +4510,13 @@ Enable (default) or disable TLS support
@item --uidl
Use UIDLs to avoid downloading the same message twice.
+@item -P @var{method-list}
+@itemx --owner=@var{method-list}
+Define list of methods for setting ownership of the destination
+mailbox. @xref{mailbox-ownership-methods}, for a description of
+@var{method-list}. This option is useful only when running
+@command{movemail} as root.
+
@item -v
@item --verbose
Increase verbosity level.
diff --git a/movemail/movemail.c b/movemail/movemail.c
index bacfd407c..99dc5b05c 100644
--- a/movemail/movemail.c
+++ b/movemail/movemail.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <mailutils/mailutils.h>
@@ -39,30 +40,74 @@ static char args_doc[] = N_("inbox-url destfile [POP-password]");
#define OPT_EMACS 256
static struct argp_option options[] = {
- { "preserve", 'p', NULL, 0, N_("Preserve the source mailbox"), 0 },
+ { "preserve", 'p', NULL, 0, N_("Preserve the source mailbox") },
{ "keep-messages", 0, NULL, OPTION_ALIAS, NULL },
- { "reverse", 'r', NULL, 0, N_("Reverse the sorting order"), 0 },
+ { "reverse", 'r', NULL, 0, N_("Reverse the sorting order") },
{ "emacs", OPT_EMACS, NULL, 0,
- N_("Output information used by Emacs rmail interface"), 0 },
- { "copy-permissions", 'P', NULL, 0,
- N_("Copy original mailbox permissions and ownership when applicable"),
- 0 },
+ N_("Output information used by Emacs rmail interface") },
{ "uidl", 'u', NULL, 0,
- N_("Use UIDLs to avoid downloading the same message twice"),
- 0 },
+ N_("Use UIDLs to avoid downloading the same message twice") },
{ "verbose", 'v', NULL, 0,
- N_("Increase verbosity level"),
- 0 },
+ N_("Increase verbosity level") },
+ { "owner", 'P', N_("MODELIST"), 0,
+ N_("Control mailbox ownership") },
{ NULL, 0, NULL, 0, NULL, 0 }
};
static int reverse_order;
static int preserve_mail;
static int emacs_mode;
-static int copy_meta;
static int uidl_option;
static int verbose_option;
+enum set_ownership_mode
+ {
+ copy_owner_id,
+ copy_owner_name,
+ set_owner_id,
+ set_owner_name
+ };
+#define SET_OWNERSHIP_MAX 4
+
+struct user_id
+{
+ uid_t uid;
+ gid_t gid;
+};
+
+struct set_ownership_method
+{
+ enum set_ownership_mode mode;
+ union
+ {
+ char *name;
+ struct user_id id;
+ } owner;
+};
+
+static struct set_ownership_method so_methods[SET_OWNERSHIP_MAX];
+static int so_method_num;
+
+struct set_ownership_method *
+get_next_so_method ()
+{
+ if (so_method_num == MU_ARRAY_SIZE (so_methods))
+ {
+ mu_error (_("ownership method table overflow"));
+ exit (1);
+ }
+ return so_methods + so_method_num++;
+}
+
+mu_kwd_t method_kwd[] = {
+ { "copy-id", copy_owner_id },
+ { "copy-name", copy_owner_name },
+ { "set-name", set_owner_name },
+ { "user", set_owner_name },
+ { "set-id", set_owner_id },
+ { NULL }
+};
+
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
@@ -79,7 +124,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
break;
case 'P':
- copy_meta = 1;
+ mu_argp_node_list_new (&lst, "mailbox-ownership", arg);
break;
case 'u':
@@ -93,7 +138,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
case OPT_EMACS:
mu_argp_node_list_new (&lst, "emacs", "yes");
break;
-
+
case ARGP_KEY_INIT:
mu_argp_node_list_init (&lst);
break;
@@ -118,6 +163,130 @@ static struct argp argp = {
};
+static int
+_cb_mailbox_ownership (mu_debug_t debug, const char *str)
+{
+ if (strcmp (str, "clear") == 0)
+ so_method_num = 0;
+ else
+ {
+ int code;
+ char *p;
+ size_t len = strcspn (str, "=");
+ struct set_ownership_method *meth;
+
+ if (mu_kwd_xlat_name_len (method_kwd, str, len, &code))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("Invalid ownership method: %s"),
+ str);
+ return 1;
+ }
+
+ meth = get_next_so_method ();
+ meth->mode = code;
+ switch (meth->mode)
+ {
+ case copy_owner_id:
+ case copy_owner_name:
+ break;
+
+ case set_owner_id:
+ if (!str[len])
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("Ownership method %s requires value"),
+ str);
+ return 1;
+ }
+ str += len + 1;
+ meth->owner.id.uid = strtoul (str, &p, 0);
+ if (*p)
+ {
+ if (*p == ':')
+ {
+ str = p + 1;
+ meth->owner.id.gid = strtoul (str, &p, 0);
+ if (*p)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("expected gid number, but found %s"),
+ str);
+ return 1;
+ }
+ }
+ else
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("expected uid number, but found %s"),
+ str);
+ return 1;
+ }
+ }
+ else
+ meth->owner.id.gid = (gid_t) -1;
+ break;
+
+ case set_owner_name:
+ if (!str[len])
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("Ownership method %s requires value"),
+ str);
+ return 1;
+ }
+ meth->owner.name = mu_strdup (str + len + 1);
+ }
+ }
+ return 0;
+}
+
+static int
+cb_mailbox_ownership (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+ int i;
+
+ if (val->type == MU_CFG_STRING)
+ {
+ const char *str = val->v.string;
+ if (!strchr (str, ','))
+ return _cb_mailbox_ownership (debug, str);
+ else
+ {
+ int argc;
+ char **argv;
+
+ if (mu_argcv_get_np (str, strlen (str), ",", NULL, 0,
+ &argc, &argv, NULL))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("cannot parse %s"),
+ str);
+ return 1;
+ }
+
+ for (i = 0; i < argc; i++)
+ if (_cb_mailbox_ownership (debug, argv[i]))
+ return 1;
+
+ mu_argcv_free (argc, argv);
+ return 0;
+ }
+ }
+
+ if (mu_cfg_assert_value_type (val, MU_CFG_LIST, debug))
+ return 1;
+
+ for (i = 0; i < val->v.arg.c; i++)
+ {
+ if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING, debug))
+ return 1;
+ if (_cb_mailbox_ownership (debug, val->v.arg.v[i].v.string))
+ return 1;
+ }
+ return 0;
+}
+
struct mu_cfg_param movemail_cfg_param[] = {
{ "preserve", mu_cfg_bool, &preserve_mail, 0, NULL,
N_("Do not remove messages from the source mailbox.") },
@@ -128,7 +297,16 @@ struct mu_cfg_param movemail_cfg_param[] = {
{ "uidl", mu_cfg_bool, &uidl_option, 0, NULL,
N_("Use UIDLs to avoid downloading the same message twice.") },
{ "verbose", mu_cfg_int, &verbose_option, 0, NULL,
- N_("Increase verbosity level.") },
+ N_("Set verbosity level.") },
+ { "mailbox-ownership", mu_cfg_callback, NULL, 0,
+ cb_mailbox_ownership,
+ N_("Define a list of methods for setting mailbox ownership. Valid "
+ "methods are:\n"
+ " copy-id get owner UID and GID from the source mailbox\n"
+ " copy-name get owner name from the source mailbox URL\n"
+ " set-id=UID[:GID] set supplied UID and GID\n"
+ " set-name=USER make destination mailbox owned by USER"),
+ N_("methods: list") },
{ NULL }
};
@@ -300,29 +478,18 @@ close_mailboxes (void)
mu_mailbox_close (dest);
mu_mailbox_close (source);
}
-
-static void
-set_permissions (mu_mailbox_t mbox)
+
+static int
+get_mbox_owner_id (mu_mailbox_t mbox, mu_url_t url, struct user_id *id)
{
- mu_url_t url = NULL;
const char *s;
- int rc;
- uid_t uid;
- gid_t gid;
-
- if (getuid () != 0)
- {
- mu_error (_("must be root to use --copy-permissions"));
- exit (1);
- }
- mu_mailbox_get_url (mbox, &url);
- rc = mu_url_sget_scheme (url, &s);
+ int rc = mu_url_sget_scheme (url, &s);
if (rc)
die (mbox, _("Cannot get scheme"), rc);
- if (strcmp (s, "/") == 0
- || strcmp (s, "mbox") == 0
- || strcmp (s, "mh") == 0
- || strcmp (s, "maildir") == 0)
+ if ((strcmp (s, "/") == 0
+ || strcmp (s, "mbox") == 0
+ || strcmp (s, "mh") == 0
+ || strcmp (s, "maildir") == 0))
{
struct stat st;
@@ -335,33 +502,124 @@ set_permissions (mu_mailbox_t mbox)
mu_strerror (errno));
exit (1);
}
- uid = st.st_uid;
- gid = st.st_gid;
+ id->uid = st.st_uid;
+ id->gid = st.st_gid;
+ return 0;
}
- else
+ else if (verbose_option)
+ mu_diag_output (MU_DIAG_WARNING,
+ _("ignoring copy-name: not a local mailbox"));
+ return 1;
+}
+
+static int
+get_user_id (const char *name, struct user_id *id)
+{
+ struct mu_auth_data *auth = mu_get_auth_by_name (name);
+
+ if (!auth)
{
- struct mu_auth_data *auth;
-
- rc = mu_url_sget_user (url, &s);
- if (rc)
- die (mbox, _("Cannot get user"), rc);
-
- auth = mu_get_auth_by_name (s);
- if (!auth)
- {
- mu_error (_("No such user: %s"), s);
- exit (1);
- }
- else
+ if (verbose_option)
+ mu_diag_output (MU_DIAG_WARNING, _("no such user: %s"), name);
+ return 1;
+ }
+
+ id->uid = auth->uid;
+ id->gid = auth->gid;
+ mu_auth_data_free (auth);
+ return 0;
+}
+
+static int
+get_mbox_owner_name (mu_mailbox_t mbox, mu_url_t url, struct user_id *id)
+{
+ const char *s;
+ int rc = mu_url_sget_user (url, &s);
+ if (rc)
+ /* FIXME */
+ die (mbox, _("Cannot get mailbox owner name"), rc);
+
+ return get_user_id (s, id);
+}
+
+static int
+guess_mbox_owner (mu_mailbox_t mbox, struct user_id *id)
+{
+ mu_url_t url = NULL;
+ int rc;
+ struct set_ownership_method *meth;
+
+ rc = mu_mailbox_get_url (mbox, &url);
+ if (rc)
+ die (mbox, _("Cannot get url"), rc);
+
+ rc = 1;
+ for (meth = so_methods; rc == 1 && meth < so_methods + so_method_num; meth++)
+ {
+ switch (meth->mode)
{
- uid = auth->uid;
- gid = auth->gid;
+ case copy_owner_id:
+ rc = get_mbox_owner_id (mbox, url, id);
+ break;
+
+ case copy_owner_name:
+ rc = get_mbox_owner_name (mbox, url, id);
+ break;
+
+ case set_owner_id:
+ id->uid = meth->owner.id.uid;
+ rc = 0;
+ if (meth->owner.id.gid == (gid_t)-1)
+ {
+ struct passwd *pw = getpwuid (id->uid);
+ if (pw)
+ id->gid = pw->pw_gid;
+ else
+ {
+ if (verbose_option)
+ mu_diag_output (MU_DIAG_WARNING,
+ _("no user with uid %lu found"),
+ (unsigned long) id->uid);
+ rc = 1;
+ }
+ }
+ break;
+
+ case set_owner_name:
+ rc = get_user_id (meth->owner.name, id);
+ break;
}
- mu_auth_data_free (auth);
}
+
+ return rc;
+}
- if (mu_switch_to_privs (uid, gid, NULL))
- exit (1);
+static void
+switch_owner (mu_mailbox_t mbox)
+{
+ struct user_id user_id;
+
+ if (so_method_num == 0)
+ return;
+
+ if (getuid ())
+ {
+ if (verbose_option)
+ mu_diag_output (MU_DIAG_WARNING,
+ _("ignoring mailbox-ownership statement"));
+ return;
+ }
+
+ if (guess_mbox_owner (mbox, &user_id) == 0)
+ {
+ if (mu_switch_to_privs (user_id.uid, user_id.gid, NULL))
+ exit (1);
+ }
+ else
+ {
+ mu_error (_("no suitable method for setting mailbox ownership"));
+ exit (1);
+ }
}
static int
@@ -443,8 +701,7 @@ main (int argc, char **argv)
else
open_mailbox (&source, source_name, flags, argv[2]);
- if (copy_meta)
- set_permissions (source);
+ switch_owner (source);
open_mailbox (&dest, dest_name, MU_STREAM_RDWR | MU_STREAM_CREAT, NULL);

Return to:

Send suggestions and report system problems to the System administrator.