changeset 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 (2010-10-25)
parents ec28ce798b79
children 0509d0c933a7
files src/alias.c
diffstat 1 files changed, 157 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/src/alias.c	Mon Oct 25 14:02:18 2010 -0300
+++ b/src/alias.c	Mon Oct 25 15:35:28 2010 -0300
@@ -1,5 +1,6 @@
 /*  MasqMail
     Copyright (C) 2000-2001 Oliver Kurth
+    Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -26,22 +27,28 @@
 	GList *addr_node;
 	address *a;
 
+	if (addr->domain == NULL) {
+		return TRUE;
+	}
 	foreach(conf.local_hosts, dom_node) {
-		if (addr->domain == NULL)
-			return TRUE;
-		if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD) == 0) {
-			foreach(conf.not_local_addresses, addr_node) {
-				a = create_address_qualified(addr_node->data, TRUE, conf.host_name);
-				DEBUG(6) debugf("not_local_addresses: addr_node->data=%s a->address=%s\n",
-				                addr_node->data, a->address);
-				if (addr_isequal(a, addr)) {
-					destroy_address(a);
-					return FALSE;
-				}
+		/* Note: FNM_CASEFOLD is a GNU extension */
+		if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD) != 0) {
+			/* no match, try next */
+			continue;
+		}
+		foreach(conf.not_local_addresses, addr_node) {
+			a = create_address_qualified(addr_node->data, TRUE, conf.host_name);
+			DEBUG(6) debugf("not_local_addresses: addr_node->data=%s a->address=%s\n",
+			                addr_node->data, a->address);
+			if (addr_isequal(a, addr)) {
 				destroy_address(a);
+				/* in local_hosts but also in not_local_addresses */
+				return FALSE;
 			}
-			return TRUE;
+			destroy_address(a);
 		}
+		/* in local_hosts */
+		return TRUE;
 	}
 	foreach(conf.local_addresses, addr_node) {
 		a = create_address_qualified(addr_node->data, TRUE, conf.host_name);
@@ -49,6 +56,7 @@
 		                addr_node->data, a->address);
 		if (addr_isequal(a, addr)) {
 			destroy_address(a);
+			/* in local_addresses */
 			return TRUE;
 		}
 		destroy_address(a);
@@ -75,14 +83,14 @@
 		q = buf;
 		while (isspace(*p))
 			p++;
