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  	}