text: /*	$OpenBSD: aliases.c,v 1.79 2021/06/14 17:58:15 eric Exp $	*/
  
  /*
   * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
   *
   * Permission to use, copy, modify, and distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include "includes.h"
  
  #include <sys/types.h>
  #include <sys/queue.h>
  #include <sys/tree.h>
  #include <sys/socket.h>
  
  #include <ctype.h>
  #include <errno.h>
  #include <event.h>
  #include <imsg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <limits.h>
  #ifdef HAVE_UTIL_H
  #include <util.h>
  #endif
  #ifdef HAVE_LIBUTIL_H
  #include <libutil.h>
  #endif
  
  #include "smtpd.h"
  #include "log.h"
  
  static int aliases_expand_include(struct expand *, const char *);
  
  int
  aliases_get(struct expand *expand, const char *username)
  {
  	struct expandnode      *xn;
  	char			buf[SMTPD_MAXLOCALPARTSIZE];
  	size_t			nbaliases;
  	int			ret;
  	union lookup		lk;
  	struct dispatcher      *dsp;
  	struct table	       *mapping = NULL;
  	char		       *pbuf;
  
  	dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher);
  	mapping = table_find(env, dsp->u.local.table_alias);
  
  	xlowercase(buf, username, sizeof(buf));
  
  	/* first, check if entry has a user-part tag */
  	pbuf = strchr(buf, *env->sc_subaddressing_delim);
  	if (pbuf) {
  		ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  		if (ret < 0)
  			return (-1);
  		if (ret)
  			goto expand;
  		*pbuf = '\0';
  	}
  
  	/* no user-part tag, try looking up user */
  	ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  	if (ret <= 0)
  		return ret;
  
  expand:
  	/* foreach node in table_alias expandtree, we merge */
  	nbaliases = 0;
  	RB_FOREACH(xn, expandtree, &lk.expand->tree) {
  		if (xn->type == EXPAND_INCLUDE)
  			nbaliases += aliases_expand_include(expand,
  			    xn->u.buffer);
  		else {
  			expand_insert(expand, xn);
  			nbaliases++;
  		}
  	}
  
  	expand_free(lk.expand);
  
  	log_debug("debug: aliases_get: returned %zd aliases", nbaliases);
  	return nbaliases;
  }
  
  int
  aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr)
  {
  	struct expandnode      *xn;
  	union lookup		lk;
  	char			buf[LINE_MAX];
  	char			user[LINE_MAX];
  	char			tag[LINE_MAX];
  	char			domain[LINE_MAX];
  	char		       *pbuf;
  	int			nbaliases;
  	int			ret;
  	struct dispatcher      *dsp;
  	struct table	       *mapping = NULL;
  
  	dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher);
  	mapping = table_find(env, dsp->u.local.table_virtual);
  
  	if (!bsnprintf(user, sizeof(user), "%s", maddr->user))
  		return 0;
  	if (!bsnprintf(domain, sizeof(domain), "%s", maddr->domain))
  		return 0;
  	xlowercase(user, user, sizeof(user));
  	xlowercase(domain, domain, sizeof(domain));
  
  	memset(tag, '\0', sizeof tag);
  	pbuf = strchr(user, *env->sc_subaddressing_delim);
  	if (pbuf) {
  		if (!bsnprintf(tag, sizeof(tag), "%s", pbuf + 1))
  			return 0;
  		xlowercase(tag, tag, sizeof(tag));
  		*pbuf = '\0';
  	}
  
  	/* first, check if entry has a user-part tag */
  	if (tag[0]) {
  		if (!bsnprintf(buf, sizeof(buf), "%s%c%s@%s",
  			user, *env->sc_subaddressing_delim, tag, domain))
  			return 0;
  		ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  		if (ret < 0)
  			return (-1);
  		if (ret)
  			goto expand;
  	}
  
  	/* then, check if entry exists without user-part tag */
  	if (!bsnprintf(buf, sizeof(buf), "%s@%s", user, domain))
  		return 0;
  	ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  	if (ret < 0)
  		return (-1);
  	if (ret)
  		goto expand;
  
  	if (tag[0]) {
  		/* Failed ? We lookup for username + user-part tag */
  		if (!bsnprintf(buf, sizeof(buf), "%s%c%s",
  			user, *env->sc_subaddressing_delim, tag))
  			return 0;
  		ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  		if (ret < 0)
  			return (-1);
  		if (ret)
  			goto expand;
  	}
  
  	/* Failed ? We lookup for username only */
  	if (!bsnprintf(buf, sizeof(buf), "%s", user))
  		return 0;
  	ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  	if (ret < 0)
  		return (-1);
  	if (ret)
  		goto expand;
  
  	/* Do not try catch-all entries if there is no domain */
  	if (domain[0] == '\0')
  		return 0;
  
  	if (!bsnprintf(buf, sizeof(buf), "@%s", domain))
  		return 0;
  	/* Failed ? We lookup for catch all for virtual domain */
  	ret = table_lookup(mapping, K_ALIAS, buf, &lk);
  	if (ret < 0)
  		return (-1);
  	if (ret)
  		goto expand;
  
  	/* Failed ? We lookup for a *global* catch all */
  	ret = table_lookup(mapping, K_ALIAS, "@", &lk);
  	if (ret <= 0)
  		return (ret);
  
  expand:
  	/* foreach node in table_virtual expand, we merge */
  	nbaliases = 0;
  	RB_FOREACH(xn, expandtree, &lk.expand->tree) {
  		if (xn->type == EXPAND_INCLUDE)
  			nbaliases += aliases_expand_include(expand,
  			    xn->u.buffer);
  		else {
  			expand_insert(expand, xn);
  			nbaliases++;
  		}
  	}
  
  	expand_free(lk.expand);
  
  	log_debug("debug: aliases_virtual_get: '%s' resolved to %d nodes",
  	    buf, nbaliases);
  
  	return nbaliases;
  }
  
  static int
  aliases_expand_include(struct expand *expand, const char *filename)
  {
  	FILE *fp;
  	char *line;
  	size_t len, lineno = 0;
  	char delim[3] = { '\\', '#', '\0' };
  
  	fp = fopen(filename, "r");
  	if (fp == NULL) {
  		log_warn("warn: failed to open include file \"%s\".", filename);
  		return 0;
  	}
  
  	while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
  		expand_line(expand, line, 0);
  		free(line);
  	}
  
  	fclose(fp);
  	return 1;
  }
  