-		if (*p != '\"') {
+		if (*p != '"') {
 			while (*p && (*p != ',') && (q < buf + 255))
 				*(q++) = *(p++);
 			*q = '\0';
 		} else {
 			gboolean escape = FALSE;
 			p++;
-			while (*p && (*p != '\"' || escape) && (q < buf + 255)) {
+			while (*p && (*p != '"' || escape) && (q < buf + 255)) {
 				if ((*p == '\\') && !escape)
 					escape = TRUE;
 				else {
@@ -102,99 +110,154 @@
 	return list;
 }
 
-GList*
-alias_expand(GList * alias_table, GList * rcpt_list, GList * non_rcpt_list)
+/*
+addr is assumed to be local and no pipe address nor not-to-expand
+*/
+static GList*
+expand_one(GList* alias_table, address* addr)
 {
-	GList *done_list = NULL;
-	GList *rcpt_node = g_list_copy(rcpt_list);
+	GList *val_list;
+	GList *val_node;
+	GList *alias_list = NULL;
+	GList *alias_node;
+	gchar *val;
+	address* alias_addr;
 
-	while (rcpt_node != NULL) {
-		address *addr = (address *) (rcpt_node->data);
-		DEBUG(5) debugf("alias_expand begin: '%s@%s'\n", addr->local_part, addr->domain);
-		/* if(addr_is_local(addr) && (addr->local_part[0] != '|') && */
-		if (addr_is_local(addr) && !(addr->flags & ADDR_FLAG_NOEXPAND)) {
-			gchar *val;
+	/* expand the local alias */
+	DEBUG(6) debugf("alias: '%s' is local and will get expanded\n", addr->local_part);
+
+	if (strcasecmp(addr->local_part, "postmaster") == 0) {
+		/* postmaster must always be matched caseless
+		   see RFC 822 and RFC 5321 */
+		val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp);
+	} else {
+		val = (gchar *) table_find_func(alias_table, addr->local_part, conf.alias_local_cmp);
+	}
+	if (!val) {
+		DEBUG(5) debugf("alias: '%s' is fully expanded, hence completed\n",
+		                addr->local_part);
+		return g_list_append(NULL, addr);
+	}
 
-			DEBUG(5) debugf("alias: '%s' is local\n", addr->local_part);
-			if (strcasecmp(addr->local_part, "postmaster") == 0)
-				/* postmaster needs always to be matched caseless
-				   see RFC 822 and RFC 5321 */
-				val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp);
-			else
-				val = (gchar *) table_find_func(alias_table, addr->local_part, conf.alias_local_cmp);
+	DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val);
+	val_list = parse_list(val);
+	alias_list = NULL;
+
+	foreach(val_list, val_node) {
+		gchar *val = (gchar *) (val_node->data);
+		address *alias_addr;
+		address *addr_parent = NULL;
+	
+		DEBUG(6) debugf("alias: processing '%s'\n", val);
 
-			if (val != NULL) {
-				GList *val_list = parse_list(val);
-				GList *val_node;
-				GList *alias_list = NULL;
-
-				DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val);
-				foreach(val_list, val_node) {
-					gchar *val = (gchar *) (val_node->data);
-					address *alias_addr;
-					address *addr_parent = NULL;
+		if (val[0] == '\\') {
+			DEBUG(5) debugf("alias: '%s' is marked as final, hence completed\n", val);
+			alias_addr = create_address_qualified(val+1, TRUE, conf.host_name);
+			g_free(val);
+			DEBUG(6) debugf("alias:     address generated: '%s'\n",
+			                alias_addr->local_part);
+			alias_list = g_list_append(alias_list, alias_addr);
+			continue;
+		}
+	
+		if (val[0] == '|') {
+			DEBUG(5) debugf("alias: '%s' is a pipe address\n", val);
+			alias_addr = create_address_pipe(val+1);
+			g_free(val);
+			DEBUG(6) debugf("alias:     pipe generated: %s\n",
+			                alias_addr->local_part);
+			alias_list = g_list_append(alias_list, alias_addr);
+			continue;
+		}
 
-					if (val[0] == '|') {
-						DEBUG(5) debugf("alias: %s is a pipe address\n", val);
-						alias_addr = create_address_pipe(val);
-						DEBUG(5) debugf("alias_pipe: %s is a pipe address\n", alias_addr->local_part);
-					} else if (val[0] == '\\') {
-						DEBUG(5) debugf("alias: shall not be expanded: '%s'\n", val);
-						alias_addr = create_address_qualified(&(val[1]), TRUE, conf.host_name);
-						alias_addr->flags |= ADDR_FLAG_NOEXPAND;
-						DEBUG(5) debugf("alias: not expanded: '%s'\n", alias_addr->local_part);
-					} else {
-						alias_addr = create_address_qualified(val, TRUE, conf.host_name);
+		alias_addr = create_address_qualified(val, TRUE, conf.host_name);
+		g_free(val);
+
+		if (!addr_is_local(alias_addr)) {
+			DEBUG(5) debugf("alias: '%s@%s' is non-local, hence completed\n",
+					alias_addr->local_part, alias_addr->domain);
+			alias_list = g_list_append(alias_list, alias_addr);
+			continue;
+		}
+
+		/* addr is local, so let's go into recursion and expand again,
+		   but first ...  search in parents for loops: */
+		for (addr_parent=addr; addr_parent; addr_parent=addr_parent->parent) {
+			if (addr_isequal_alias(alias_addr, addr_parent)) {
+				break;
+			}
+		}
+		if (addr_parent) {
+			/* loop detected, ignore this path */
+			logwrite(LOG_ALERT,
+			         "alias: detected loop (%s -> %s), hence ignoring\n",
+			         addr_parent->local_part, addr->local_part);
+			continue;
+		}
+		alias_addr->parent = addr;
 
-						/* search in parents for loops: */
-						for (addr_parent = addr; addr_parent; addr_parent = addr_parent->parent) {
-							if (addr_isequal_alias (alias_addr, addr_parent)) {
-								logwrite(LOG_ALERT,
-								         "detected alias loop, (ignoring): %s@%s -> %s@%s\n",
-								         addr_parent->local_part,
-								         addr_parent->domain,
-								         addr->local_part, addr->domain);
-								break;
-							}
-						}
-					}
-					if (!addr_parent) {
-						alias_list = g_list_append(alias_list, alias_addr);
-						alias_addr->parent = addr;
-					}
-					g_free(val);
-				}
-				g_list_free(val_list);
-				addr->children = g_list_copy(alias_list);
-				rcpt_node = g_list_concat(rcpt_node, alias_list);
-			} else {
-				DEBUG(5) debugf("alias: '%s' is completed\n", addr->local_part);
-				done_list = g_list_append(done_list, addr);
+		/* recurse */
+		DEBUG(6) debugf("alias: >>\n");
+		alias_node = expand_one(alias_table, alias_addr);
+		DEBUG(6) debugf("alias: <<\n");
+		if (alias_node) {
+			alias_list = g_list_concat(alias_list, alias_node);
+		}
+	}
+	g_list_free(val_list);
+	addr->children = g_list_copy(alias_list);
+
+	return alias_list;
+}
+
+GList*
+alias_expand(GList* alias_table, GList* rcpt_list, GList* non_rcpt_list)
+{
+	GList *rcpt_node = NULL;
+	GList *alias_list = NULL;
+	GList *done_list = NULL;
+	GList *rcpt_node_next = NULL;
+	address *addr = NULL;
+
+	for (rcpt_node=g_list_copy(rcpt_list);
+	     rcpt_node;
+	     rcpt_node=g_list_next(rcpt_node)) {
+
+		addr = (address *) (rcpt_node->data);
+		if (addr_is_local(addr)) {
+			DEBUG(5) debugf("alias: (orig rcpt addr) expand local '%s'\n",
+			                addr->local_part);
+			alias_list = expand_one(alias_table, addr);
+			if (alias_list) {
+				done_list = g_list_concat(done_list, alias_list);
 			}
 		} else {
-			DEBUG(5) debugf("alias: '%s@%s' is not local\n", addr->local_part, addr->domain);
+			DEBUG(5) debugf("alias: (orig rcpt addr) don't expand non-local '%s@%s'\n",
+					addr->local_part, addr->domain);
 			done_list = g_list_append(done_list, addr);
 		}
-		rcpt_node = g_list_next(rcpt_node);
+	}
+
+	/* we're done if we don't have to remove rcpts */
+	if (!non_rcpt_list) {
+		return done_list;
 	}
 
-	/* delete addresses from done_list if they are in the non_rcpt_list */
-	if (non_rcpt_list) {
-		GList *rcpt_node_next;
-		for (rcpt_node = g_list_first(done_list); rcpt_node; rcpt_node = rcpt_node_next) {
-			address *addr = (address *) (rcpt_node->data);
-			GList *non_node;
-
-			rcpt_node_next = g_list_next(rcpt_node);
+	/* delete addresses of non_rcpt_list from done_list */
+	for (rcpt_node = g_list_first(done_list); rcpt_node; rcpt_node = rcpt_node_next) {
+		address *addr = (address *) (rcpt_node->data);
+		GList *non_node;
 
-			foreach(non_rcpt_list, non_node) {
-				address *non_addr = (address *) (non_node->data);
-				if (addr_isequal(addr, non_addr)) {
-					done_list = g_list_remove_link(done_list, rcpt_node);
-					g_list_free_1(rcpt_node);
-					addr_mark_delivered(addr);  /* this address is still in the children lists of the original address */
-					break;
-				}
+		rcpt_node_next = g_list_next(rcpt_node);
+		foreach(non_rcpt_list, non_node) {
+			address *non_addr = (address *) (non_node->data);
+			if (addr_isequal(addr, non_addr)) {
+				done_list = g_list_remove_link(done_list, rcpt_node);
+				g_list_free_1(rcpt_node);
+				/* this address is still in the children lists
+				   of the original address, simply mark them delivered */
+				addr_mark_delivered(addr);
+				break;
 			}
 		}
 	}