1/*++
2/* NAME
3/*	cleanup_map1n 3
4/* SUMMARY
5/*	one-to-many address mapping
6/* SYNOPSIS
7/*	#include <cleanup.h>
8/*
9/*	ARGV	*cleanup_map1n_internal(state, addr, maps, propagate)
10/*	CLEANUP_STATE *state;
11/*	const char *addr;
12/*	MAPS	*maps;
13/*	int	propagate;
14/* DESCRIPTION
15/*	This module implements one-to-many table mapping via table lookup.
16/*	Table lookups are done with quoted (externalized) address forms.
17/*	The process is recursive. The recursion terminates when the
18/*	left-hand side appears in its own expansion.
19/*
20/*	cleanup_map1n_internal() is the interface for addresses in
21/*	internal (unquoted) form.
22/* DIAGNOSTICS
23/*	When the maximal expansion or recursion limit is reached,
24/*	the alias is not expanded and the CLEANUP_STAT_DEFER error
25/*	is raised with reason "4.6.0 Alias expansion error".
26/*
27/*	When table lookup fails, the alias is not expanded and the
28/*	CLEANUP_STAT_WRITE error is raised with reason "4.6.0 Alias
29/*	expansion error".
30/* SEE ALSO
31/*	mail_addr_map(3) address mappings
32/*	mail_addr_find(3) address lookups
33/* LICENSE
34/* .ad
35/* .fi
36/*	The Secure Mailer license must be distributed with this software.
37/* AUTHOR(S)
38/*	Wietse Venema
39/*	IBM T.J. Watson Research
40/*	P.O. Box 704
41/*	Yorktown Heights, NY 10598, USA
42/*--*/
43
44/* System library. */
45
46#include <sys_defs.h>
47#include <string.h>
48
49#ifdef STRCASECMP_IN_STRINGS_H
50#include <strings.h>
51#endif
52
53/* Utility library. */
54
55#include <mymalloc.h>
56#include <msg.h>
57#include <argv.h>
58#include <vstring.h>
59#include <dict.h>
60
61/* Global library. */
62
63#include <mail_params.h>
64#include <mail_addr_map.h>
65#include <cleanup_user.h>
66#include <quote_822_local.h>
67#include <been_here.h>
68
69/* Application-specific. */
70
71#include "cleanup.h"
72
73/* cleanup_map1n_internal - one-to-many table lookups */
74
75ARGV   *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr,
76			               MAPS *maps, int propagate)
77{
78    ARGV   *argv;
79    ARGV   *lookup;
80    int     count;
81    int     i;
82    int     arg;
83    BH_TABLE *been_here;
84    char   *saved_lhs;
85
86    /*
87     * Initialize.
88     */
89    argv = argv_alloc(1);
90    argv_add(argv, addr, ARGV_END);
91    argv_terminate(argv);
92    been_here = been_here_init(0, BH_FLAG_FOLD);
93
94    /*
95     * Rewrite the address vector in place. With each map lookup result,
96     * split it into separate addresses, then rewrite and flatten each
97     * address, and repeat the process. Beware: argv is being changed, so we
98     * must index the array explicitly, instead of running along it with a
99     * pointer.
100     */
101#define UPDATE(ptr,new)	do { \
102	if (ptr) myfree(ptr); ptr = mystrdup(new); \
103    } while (0)
104#define STR	vstring_str
105#define RETURN(x) do { \
106	been_here_free(been_here); return (x); \
107    } while (0)
108#define UNEXPAND(argv, addr) do { \
109	argv_truncate((argv), 0); argv_add((argv), (addr), (char *) 0); \
110    } while (0)
111
112    for (arg = 0; arg < argv->argc; arg++) {
113	if (argv->argc > var_virt_expan_limit) {
114	    msg_warn("%s: unreasonable %s map expansion size for %s -- "
115		     "message not accepted, try again later",
116		     state->queue_id, maps->title, addr);
117	    state->errs |= CLEANUP_STAT_DEFER;
118	    UPDATE(state->reason, "4.6.0 Alias expansion error");
119	    UNEXPAND(argv, addr);
120	    RETURN(argv);
121	}
122	for (count = 0; /* void */ ; count++) {
123
124	    /*
125	     * Don't expand an address that already expanded into itself.
126	     */
127	    if (been_here_check_fixed(been_here, argv->argv[arg]) != 0)
128		break;
129	    if (count >= var_virt_recur_limit) {
130		msg_warn("%s: unreasonable %s map nesting for %s -- "
131			 "message not accepted, try again later",
132			 state->queue_id, maps->title, addr);
133		state->errs |= CLEANUP_STAT_DEFER;
134		UPDATE(state->reason, "4.6.0 Alias expansion error");
135		UNEXPAND(argv, addr);
136		RETURN(argv);
137	    }
138	    quote_822_local(state->temp1, argv->argv[arg]);
139	    if ((lookup = mail_addr_map(maps, STR(state->temp1), propagate)) != 0) {
140		saved_lhs = mystrdup(argv->argv[arg]);
141		for (i = 0; i < lookup->argc; i++) {
142		    unquote_822_local(state->temp1, lookup->argv[i]);
143		    if (i == 0) {
144			UPDATE(argv->argv[arg], STR(state->temp1));
145		    } else {
146			argv_add(argv, STR(state->temp1), ARGV_END);
147			argv_terminate(argv);
148		    }
149
150		    /*
151		     * Allow an address to expand into itself once.
152		     */
153		    if (strcasecmp(saved_lhs, STR(state->temp1)) == 0)
154			been_here_fixed(been_here, saved_lhs);
155		}
156		myfree(saved_lhs);
157		argv_free(lookup);
158	    } else if (maps->error != 0) {
159		msg_warn("%s: %s map lookup problem for %s -- "
160			 "message not accepted, try again later",
161			 state->queue_id, maps->title, addr);
162		state->errs |= CLEANUP_STAT_WRITE;
163		UPDATE(state->reason, "4.6.0 Alias expansion error");
164		UNEXPAND(argv, addr);
165		RETURN(argv);
166	    } else {
167		break;
168	    }
169	}
170    }
171    RETURN(argv);
172}
173