view src/alias.c @ 434:f2a7271746d1 default tip

Removes Freshmeat.net from the docs The site, which was later renamed to freecode.com, is no longer maintained (contains only a static copy).
author markus schnalke <meillo@marmaro.de>
date Sat, 07 Feb 2015 11:45:07 +0100
parents ddac877ced95
children
line wrap: on
line source

/*
**  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
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "masqmail.h"
#include <fnmatch.h>

gboolean
addr_is_local(address *addr)
{
	GList *dom_node;
	GList *addr_node;
	address *a;

	if (!addr->domain) {
		return TRUE;
	}
	foreach(conf.local_hosts, dom_node) {
		/* 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, conf.localpartcmp)) {
				/* also in not_local_addresses */
				destroy_address(a);
				return FALSE;
			}
			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);
		DEBUG(6) debugf("local_addresses: addr_node->data=%s "
				"a->address=%s\n",
		                addr_node->data, a->address);
		if (addr_isequal(a, addr, conf.localpartcmp)) {
			/* in local_addresses */
			destroy_address(a);
			return TRUE;
		}
		destroy_address(a);
	}
	return FALSE;
}

static GList*
parse_list(gchar *line)
{
	GList *list = NULL;
	gchar buf[256];
	gchar *p, *q;

	p = line;
	while (*p) {
		q = buf;
		while (isspace(*p)) {
			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)) {
				if ((*p == '\\') && !escape) {
					escape = TRUE;
				} else {
					escape = FALSE;
					*(q++) = *p;
				}
				p++;
			}
			*q = '\0';
			while (*p && (*p != ',')) {
				p++;
			}
		}
		list = g_list_append(list, g_strdup(g_strchomp(buf)));
		if (*p) {
			p++;
		}
	}
	return list;
}

static int
globaliascmp(const char *pattern, const char *addr)
{
	if (conf.localpartcmp == strcasecmp) {
		return fnmatch(pattern, addr, FNM_CASEFOLD);
	} else if (strncasecmp(addr, "postmaster", 10)==0) {
		/* postmaster must always be matched caseless
		** see RFC 822 and RFC 5321 */
		return fnmatch(pattern, addr, FNM_CASEFOLD);
	} else {
		/* case-sensitive */
		return fnmatch(pattern, addr, 0);
	}
}

/*
**  addr is assumed to be local and no pipe address nor not-to-expand
*/
static GList*
expand_one(GList *alias_table, address *addr, int doglob)
{
	GList *val_list;
	GList *val_node;
	GList *alias_list = NULL;
	GList *alias_node;
	gchar *val;
	char addrstr[BUFSIZ];

	if (doglob) {
		snprintf(addrstr, sizeof addrstr, "%s@%s",
				addr->local_part, addr->domain);
	} else {
		snprintf(addrstr, sizeof addrstr, "%s", addr->local_part);
	}

	/* expand the local alias */
	DEBUG(6) debugf("alias: '%s' is local and will get expanded\n",
			addrstr);

	if (doglob) {
		val = (gchar *) table_find_func(alias_table, addrstr,
				globaliascmp);

	} else 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, addrstr,
				strcasecmp);
	} else {
		val = (gchar *) table_find_func(alias_table, addrstr,
				conf.localpartcmp);
	}
	if (!val) {
		DEBUG(5) debugf("alias: '%s' is fully expanded, hence "
				"completed\n", addrstr);
		return g_list_append(NULL, addr);
	}

	DEBUG(5) debugf("alias: '%s' -> '%s'\n", addrstr, val);
	val_list = parse_list(val);
	alias_list = NULL;

	foreach(val_list, val_node) {
		gchar *val = (gchar *) (val_node->data);
		address *alias_addr;
	
		DEBUG(6) debugf("alias: processing '%s'\n", val);

		if (*val == '\\') {
			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->address);
			alias_list = g_list_append(alias_list, alias_addr);
			continue;
		}
	
		if (*val == '|') {
			DEBUG(5) debugf("alias: '%s' is a pipe address\n",
					val);
			alias_addr = create_address_pipe(val);
			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;
		}

		alias_addr = create_address_qualified(val, TRUE,
				conf.host_name);
		g_free(val);

		if (!addr_is_local(alias_addr)) {
			DEBUG(5) debugf("alias: '%s' is non-local, "
					"hence completed\n",
					alias_addr->address);
			alias_list = g_list_append(alias_list, alias_addr);
			continue;
		}

		/* addr is local and to expand at this point */
		/* but first ... search in parents for loops: */
		if (addr_isequal_parent(addr, alias_addr, conf.localpartcmp)) {
			/* loop detected, ignore this path */
			logwrite(LOG_ALERT, "alias: detected loop, "
				"hence ignoring '%s'\n",
				alias_addr->address);
			continue;
		}
		alias_addr->parent = addr;

		/* recurse */
		DEBUG(6) debugf("alias: >>\n");
		alias_node = expand_one(alias_table, alias_addr, doglob);
		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,
		int doglob)
{
	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: expand local '%s' "
					"(orig rcpt addr)\n", addr->address);
			alias_list = expand_one(alias_table, addr, doglob);
			if (alias_list) {
				done_list = g_list_concat(done_list,
						alias_list);
			}
		} else {
			DEBUG(5) debugf("alias: don't expand non-local '%s' "
					"(orig rcpt addr)\n", addr->address);
			done_list = g_list_append(done_list, addr);
		}
	}

	/* we're done if we don't have to remove rcpts */
	if (!non_rcpt_list) {
		return done_list;
	}

	/* 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;

		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, conf.localpartcmp)) {
				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;
			}
		}
	}
	return done_list;
}