masqmail

changeset 387:a408411ff8df

Added a glob-pattern aliasing facility. One use-case is virtual hosting another catch-all maildrops, but you may use it as a more flexible aliasing mechanism as well.
author markus schnalke <meillo@marmaro.de>
date Sat, 18 Feb 2012 12:35:12 +0100
parents 13c9e0969054
children aa40710f09fe
files man/masqmail.aliases.5 man/masqmail.conf.5 src/alias.c src/conf.c src/deliver.c src/masqmail.h
diffstat 6 files changed, 142 insertions(+), 27 deletions(-) [+]
line diff
     1.1 --- a/man/masqmail.aliases.5	Sat Feb 18 11:43:06 2012 +0100
     1.2 +++ b/man/masqmail.aliases.5	Sat Feb 18 12:35:12 2012 +0100
     1.3 @@ -7,21 +7,44 @@
     1.4  .SH DESCRIPTION
     1.5  
     1.6  This man page describes the format of the masqmail alias file.
     1.7 -Its usual location is \fI/etc/aliases\fR.
     1.8 +It's usual location is \fI/etc/aliases\fR.
     1.9 +
    1.10 +There exists also a variant of this format for glob patterns.
    1.11 +It's used with the \fIglobalias_file\fP config option.
    1.12 +
    1.13 +The difference between the two formats are only on the left-hand side.
    1.14 +A normal alias file has a local_part there that gets string-compared
    1.15 +against the local part;
    1.16 +whereas a glob alias file has a glob pattern that is matched against
    1.17 +the whole address. The right-hand side of the two aliasing kinds
    1.18 +has the same format.
    1.19  
    1.20  
    1.21  .SH FILE FORMAT
    1.22  
    1.23 -The alias file consists of lines of the form:
    1.24 +Normal alias files consist of lines of the form:
    1.25 +
    1.26 +.RS
    1.27  local_part: item1, item2, ...
    1.28 +.RE
    1.29 +
    1.30 +Glob-pattern alias files consist of lines of the form:
    1.31 +
    1.32 +.RS
    1.33 +glob_pattern: item1, item2, ...
    1.34 +.RE
    1.35 +
    1.36  Items can be surrounded by double quotes `"'.
    1.37  If within the quotes other quotes are needed for an address they can be
    1.38  escaped with a leading backslash `\\'.
    1.39  
    1.40 -A leading backslash `\\' indicates that this address shall not be further expanded.
    1.41 +A leading backslash `\\' indicates that this address shall not be
    1.42 +further expanded.
    1.43  
    1.44 -A leading pipe symbol `|' indicates that the item shall be treated as a pipe command.
    1.45 -The content of the message will then be sent to the standard input of the command.
    1.46 +A leading pipe symbol `|' indicates that the item shall be treated as a
    1.47 +pipe command.
    1.48 +The content of the message will then be sent to the standard input of the
    1.49 +command.
    1.50  The command will run under the user id and group id masqmail is running as.
    1.51  If quotes are needed, the pipe symbol must appear within the quotes.
    1.52  
    1.53 @@ -32,11 +55,15 @@
    1.54  any alias which matches one of the recipient addresses,
    1.55  the change will have effect next time a delivery is attemped.
    1.56  
    1.57 -There is no need to restart masqmail or run any command when the alias file has been changed.
    1.58 +There is no need to restart masqmail or run any command when the alias
    1.59 +file has been changed. (`masqmail -bi' is a no-op.)
    1.60  
    1.61  
    1.62  .SH EXAMPLE
    1.63  
    1.64 +A normal alias file:
    1.65 +
    1.66 +.RS
    1.67  .nf
    1.68  # postmaster is required by RFC 2821
    1.69  postmaster: root
    1.70 @@ -50,6 +77,28 @@
    1.71  # pass mail to a script
    1.72  foo: |/usr/bin/foo
    1.73  .fi
    1.74 +.RE
    1.75 +
    1.76 +
    1.77 +A glob-pattern alias file:
    1.78 +
    1.79 +.RS
    1.80 +.nf
    1.81 +# the postmaster for any domain on this host
    1.82 +postmaster@*: ken
    1.83 +
    1.84 +# split virtual domains
    1.85 +info@foo.example.org: doug
    1.86 +info@bar.example.org: rob
    1.87 +
    1.88 +# the order of entries is important
    1.89 +# this must be before the catch-all line
    1.90 +list\-*@example.org: |/path/to/some/script
    1.91 +
    1.92 +# catch-all address forwarded to extern
    1.93 +*@example.org: brian@other.host.net
    1.94 +.fi
    1.95 +.RE
    1.96  
    1.97  
    1.98  .SH AUTHOR
    1.99 @@ -57,8 +106,10 @@
   1.100  Masqmail was written by Oliver Kurth.
   1.101  It is now maintained by Markus Schnalke <meillo@marmaro.de>.
   1.102  
   1.103 -You will find the newest version of masqmail at \fBhttp://marmaro.de/prog/masqmail/\fR.
   1.104 -There is also a mailing list, you will find information about it at masqmail's main site.
   1.105 +You will find the newest version of masqmail at
   1.106 +\fBhttp://marmaro.de/prog/masqmail/\fR.
   1.107 +There is also a mailing list, you will find information about
   1.108 +it at masqmail's main site.
   1.109  
   1.110  
   1.111  .SH BUGS
     2.1 --- a/man/masqmail.conf.5	Sat Feb 18 11:43:06 2012 +0100
     2.2 +++ b/man/masqmail.conf.5	Sat Feb 18 12:35:12 2012 +0100
     2.3 @@ -276,6 +276,22 @@
     2.4  Default: <not set> (i.e. no aliasing is done)
     2.5  
     2.6  .TP
     2.7 +\fBglobalias_file = \fIfile\fR
     2.8 +
     2.9 +Set this to the location of a glob-pattern alias file.
    2.10 +This kind of aliasing matches glob patterns against full email addresses,
    2.11 +not strings against local parts like in normal aliasing.
    2.12 +You can use this to handle catch-all maildrops (``*@example.org'')
    2.13 +and to split between virtual hosts on a single machine
    2.14 +(e.g. ``info@foo.ex.org'' and ``info@bar.ex.org'').
    2.15 +
    2.16 +Glob aliasing is done before normal aliasing.
    2.17 +If you have both kinds, glob and normal aliasing, then the results of the
    2.18 +glob aliasing may be expanded further by the normal aliasing mechanism.
    2.19 +
    2.20 +Default: <not set> (i.e. no glob aliasing is done)
    2.21 +
    2.22 +.TP
    2.23  \fBcaseless_matching = \fIboolean\fR
    2.24  
    2.25  If this is set, aliasing and the matching for \fBlocal_addresses\fP and
     3.1 --- a/src/alias.c	Sat Feb 18 11:43:06 2012 +0100
     3.2 +++ b/src/alias.c	Sat Feb 18 12:35:12 2012 +0100
     3.3 @@ -104,11 +104,28 @@
     3.4  	return list;
     3.5  }
     3.6  
     3.7 +static int
     3.8 +globaliascmp(const char *pattern, const char *addr)
     3.9 +{
    3.10 +	if (conf.localpartcmp==strcasecmp) {
    3.11 +		return fnmatch(pattern, addr, FNM_CASEFOLD);
    3.12 +	} else if (strncasecmp(addr, "postmaster", 10)==0) {
    3.13 +		/*
    3.14 +		**  postmaster must always be matched caseless
    3.15 +		**  see RFC 822 and RFC 5321
    3.16 +		*/
    3.17 +		return fnmatch(pattern, addr, FNM_CASEFOLD);
    3.18 +	} else {
    3.19 +		/* case-sensitive */
    3.20 +		return fnmatch(pattern, addr, 0);
    3.21 +	}
    3.22 +}
    3.23 +
    3.24  /*
    3.25  **  addr is assumed to be local and no pipe address nor not-to-expand
    3.26  */
    3.27  static GList*
    3.28 -expand_one(GList *alias_table, address *addr)
    3.29 +expand_one(GList *alias_table, address *addr, int doglob)
    3.30  {
    3.31  	GList *val_list;
    3.32  	GList *val_node;
    3.33 @@ -117,24 +134,33 @@
    3.34  	gchar *val;
    3.35  
    3.36  	/* expand the local alias */
    3.37 -	DEBUG(6) debugf("alias: '%s' is local and will get expanded\n", addr->local_part);
    3.38 +	DEBUG(6) debugf("alias: '%s' is local and will get expanded\n",
    3.39 +			doglob ? addr->address : addr->local_part);
    3.40  
    3.41 -	if (strcasecmp(addr->local_part, "postmaster") == 0) {
    3.42 +	if (doglob) {
    3.43 +		val = (gchar *) table_find_func(alias_table, addr->address,
    3.44 +				globaliascmp);
    3.45 +
    3.46 +	} else if (strcasecmp(addr->local_part, "postmaster") == 0) {
    3.47  		/*
    3.48  		**  postmaster must always be matched caseless
    3.49  		**  see RFC 822 and RFC 5321
    3.50  		*/
    3.51 -		val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp);
    3.52 +		val = (gchar *) table_find_func(alias_table, addr->local_part,
    3.53 +				strcasecmp);
    3.54  	} else {
    3.55 -		val = (gchar *) table_find_func(alias_table, addr->local_part, conf.localpartcmp);
    3.56 +		val = (gchar *) table_find_func(alias_table, addr->local_part,
    3.57 +				conf.localpartcmp);
    3.58  	}
    3.59  	if (!val) {
    3.60 -		DEBUG(5) debugf("alias: '%s' is fully expanded, hence completed\n",
    3.61 -		                addr->local_part);
    3.62 +		DEBUG(5) debugf("alias: '%s' is fully expanded, hence "
    3.63 +				"completed\n",
    3.64 +				doglob ? addr->address : addr->local_part);
    3.65  		return g_list_append(NULL, addr);
    3.66  	}
    3.67  
    3.68 -	DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val);
    3.69 +	DEBUG(5) debugf("alias: '%s' -> '%s'\n",
    3.70 +			doglob ? addr->address : addr->local_part, val);
    3.71  	val_list = parse_list(val);
    3.72  	alias_list = NULL;
    3.73  
    3.74 @@ -149,7 +175,7 @@
    3.75  			alias_addr = create_address_qualified(val+1, TRUE, conf.host_name);
    3.76  			g_free(val);
    3.77  			DEBUG(6) debugf("alias:     address generated: '%s'\n",
    3.78 -			                alias_addr->local_part);
    3.79 +			                alias_addr->address);
    3.80  			alias_list = g_list_append(alias_list, alias_addr);
    3.81  			continue;
    3.82  		}
    3.83 @@ -168,8 +194,8 @@
    3.84  		g_free(val);
    3.85  
    3.86  		if (!addr_is_local(alias_addr)) {
    3.87 -			DEBUG(5) debugf("alias: '%s@%s' is non-local, hence completed\n",
    3.88 -					alias_addr->local_part, alias_addr->domain);
    3.89 +			DEBUG(5) debugf("alias: '%s' is non-local, hence completed\n",
    3.90 +					alias_addr->address);
    3.91  			alias_list = g_list_append(alias_list, alias_addr);
    3.92  			continue;
    3.93  		}
    3.94 @@ -186,7 +212,7 @@
    3.95  
    3.96  		/* recurse */
    3.97  		DEBUG(6) debugf("alias: >>\n");
    3.98 -		alias_node = expand_one(alias_table, alias_addr);
    3.99 +		alias_node = expand_one(alias_table, alias_addr, doglob);
   3.100  		DEBUG(6) debugf("alias: <<\n");
   3.101  		if (alias_node) {
   3.102  			alias_list = g_list_concat(alias_list, alias_node);
   3.103 @@ -199,7 +225,8 @@
   3.104  }
   3.105  
   3.106  GList*
   3.107 -alias_expand(GList *alias_table, GList *rcpt_list, GList *non_rcpt_list)
   3.108 +alias_expand(GList *alias_table, GList *rcpt_list, GList *non_rcpt_list,
   3.109 +		int doglob)
   3.110  {
   3.111  	GList *rcpt_node = NULL;
   3.112  	GList *alias_list = NULL;
   3.113 @@ -214,14 +241,14 @@
   3.114  		addr = (address *) (rcpt_node->data);
   3.115  		if (addr_is_local(addr)) {
   3.116  			DEBUG(5) debugf("alias: (orig rcpt addr) expand local '%s'\n",
   3.117 -			                addr->local_part);
   3.118 -			alias_list = expand_one(alias_table, addr);
   3.119 +			                doglob ? addr->address : addr->local_part);
   3.120 +			alias_list = expand_one(alias_table, addr, doglob);
   3.121  			if (alias_list) {
   3.122  				done_list = g_list_concat(done_list, alias_list);
   3.123  			}
   3.124  		} else {
   3.125 -			DEBUG(5) debugf("alias: (orig rcpt addr) don't expand non-local '%s@%s'\n",
   3.126 -					addr->local_part, addr->domain);
   3.127 +			DEBUG(5) debugf("alias: (orig rcpt addr) don't expand non-local '%s'\n",
   3.128 +					addr->address);
   3.129  			done_list = g_list_append(done_list, addr);
   3.130  		}
   3.131  	}
     4.1 --- a/src/conf.c	Sat Feb 18 11:43:06 2012 +0100
     4.2 +++ b/src/conf.c	Sat Feb 18 12:35:12 2012 +0100
     4.3 @@ -472,6 +472,8 @@
     4.4  			conf.do_relay = parse_boolean(rval);
     4.5  		else if (strcmp(lval, "alias_file") == 0) {
     4.6  			conf.alias_file = g_strdup(rval);
     4.7 +		} else if (strcmp(lval, "globalias_file") == 0) {
     4.8 +			conf.globalias_file = g_strdup(rval);
     4.9  		} else if (strcmp(lval, "caseless_matching") == 0) {
    4.10  			conf.localpartcmp = parse_boolean(rval) ? strcasecmp : strcmp;
    4.11  		} else if (strcmp(lval, "mbox_default") == 0) {
     5.1 --- a/src/deliver.c	Sat Feb 18 11:43:06 2012 +0100
     5.2 +++ b/src/deliver.c	Sat Feb 18 12:35:12 2012 +0100
     5.3 @@ -774,6 +774,7 @@
     5.4  	GList *remote_msgout_list = NULL;
     5.5  	GList *msgout_node;
     5.6  	GList *alias_table = NULL;
     5.7 +	GList *globalias_table = NULL;
     5.8  	gboolean ok = TRUE;
     5.9  
    5.10  	/* create msgout_list */
    5.11 @@ -782,6 +783,9 @@
    5.12  		msgout_list = g_list_append(msgout_list, create_msg_out(msg));
    5.13  	}
    5.14  
    5.15 +	if (conf.globalias_file) {
    5.16 +		globalias_table = table_read(conf.globalias_file, ':');
    5.17 +	}
    5.18  	if (conf.alias_file) {
    5.19  		alias_table = table_read(conf.alias_file, ':');
    5.20  	}
    5.21 @@ -808,9 +812,19 @@
    5.22  				logwrite(LOG_ALERT, "invalid log_user address `%s', ignoring\n", conf.log_user);
    5.23  			}
    5.24  		}
    5.25 +		if (globalias_table) {
    5.26 +			GList *globaliased_rcpt_list;
    5.27 +			globaliased_rcpt_list = alias_expand(globalias_table,
    5.28 +					rcpt_list,
    5.29 +					msgout->msg->non_rcpt_list, 1);
    5.30 +			g_list_free(rcpt_list);
    5.31 +			rcpt_list = globaliased_rcpt_list;
    5.32 +		}
    5.33  		if (alias_table) {
    5.34  			GList *aliased_rcpt_list;
    5.35 -			aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list);
    5.36 +			aliased_rcpt_list = alias_expand(alias_table,
    5.37 +					rcpt_list,
    5.38 +					msgout->msg->non_rcpt_list, 0);
    5.39  			g_list_free(rcpt_list);
    5.40  			rcpt_list = aliased_rcpt_list;
    5.41  		}
    5.42 @@ -838,6 +852,9 @@
    5.43  	if (alias_table) {
    5.44  		destroy_table(alias_table);
    5.45  	}
    5.46 +	if (globalias_table) {
    5.47 +		destroy_table(globalias_table);
    5.48 +	}
    5.49  
    5.50  	/* process local/remote msgout lists -> delivery */
    5.51  
     6.1 --- a/src/masqmail.h	Sat Feb 18 11:43:06 2012 +0100
     6.2 +++ b/src/masqmail.h	Sat Feb 18 12:35:12 2012 +0100
     6.3 @@ -165,6 +165,7 @@
     6.4  
     6.5  	gchar *alias_file;
     6.6  	int (*localpartcmp) (const char *, const char *);
     6.7 +	gchar *globalias_file;
     6.8  
     6.9  	GList *perma_routes;
    6.10  	GList *query_routes;  /* list of pairs which point to lists */
    6.11 @@ -331,7 +332,8 @@
    6.12  
    6.13  /* alias.c*/
    6.14  gboolean addr_is_local(address *addr);
    6.15 -GList *alias_expand(GList *alias_table, GList *rcpt_list, GList *non_rcpt_list);
    6.16 +GList *alias_expand(GList *alias_table, GList *rcpt_list, GList *non_rcpt_list,
    6.17 +		int doglob);
    6.18  
    6.19  /* child.c */
    6.20  int child(const char *command);