masqmail
diff src/alias.c @ 239:31ee44f45787
refactored alias.c heavily
especially substituted the loop-based alias_expand()
with a recursive approach. Now alias_expand() wraps
alias_one() which recursively expands aliases.
In principle the ``data processing'' is the same but
now it's clearer structured and thus easier to understand
IMO.
The loop might have been faster but I don't care for
speed -- the most simple solution is the best. It's fast
enough, that is sufficient.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Mon, 25 Oct 2010 15:35:28 -0300 |
parents | 3f33a0feeeb0 |
children | bc9d9cd9ee8e |
line diff
1.1 --- a/src/alias.c Mon Oct 25 14:02:18 2010 -0300 1.2 +++ b/src/alias.c Mon Oct 25 15:35:28 2010 -0300 1.3 @@ -1,5 +1,6 @@ 1.4 /* MasqMail 1.5 Copyright (C) 2000-2001 Oliver Kurth 1.6 + Copyright (C) 2010 markus schnalke <meillo@marmaro.de> 1.7 1.8 This program is free software; you can redistribute it and/or modify 1.9 it under the terms of the GNU General Public License as published by 1.10 @@ -26,22 +27,28 @@ 1.11 GList *addr_node; 1.12 address *a; 1.13 1.14 + if (addr->domain == NULL) { 1.15 + return TRUE; 1.16 + } 1.17 foreach(conf.local_hosts, dom_node) { 1.18 - if (addr->domain == NULL) 1.19 - return TRUE; 1.20 - if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD) == 0) { 1.21 - foreach(conf.not_local_addresses, addr_node) { 1.22 - a = create_address_qualified(addr_node->data, TRUE, conf.host_name); 1.23 - DEBUG(6) debugf("not_local_addresses: addr_node->data=%s a->address=%s\n", 1.24 - addr_node->data, a->address); 1.25 - if (addr_isequal(a, addr)) { 1.26 - destroy_address(a); 1.27 - return FALSE; 1.28 - } 1.29 + /* Note: FNM_CASEFOLD is a GNU extension */ 1.30 + if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD) != 0) { 1.31 + /* no match, try next */ 1.32 + continue; 1.33 + } 1.34 + foreach(conf.not_local_addresses, addr_node) { 1.35 + a = create_address_qualified(addr_node->data, TRUE, conf.host_name); 1.36 + DEBUG(6) debugf("not_local_addresses: addr_node->data=%s a->address=%s\n", 1.37 + addr_node->data, a->address); 1.38 + if (addr_isequal(a, addr)) { 1.39 destroy_address(a); 1.40 + /* in local_hosts but also in not_local_addresses */ 1.41 + return FALSE; 1.42 } 1.43 - return TRUE; 1.44 + destroy_address(a); 1.45 } 1.46 + /* in local_hosts */ 1.47 + return TRUE; 1.48 } 1.49 foreach(conf.local_addresses, addr_node) { 1.50 a = create_address_qualified(addr_node->data, TRUE, conf.host_name); 1.51 @@ -49,6 +56,7 @@ 1.52 addr_node->data, a->address); 1.53 if (addr_isequal(a, addr)) { 1.54 destroy_address(a); 1.55 + /* in local_addresses */ 1.56 return TRUE; 1.57 } 1.58 destroy_address(a); 1.59 @@ -75,14 +83,14 @@ 1.60 q = buf; 1.61 while (isspace(*p)) 1.62 p++; 1.63 - if (*p != '\"') { 1.64 + if (*p != '"') { 1.65 while (*p && (*p != ',') && (q < buf + 255)) 1.66 *(q++) = *(p++); 1.67 *q = '\0'; 1.68 } else { 1.69 gboolean escape = FALSE; 1.70 p++; 1.71 - while (*p && (*p != '\"' || escape) && (q < buf + 255)) { 1.72 + while (*p && (*p != '"' || escape) && (q < buf + 255)) { 1.73 if ((*p == '\\') && !escape) 1.74 escape = TRUE; 1.75 else { 1.76 @@ -102,99 +110,154 @@ 1.77 return list; 1.78 } 1.79 1.80 +/* 1.81 +addr is assumed to be local and no pipe address nor not-to-expand 1.82 +*/ 1.83 +static GList* 1.84 +expand_one(GList* alias_table, address* addr) 1.85 +{ 1.86 + GList *val_list; 1.87 + GList *val_node; 1.88 + GList *alias_list = NULL; 1.89 + GList *alias_node; 1.90 + gchar *val; 1.91 + address* alias_addr; 1.92 + 1.93 + /* expand the local alias */ 1.94 + DEBUG(6) debugf("alias: '%s' is local and will get expanded\n", addr->local_part); 1.95 + 1.96 + if (strcasecmp(addr->local_part, "postmaster") == 0) { 1.97 + /* postmaster must always be matched caseless 1.98 + see RFC 822 and RFC 5321 */ 1.99 + val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp); 1.100 + } else { 1.101 + val = (gchar *) table_find_func(alias_table, addr->local_part, conf.alias_local_cmp); 1.102 + } 1.103 + if (!val) { 1.104 + DEBUG(5) debugf("alias: '%s' is fully expanded, hence completed\n", 1.105 + addr->local_part); 1.106 + return g_list_append(NULL, addr); 1.107 + } 1.108 + 1.109 + DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val); 1.110 + val_list = parse_list(val); 1.111 + alias_list = NULL; 1.112 + 1.113 + foreach(val_list, val_node) { 1.114 + gchar *val = (gchar *) (val_node->data); 1.115 + address *alias_addr; 1.116 + address *addr_parent = NULL; 1.117 + 1.118 + DEBUG(6) debugf("alias: processing '%s'\n", val); 1.119 + 1.120 + if (val[0] == '\\') { 1.121 + DEBUG(5) debugf("alias: '%s' is marked as final, hence completed\n", val); 1.122 + alias_addr = create_address_qualified(val+1, TRUE, conf.host_name); 1.123 + g_free(val); 1.124 + DEBUG(6) debugf("alias: address generated: '%s'\n", 1.125 + alias_addr->local_part); 1.126 + alias_list = g_list_append(alias_list, alias_addr); 1.127 + continue; 1.128 + } 1.129 + 1.130 + if (val[0] == '|') { 1.131 + DEBUG(5) debugf("alias: '%s' is a pipe address\n", val); 1.132 + alias_addr = create_address_pipe(val+1); 1.133 + g_free(val); 1.134 + DEBUG(6) debugf("alias: pipe generated: %s\n", 1.135 + alias_addr->local_part); 1.136 + alias_list = g_list_append(alias_list, alias_addr); 1.137 + continue; 1.138 + } 1.139 + 1.140 + alias_addr = create_address_qualified(val, TRUE, conf.host_name); 1.141 + g_free(val); 1.142 + 1.143 + if (!addr_is_local(alias_addr)) { 1.144 + DEBUG(5) debugf("alias: '%s@%s' is non-local, hence completed\n", 1.145 + alias_addr->local_part, alias_addr->domain); 1.146 + alias_list = g_list_append(alias_list, alias_addr); 1.147 + continue; 1.148 + } 1.149 + 1.150 + /* addr is local, so let's go into recursion and expand again, 1.151 + but first ... search in parents for loops: */ 1.152 + for (addr_parent=addr; addr_parent; addr_parent=addr_parent->parent) { 1.153 + if (addr_isequal_alias(alias_addr, addr_parent)) { 1.154 + break; 1.155 + } 1.156 + } 1.157 + if (addr_parent) { 1.158 + /* loop detected, ignore this path */ 1.159 + logwrite(LOG_ALERT, 1.160 + "alias: detected loop (%s -> %s), hence ignoring\n", 1.161 + addr_parent->local_part, addr->local_part); 1.162 + continue; 1.163 + } 1.164 + alias_addr->parent = addr; 1.165 + 1.166 + /* recurse */ 1.167 + DEBUG(6) debugf("alias: >>\n"); 1.168 + alias_node = expand_one(alias_table, alias_addr); 1.169 + DEBUG(6) debugf("alias: <<\n"); 1.170 + if (alias_node) { 1.171 + alias_list = g_list_concat(alias_list, alias_node); 1.172 + } 1.173 + } 1.174 + g_list_free(val_list); 1.175 + addr->children = g_list_copy(alias_list); 1.176 + 1.177 + return alias_list; 1.178 +} 1.179 + 1.180 GList* 1.181 -alias_expand(GList * alias_table, GList * rcpt_list, GList * non_rcpt_list) 1.182 +alias_expand(GList* alias_table, GList* rcpt_list, GList* non_rcpt_list) 1.183 { 1.184 + GList *rcpt_node = NULL; 1.185 + GList *alias_list = NULL; 1.186 GList *done_list = NULL; 1.187 - GList *rcpt_node = g_list_copy(rcpt_list); 1.188 + GList *rcpt_node_next = NULL; 1.189 + address *addr = NULL; 1.190 1.191 - while (rcpt_node != NULL) { 1.192 - address *addr = (address *) (rcpt_node->data); 1.193 - DEBUG(5) debugf("alias_expand begin: '%s@%s'\n", addr->local_part, addr->domain); 1.194 - /* if(addr_is_local(addr) && (addr->local_part[0] != '|') && */ 1.195 - if (addr_is_local(addr) && !(addr->flags & ADDR_FLAG_NOEXPAND)) { 1.196 - gchar *val; 1.197 + for (rcpt_node=g_list_copy(rcpt_list); 1.198 + rcpt_node; 1.199 + rcpt_node=g_list_next(rcpt_node)) { 1.200 1.201 - DEBUG(5) debugf("alias: '%s' is local\n", addr->local_part); 1.202 - if (strcasecmp(addr->local_part, "postmaster") == 0) 1.203 - /* postmaster needs always to be matched caseless 1.204 - see RFC 822 and RFC 5321 */ 1.205 - val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp); 1.206 - else 1.207 - val = (gchar *) table_find_func(alias_table, addr->local_part, conf.alias_local_cmp); 1.208 - 1.209 - if (val != NULL) { 1.210 - GList *val_list = parse_list(val); 1.211 - GList *val_node; 1.212 - GList *alias_list = NULL; 1.213 - 1.214 - DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val); 1.215 - foreach(val_list, val_node) { 1.216 - gchar *val = (gchar *) (val_node->data); 1.217 - address *alias_addr; 1.218 - address *addr_parent = NULL; 1.219 - 1.220 - if (val[0] == '|') { 1.221 - DEBUG(5) debugf("alias: %s is a pipe address\n", val); 1.222 - alias_addr = create_address_pipe(val); 1.223 - DEBUG(5) debugf("alias_pipe: %s is a pipe address\n", alias_addr->local_part); 1.224 - } else if (val[0] == '\\') { 1.225 - DEBUG(5) debugf("alias: shall not be expanded: '%s'\n", val); 1.226 - alias_addr = create_address_qualified(&(val[1]), TRUE, conf.host_name); 1.227 - alias_addr->flags |= ADDR_FLAG_NOEXPAND; 1.228 - DEBUG(5) debugf("alias: not expanded: '%s'\n", alias_addr->local_part); 1.229 - } else { 1.230 - alias_addr = create_address_qualified(val, TRUE, conf.host_name); 1.231 - 1.232 - /* search in parents for loops: */ 1.233 - for (addr_parent = addr; addr_parent; addr_parent = addr_parent->parent) { 1.234 - if (addr_isequal_alias (alias_addr, addr_parent)) { 1.235 - logwrite(LOG_ALERT, 1.236 - "detected alias loop, (ignoring): %s@%s -> %s@%s\n", 1.237 - addr_parent->local_part, 1.238 - addr_parent->domain, 1.239 - addr->local_part, addr->domain); 1.240 - break; 1.241 - } 1.242 - } 1.243 - } 1.244 - if (!addr_parent) { 1.245 - alias_list = g_list_append(alias_list, alias_addr); 1.246 - alias_addr->parent = addr; 1.247 - } 1.248 - g_free(val); 1.249 - } 1.250 - g_list_free(val_list); 1.251 - addr->children = g_list_copy(alias_list); 1.252 - rcpt_node = g_list_concat(rcpt_node, alias_list); 1.253 - } else { 1.254 - DEBUG(5) debugf("alias: '%s' is completed\n", addr->local_part); 1.255 - done_list = g_list_append(done_list, addr); 1.256 + addr = (address *) (rcpt_node->data); 1.257 + if (addr_is_local(addr)) { 1.258 + DEBUG(5) debugf("alias: (orig rcpt addr) expand local '%s'\n", 1.259 + addr->local_part); 1.260 + alias_list = expand_one(alias_table, addr); 1.261 + if (alias_list) { 1.262 + done_list = g_list_concat(done_list, alias_list); 1.263 } 1.264 } else { 1.265 - DEBUG(5) debugf("alias: '%s@%s' is not local\n", addr->local_part, addr->domain); 1.266 + DEBUG(5) debugf("alias: (orig rcpt addr) don't expand non-local '%s@%s'\n", 1.267 + addr->local_part, addr->domain); 1.268 done_list = g_list_append(done_list, addr); 1.269 } 1.270 - rcpt_node = g_list_next(rcpt_node); 1.271 } 1.272 1.273 - /* delete addresses from done_list if they are in the non_rcpt_list */ 1.274 - if (non_rcpt_list) { 1.275 - GList *rcpt_node_next; 1.276 - for (rcpt_node = g_list_first(done_list); rcpt_node; rcpt_node = rcpt_node_next) { 1.277 - address *addr = (address *) (rcpt_node->data); 1.278 - GList *non_node; 1.279 + /* we're done if we don't have to remove rcpts */ 1.280 + if (!non_rcpt_list) { 1.281 + return done_list; 1.282 + } 1.283 1.284 - rcpt_node_next = g_list_next(rcpt_node); 1.285 + /* delete addresses of non_rcpt_list from done_list */ 1.286 + for (rcpt_node = g_list_first(done_list); rcpt_node; rcpt_node = rcpt_node_next) { 1.287 + address *addr = (address *) (rcpt_node->data); 1.288 + GList *non_node; 1.289 1.290 - foreach(non_rcpt_list, non_node) { 1.291 - address *non_addr = (address *) (non_node->data); 1.292 - if (addr_isequal(addr, non_addr)) { 1.293 - done_list = g_list_remove_link(done_list, rcpt_node); 1.294 - g_list_free_1(rcpt_node); 1.295 - addr_mark_delivered(addr); /* this address is still in the children lists of the original address */ 1.296 - break; 1.297 - } 1.298 + rcpt_node_next = g_list_next(rcpt_node); 1.299 + foreach(non_rcpt_list, non_node) { 1.300 + address *non_addr = (address *) (non_node->data); 1.301 + if (addr_isequal(addr, non_addr)) { 1.302 + done_list = g_list_remove_link(done_list, rcpt_node); 1.303 + g_list_free_1(rcpt_node); 1.304 + /* this address is still in the children lists 1.305 + of the original address, simply mark them delivered */ 1.306 + addr_mark_delivered(addr); 1.307 + break; 1.308 } 1.309 } 1.310 }