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);