Commit 1c2596eb 1c2596eba7c55f40e5b31e7c310ce3e0777c4ca5 by Sergey Poznyakoff

Implement forwarding functionality in Maidag.

* maidag/forward.c: New file.
* maidag/Makefile.am (maidag_SOURCES): Add forward.c.
* maidag/deliver.c (deliver_url): Call maidag_forward for local
users.
* maidag/maidag.c (forward_file, forward_file_checks): New
variables.
(maidag_cfg_param): New keywords forward-file and
forward-file-checks.
* maidag/maidag.h (FWD_IWGRP, FWD_IWOTH, FWD_LINK, FWD_DIR_IWGRP)
(FWD_DIR_IWOTH, FWD_ALL): New defines.
(forward_file, forward_file_checks): New variables.
(enum maidag_forward_result): New data type.
(maidag_forward): New prototype.
1 parent 2ba12864
2008-11-05 Sergey Poznyakoff <gray@gnu.org.ua>
Implement forwarding functionality in Maidag.
* maidag/forward.c: New file.
* maidag/Makefile.am (maidag_SOURCES): Add forward.c.
* maidag/deliver.c (deliver_url): Call maidag_forward for local
users.
* maidag/maidag.c (forward_file, forward_file_checks): New
variables.
(maidag_cfg_param): New keywords forward-file and
forward-file-checks.
* maidag/maidag.h (FWD_IWGRP, FWD_IWOTH, FWD_LINK, FWD_DIR_IWGRP)
(FWD_DIR_IWOTH, FWD_ALL): New defines.
(forward_file, forward_file_checks): New variables.
(enum maidag_forward_result): New data type.
(maidag_forward): New prototype.
Simplify calls to final delivery functions.
* maidag/deliver.c (mda): Pass msg as a 1st argument to deliver.
......
......@@ -20,6 +20,7 @@ INCLUDES = -I${top_srcdir} @MU_COMMON_INCLUDES@ @GUILE_INCLUDES@
sbin_PROGRAMS=maidag
maidag_SOURCES=\
deliver.c\
forward.c\
lmtp.c\
maidag.c\
maidag.h\
......
......@@ -329,6 +329,22 @@ deliver_url (mu_url_t url, mu_message_t msg, const char *name, char **errp)
mu_auth_data_free (auth);
return 0;
}
if (forward_file)
switch (maidag_forward (msg, auth, forward_file))
{
case maidag_forward_none:
case maidag_forward_metoo:
break;
case maidag_forward_ok:
mu_auth_data_free (auth);
return 0;
case maidag_forward_error:
mu_auth_data_free (auth);
return exit_code = EX_TEMPFAIL;
}
}
if (!url)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2002, 2005,
2007, 2008 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA */
/* ".forward" support for GNU Maidag */
#include "maidag.h"
/* Functions for checking file mode of .forward and its directory.
Each of these checks certain bits and returns 0 if they are OK
and non-0 otherwise. */
static int
check_iwgrp (struct stat *filest, struct stat *dirst)
{
return filest->st_mode & S_IWGRP;
}
static int
check_iwoth (struct stat *filest, struct stat *dirst)
{
return filest->st_mode & S_IWOTH;
}
static int
check_linked_wrdir (struct stat *filest, struct stat *dirst)
{
return (filest->st_mode & S_IFLNK) && (dirst->st_mode & (S_IWGRP | S_IWOTH));
}
static int
check_dir_iwgrp (struct stat *filest, struct stat *dirst)
{
return dirst->st_mode & S_IWGRP;
}
static int
check_dir_iwoth (struct stat *filest, struct stat *dirst)
{
return dirst->st_mode & S_IWOTH;
}
/* The table of permission checkers below has this type: */
struct perm_checker
{
int flag; /* FWD_ flag that enables this entry */
char *descr; /* Textual description to use if FUN returns !0 */
int (*fun) (struct stat *filest, struct stat *dirst); /* Checker function */
};
static struct perm_checker perm_check_tab[] = {
{ FWD_IWGRP, N_("group writable forward file"), check_iwgrp },
{ FWD_IWOTH, N_("world writable forward file"), check_iwoth },
{ FWD_LINK, N_("linked forward file in writable dir"), check_linked_wrdir },
{ FWD_DIR_IWGRP, N_("forward file in group writable directory"),
check_dir_iwgrp },
{ FWD_DIR_IWOTH, N_("forward file in world writable directory"),
check_dir_iwoth },
{ 0 }
};
/* Check if the forwrd file FILENAME has right permissions and file mode.
DIRST describes the directory holding the file, AUTH gives current user
authority. */
int
check_forward_permissions (const char *filename, struct stat *dirst,
struct mu_auth_data *auth)
{
struct stat st;
if (stat (filename, &st) == 0)
{
int i;
if (auth->uid != st.st_uid)
{
mu_error (_("%s not owned by %s"), filename, auth->name);
return 1;
}
for (i = 0; perm_check_tab[i].flag; i++)
if ((forward_file_checks & perm_check_tab[i].flag)
&& perm_check_tab[i].fun (&st, dirst))
{
mu_error ("%s: %s", filename, gettext (perm_check_tab[i].descr));
return 1;
}
return 0;
}
else if (errno != ENOENT)
mu_error (_("%s: cannot stat file: %s"),
filename, mu_strerror (errno));
return 1;
}
/* Auxiliary functions */
/* Forward message MSG to given EMAIL, using MAILER and sender address FROM */
static int
forward_to_email (mu_message_t msg, mu_address_t from,
mu_mailer_t mailer, const char *email)
{
mu_address_t to;
int rc;
rc = mu_address_create (&to, email);
if (rc)
{
mu_error (_("%s: cannot create email: %s"), email, mu_strerror (rc));
return 1;
}
rc = mu_mailer_send_message (mailer, msg, from, to);
if (rc)
mu_error (_("Sending message to `%s' failed: %s"),
email, mu_strerror (rc));
mu_address_destroy (&to);
return rc;
}
/* Create a mailer if it does not already exist.*/
int
forward_mailer_create (mu_mailer_t *pmailer)
{
int rc;
if (*pmailer == NULL)
{
rc = mu_mailer_create (pmailer, NULL);
if (rc)
{
const char *url = NULL;
mu_mailer_get_url_default (&url);
mu_error (_("Creating mailer `%s' failed: %s"),
url, mu_strerror (rc));
return 1;
}
rc = mu_mailer_open (*pmailer, 0);
if (rc)
{
const char *url = NULL;
mu_mailer_get_url_default (&url);
mu_error (_("Opening mailer `%s' failed: %s"),
url, mu_strerror (rc));
mu_mailer_destroy (pmailer);
return 1;
}
}
return 0;
}
/* Create *PFROM (if it is NULL), from the envelope sender address of MSG. */
static int
create_from_address (mu_message_t msg, mu_address_t *pfrom)
{
if (!*pfrom)
{
mu_envelope_t envelope;
const char *str;
int status = mu_message_get_envelope (msg, &envelope);
if (status)
{
mu_error (_("cannot get envelope: %s"), mu_strerror (status));
return 1;
}
status = mu_envelope_sget_sender (envelope, &str);
if (status)
{
mu_error (_("cannot get envelope sender: %s"), mu_strerror (status));
return 1;
}
status = mu_address_create (pfrom, str);
if (status)
{
mu_error (_("%s: cannot create email: %s"), str,
mu_strerror (status));
return 1;
}
}
return 0;
}
/* Forward message MSG as requested by file FILENAME.
MYNAME gives local user name. */
enum maidag_forward_result
process_forward (mu_message_t msg, char *filename, const char *myname)
{
FILE *fp;
size_t size = 0;
char *buf = NULL;
enum maidag_forward_result result = maidag_forward_ok;
mu_mailer_t mailer = NULL;
mu_address_t from = NULL;
fp = fopen (filename, "r");
if (!fp)
{
mu_error (_("%s: cannot open forward file: %s"),
filename, mu_strerror (errno));
return maidag_forward_error;
}
while (getline (&buf, &size, fp) > 0)
{
char *p, *q;
for (p = buf; *p && isascii (*p) && isspace (*p); p++)
;
q = p + strlen (p);
if (q > p)
{
if (*--q == '\n')
q--;
for (; q > p && isascii (*q) && isspace (*q); q--)
;
q[1] = 0;
}
if (*p && *p != '#')
{
if (strchr (p, '@'))
{
if (create_from_address (msg, &from)
|| forward_mailer_create (&mailer)
|| forward_to_email (msg, from, mailer, p))
result = maidag_forward_error;
}
else
{
if (*p == '\\')
p++;
if (strcmp (p, myname) == 0)
{
if (result == maidag_forward_ok)
result = maidag_forward_metoo;
}
else if (deliver (msg, p, NULL))
result = maidag_forward_error;
}
}
}
mu_address_destroy (&from);
if (mailer)
{
mu_mailer_close (mailer);
mu_mailer_destroy (&mailer);
}
free (buf);
fclose (fp);
return result;
}
/* Check if the forward file FWFILE for user given by AUTH exists, and if
so, use to to forward message MSG. */
enum maidag_forward_result
maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
{
struct stat st;
char *filename;
enum maidag_forward_result result = maidag_forward_none;
if (stat (auth->dir, &st))
{
if (errno == ENOENT)
/* FIXME: a warning, maybe? */;
else if (!S_ISDIR (st.st_mode))
mu_error (_("%s: not a directory"), auth->dir);
else
mu_error (_("%s: cannot stat directory: %s"),
auth->dir, mu_strerror (errno));
return maidag_forward_none;
}
asprintf (&filename, "%s/%s", auth->dir, fwfile);
if (!filename)
{
mu_error ("%s", mu_strerror (errno));
return maidag_forward_error;
}
if (check_forward_permissions (filename, &st, auth) == 0)
result = process_forward (msg, filename, auth->name);
free (filename);
return result;
}
......@@ -33,6 +33,9 @@ char *sender_address = NULL;
char *progfile_pattern = NULL;
char *sieve_pattern = NULL;
char *forward_file = NULL;
int forward_file_checks = FWD_ALL;
int log_to_stderr = -1;
/* Debuggig options */
......@@ -277,6 +280,55 @@ cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg)
return mu_cfg_string_value_cb (debug, arg, cb2_group, data);
}
static struct mu_kwd forward_checks[] = {
{ "all", FWD_ALL },
{ "groupwritablefile", FWD_IWGRP },
{ "file_iwgrp", FWD_IWGRP },
{ "worldwritablefile", FWD_IWOTH },
{ "file_iwoth", FWD_IWOTH },
{ "linkedfileinwritabledir", FWD_LINK },
{ "link", FWD_LINK },
{ "fileingroupwritabledir", FWD_DIR_IWGRP },
{ "dir_iwgrp", FWD_DIR_IWGRP },
{ "fileinworldwritabledir", FWD_DIR_IWOTH },
{ "dir_iwoth", FWD_DIR_IWOTH },
{ NULL }
};
static int
cb2_forward_file_checks (mu_debug_t debug, const char *name, void *data)
{
int negate = 0;
const char *str;
int val;
if (strlen (name) > 2 && strncasecmp (name, "no", 2) == 0)
{
negate = 1;
str = name + 2;
}
else
str = name;
if (mu_kwd_xlat_name_ci (forward_checks, str, &val))
mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Unknown keyword: %s"),
name);
else
{
if (negate)
forward_file_checks &= ~val;
else
forward_file_checks |= val;
}
return 0;
}
static int
cb_forward_file_checks (mu_debug_t debug, void *data, mu_config_value_t *arg)
{
return mu_cfg_string_value_cb (debug, arg, cb2_forward_file_checks, 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 "
......@@ -316,6 +368,11 @@ struct mu_cfg_param maidag_cfg_param[] = {
" l - sieve action logs\n") },
{ "stderr", mu_cfg_bool, &log_to_stderr, 0, NULL,
N_("Log to stderr instead of syslog.") },
{ "forward-file", mu_cfg_string, &forward_file, 0, NULL,
N_("Process forward file.") },
{ "forward-file-checks", mu_cfg_callback, NULL, 0, cb_forward_file_checks,
N_("Configure safety checks for the forward file."),
N_("arg: list") },
/* LMTP support */
{ "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL,
N_("Run in LMTP mode.") },
......
......@@ -109,6 +109,14 @@ extern int debug_level;
#define MAXFD 64
#define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE)
/* .forward file checks */
#define FWD_IWGRP 0x0001 /* group writable forward file */
#define FWD_IWOTH 0x0002 /* world writable forward file */
#define FWD_LINK 0x0004 /* linked forward file in writable dir */
#define FWD_DIR_IWGRP 0x0008 /* forward file in group writable directory */
#define FWD_DIR_IWOTH 0x0010 /* forward file in world writable directory */
#define FWD_ALL (FWD_IWGRP|FWD_IWOTH|FWD_LINK|FWD_DIR_IWOTH|FWD_DIR_IWGRP)
extern int exit_code;
extern int log_to_stderr;
extern int multiple_delivery;
......@@ -117,6 +125,9 @@ extern uid_t current_uid;
extern char *quotadbname;
extern char *quota_query;
extern char *forward_file;
extern int forward_file_checks;
extern char *sender_address;
extern char *progfile_pattern;
extern char *sieve_pattern;
......@@ -165,3 +176,15 @@ int mail_tmp_begin (struct mail_tmp **pmtmp, const char *from);
int mail_tmp_add_line (struct mail_tmp *mtmp, char *buf, size_t buflen);
int mail_tmp_finish (struct mail_tmp *mtmp, mu_mailbox_t *mbox);
void mail_tmp_destroy (struct mail_tmp **pmtmp);
enum maidag_forward_result
{
maidag_forward_none,
maidag_forward_ok,
maidag_forward_metoo,
maidag_forward_error
};
enum maidag_forward_result maidag_forward (mu_message_t msg,
struct mu_auth_data *auth,
char *fwfile);
......