Commit 437bd788 437bd78824ad255ed9013a9501cbb86aa8361874 by Sergey Poznyakoff

Make maidag hanlde mailer URLs.

This makes it possible to `deliver' mails to mailers without
explicitly specifying `remote+mailer' mailbox URLs.  In
particular, it is handy for implementing mailing lists using
`prog' mailers (as with Mailman).

* maidag/deliver.c (notify_action): Do nothing if biff_user_name
is empty.
(deliver_to_user): Remove `name' argument.  Allow for auth==NULL.
(is_remote_url): Use case-sensitive comparison.
(is_mailer_url): New function.
(deliver_url): Handle mailer URLs as if they were corresponding
`remote+' mailbox URLs.  This allows to handle mailing lists.
* maidag/lmtp.c (lmtp_groups): New global.
(lmtp_set_privs): New function. Sets primary and
supplementary group privileges.
(maidag_lmtp_server): Call lmtp_set_privs.
* maidag/maidag.c (lmtp_group): Remove.  It is superceded by
lmtp_groups.
(maidag_cfg_param): Allow to specify supplementary groups.
* maidag/maidag.h (lmtp_group): Replace with lmtp_groups.
1 parent 01dabb5c
2008-10-28 Sergey Poznyakoff <gray@gnu.org.ua>
Make maidag hanlde mailer URLs.
This makes it possible to `deliver' mails to mailers without
explicitly specifying `remote+mailer' mailbox URLs. In
particular, it is handy for implementing mailing lists using
`prog' mailers (as with Mailman).
* maidag/deliver.c (notify_action): Do nothing if biff_user_name
is empty.
(deliver_to_user): Remove `name' argument. Allow for auth==NULL.
(is_remote_url): Use case-sensitive comparison.
(is_mailer_url): New function.
(deliver_url): Handle mailer URLs as if they were corresponding
`remote+' mailbox URLs. This allows to handle mailing lists.
* maidag/lmtp.c (lmtp_groups): New global.
(lmtp_set_privs): New function. Sets primary and
supplementary group privileges.
(maidag_lmtp_server): Call lmtp_set_privs.
* maidag/maidag.c (lmtp_group): Remove. It is superceded by
lmtp_groups.
(maidag_cfg_param): Allow to specify supplementary groups.
* maidag/maidag.h (lmtp_group): Replace with lmtp_groups.
Change mailer creation mechanism.
* include/mailutils/mailer.h (mu_mailer_create_from_url): New
......
......@@ -87,7 +87,7 @@ static const char *biff_user_name;
static int
notify_action (mu_observer_t obs, size_t type, void *data, void *action_data)
{
if (type == MU_EVT_MESSAGE_APPEND)
if (type == MU_EVT_MESSAGE_APPEND && biff_user_name)
{
mu_message_qid_t qid = data;
mu_mailbox_t mbox = mu_observer_get_owner (obs);
......@@ -144,7 +144,7 @@ attach_notify (mu_mailbox_t mbox)
int
deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
struct mu_auth_data *auth, const char *name,
struct mu_auth_data *auth,
char **errp)
{
int status;
......@@ -184,59 +184,61 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
}
#if defined(USE_MAILBOX_QUOTAS)
{
mu_off_t n;
mu_off_t msg_size;
mu_off_t mbsize;
if ((status = mu_mailbox_get_size (mbox, &mbsize)))
{
maidag_error (_("Cannot get size of mailbox %s: %s"),
path, mu_strerror (status));
if (status == ENOSYS)
mbsize = 0; /* Try to continue anyway */
else
return EX_TEMPFAIL;
}
if (auth)
{
mu_off_t n;
mu_off_t msg_size;
mu_off_t mbsize;
if ((status = mu_mailbox_get_size (mbox, &mbsize)))
{
maidag_error (_("Cannot get size of mailbox %s: %s"),
path, mu_strerror (status));
if (status == ENOSYS)
mbsize = 0; /* Try to continue anyway */
else
return EX_TEMPFAIL;
}
switch (check_quota (auth, mbsize, &n))
{
case MQUOTA_EXCEEDED:
maidag_error (_("%s: mailbox quota exceeded for this recipient"), name);
if (errp)
asprintf (errp, "%s: mailbox quota exceeded for this recipient",
name);
exit_code = EX_QUOTA();
failed++;
break;
case MQUOTA_UNLIMITED:
break;
default:
if ((status = mu_mailbox_get_size (imbx, &msg_size)))
{
maidag_error (_("Cannot get message size (input message %s): %s"),
path, mu_strerror (status));
exit_code = EX_UNAVAILABLE;
failed++;
}
else if (msg_size > n)
{
maidag_error (_("%s: message would exceed maximum mailbox size for "
"this recipient"),
name);
if (errp)
asprintf (errp,
"%s: message would exceed maximum mailbox size "
"for this recipient",
name);
exit_code = EX_QUOTA();
failed++;
}
break;
}
}
switch (check_quota (auth, mbsize, &n))
{
case MQUOTA_EXCEEDED:
maidag_error (_("%s: mailbox quota exceeded for this recipient"),
auth->name);
if (errp)
asprintf (errp, "%s: mailbox quota exceeded for this recipient",
auth->name);
exit_code = EX_QUOTA();
failed++;
break;
case MQUOTA_UNLIMITED:
break;
default:
if ((status = mu_mailbox_get_size (imbx, &msg_size)))
{
maidag_error (_("Cannot get message size (input message %s): %s"),
path, mu_strerror (status));
exit_code = EX_UNAVAILABLE;
failed++;
}
else if (msg_size > n)
{
maidag_error (_("%s: message would exceed maximum mailbox size for "
"this recipient"),
auth->name);
if (errp)
asprintf (errp,
"%s: message would exceed maximum mailbox size "
"for this recipient",
auth->name);
exit_code = EX_QUOTA();
failed++;
}
break;
}
}
#endif
if (!failed && switch_user_id (auth, 1) == 0)
......@@ -271,9 +273,24 @@ is_remote_url (mu_url_t url)
{
const char *scheme;
int rc = mu_url_sget_scheme (url, &scheme);
return rc == 0 && strncasecmp (scheme, "remote+", 7) == 0;
return rc == 0 && strncmp (scheme, "remote+", 7) == 0;
}
static int
is_mailer_url (mu_url_t url)
{
mu_record_t record = NULL;
int (*pfn) (mu_mailer_t) = NULL;
return mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE,
&record, NULL) == 0
&& mu_record_get_mailer (record, &pfn) == 0
&& pfn;
}
#define REMOTE_PREFIX "remote+"
#define REMOTE_PREFIX_LEN (sizeof(REMOTE_PREFIX)-1)
int
deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
{
......@@ -282,9 +299,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
mu_message_t msg;
int status;
if (is_remote_url (url))
auth = NULL;
else
if (name)
{
auth = mu_get_auth_by_name (name);
if (!auth)
......@@ -292,9 +307,10 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
maidag_error (_("%s: no such user"), name);
if (errp)
asprintf (errp, "%s: no such user", name);
exit_code = EX_UNAVAILABLE;
return EX_UNAVAILABLE;
exit_code = EX_NOUSER;
return EX_NOUSER;
}
if (current_uid)
auth->change_uid = 0;
......@@ -314,16 +330,59 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
return EX_TEMPFAIL;
}
if (url)
status = mu_mailbox_create_from_url (&mbox, url);
else
status = mu_mailbox_create (&mbox, auth->mailbox);
if (!url)
{
status = mu_url_create (&url, auth->mailbox);
if (status)
{
maidag_error (_("Cannot create URL for %s: %s"),
auth->mailbox, mu_strerror (status));
return exit_code = EX_UNAVAILABLE;
}
status = mu_url_parse (url);
if (status)
{
maidag_error (_("Error parsing URL %s: %s"),
auth->mailbox, mu_strerror (status));
return exit_code = EX_UNAVAILABLE;
}
}
if (is_mailer_url (url))
{
const char *scheme;
size_t len;
char *new_scheme;
mu_url_sget_scheme (url, &scheme);
len = REMOTE_PREFIX_LEN + strlen (scheme);
new_scheme = malloc (len + 1);
if (!new_scheme)
{
mu_error (_("Not enough memory"));
exit (EX_SOFTWARE);
}
strcat (strcpy (new_scheme, REMOTE_PREFIX), scheme);
status = mu_url_set_scheme (url, new_scheme);
free (new_scheme);
if (status)
{
errno = status;
maidag_error (_("Cannot modify URL %s: %s"),
mu_url_to_string (url),
mu_strerror (status));
mu_auth_data_free (auth);
return EX_TEMPFAIL;
}
}
status = mu_mailbox_create_from_url (&mbox, url);
if (status)
{
maidag_error (_("Cannot open mailbox %s: %s"),
url ? mu_url_to_string (url): auth->mailbox,
mu_url_to_string (url),
mu_strerror (status));
mu_url_destroy (&url);
mu_auth_data_free (auth);
return EX_TEMPFAIL;
}
......@@ -335,7 +394,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
will be created */
if (switch_user_id (auth, 1))
return EX_TEMPFAIL;
status = deliver_to_user (imbx, mbox, msg, auth, name, errp);
status = deliver_to_user (imbx, mbox, msg, auth, errp);
if (switch_user_id (auth, 0))
return EX_TEMPFAIL;
......@@ -371,7 +430,18 @@ deliver (mu_mailbox_t imbx, char *dest_id, char **errp)
}
status = mu_url_sget_user (url, &name);
if (status == MU_ERR_NOENT)
name = NULL;
{
if (!is_mailer_url (url) && !is_remote_url (url))
{
maidag_error (_("no user name"));
if (errp)
asprintf (errp, "no user such");
exit_code = EX_NOUSER;
return EX_NOUSER;
}
else
name = NULL;
}
else if (status)
{
maidag_error (_("%s: cannot get user name from url: %s"),
......
......@@ -25,6 +25,7 @@
#include <signal.h>
#include <mu_umaxtostr.h>
mu_list_t lmtp_groups;
static int lmtp_transcript;
void
......@@ -572,22 +573,67 @@ lmtp_connection (int fd, struct sockaddr *sa, int salen, void *data,
return 0;
}
int
maidag_lmtp_server ()
static int
lmtp_set_privs ()
{
struct group *gr = getgrnam (lmtp_group);
if (gr == NULL)
gid_t gid;
if (lmtp_groups)
{
mu_error (_("Error getting mail group"));
return EX_UNAVAILABLE;
gid_t *gidset = NULL;
size_t size = 0;
size_t j = 0;
mu_iterator_t itr;
int rc;
mu_list_count (lmtp_groups, &size);
gidset = calloc (size, sizeof (gidset[0]));
if (!gidset)
{
mu_error (_("Not enough memory"));
return EX_UNAVAILABLE;
}
if (mu_list_get_iterator (lmtp_groups, &itr) == 0)
{
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
mu_iterator_current (itr,
(void **)(gidset + j++));
mu_iterator_destroy (&itr);
}
gid = gidset[0];
rc = setgroups (j, gidset);
free (gidset);
if (rc)
{
mu_error(_("setgroups failed: %s"), mu_strerror (errno));
return EX_UNAVAILABLE;
}
}
if (setgid (gr->gr_gid) == -1)
else
{
struct group *gr = getgrnam ("mail");
if (gr == NULL)
{
mu_error (_("Error getting group `mail'"));
return EX_UNAVAILABLE;
}
gid = gr->gr_gid;
}
if (setgid (gid) == -1)
{
mu_error (_("Error setting mail group"));
return EX_UNAVAILABLE;
}
return 0;
}
int
maidag_lmtp_server ()
{
int rc = lmtp_set_privs ();
if (rc)
return rc;
if (mu_m_server_mode (server) == MODE_DAEMON)
{
......
......@@ -48,7 +48,6 @@ int lmtp_mode;
int url_option;
char *lmtp_url_string;
int reuse_lmtp_address = 1;
char *lmtp_group = "mail";
const char *program_version = "maidag (" PACKAGE_STRING ")";
static char doc[] =
......@@ -256,6 +255,28 @@ cb_debug (mu_debug_t debug, void *data, mu_config_value_t *val)
return 0;
}
static int
cb2_group (mu_debug_t debug, const char *gname, void *data)
{
mu_list_t *plist = data;
struct group *group;
if (!*plist)
mu_list_create (plist);
group = getgrnam (gname);
if (!group)
mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Unknown group: %s"), gname);
else
mu_list_append (*plist, (void*)group->gr_gid);
return 0;
}
static int
cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg)
{
return mu_cfg_string_value_cb (debug, arg, cb2_group, data);
}
struct mu_cfg_param maidag_cfg_param[] = {
{ "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL,
N_("In case of multiple delivery, exit with code 0 if at least one "
......@@ -298,8 +319,9 @@ struct mu_cfg_param maidag_cfg_param[] = {
/* LMTP support */
{ "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL,
N_("Run in LMTP mode.") },
{ "group", mu_cfg_string, &lmtp_group, 0, NULL,
N_("In LMTP mode, change to this group after startup.") },
{ "group", mu_cfg_callback, &lmtp_groups, 0, cb_group,
N_("In LMTP mode, retain these supplementary groups."),
N_("groups: list of string") },
{ "listen", mu_cfg_string, &lmtp_url_string, 0, NULL,
N_("In LMTP mode, listen on the given URL. Valid URLs are:\n"
" tcp://<address: string>:<port: number> (note that port is "
......
......@@ -126,7 +126,7 @@ extern int lmtp_mode;
extern int url_option;
extern char *lmtp_url_string;
extern int reuse_lmtp_address;
extern char *lmtp_group;
extern mu_list_t lmtp_groups;
extern mu_acl_t maidag_acl;
void close_fds (void);
......