xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/mail_addr_crunch.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
1 /*	$NetBSD: mail_addr_crunch.c,v 1.3 2020/03/18 19:05:16 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mail_addr_crunch 3
6 /* SUMMARY
7 /*	parse and canonicalize addresses, apply address extension
8 /* SYNOPSIS
9 /*	#include <mail_addr_crunch.h>
10 /*
11 /*	ARGV	*mail_addr_crunch_ext_to_int(string, extension)
12 /*	const char *string;
13 /*	const char *extension;
14 /*
15 /*	ARGV	*mail_addr_crunch_opt(string, extension, in_form, out_form)
16 /*	const char *string;
17 /*	const char *extension;
18 /*	int	in_form;
19 /*	int	out_form;
20 /* DESCRIPTION
21 /*	mail_addr_crunch_*() parses a string with zero or more addresses,
22 /*	rewrites each address to canonical form, and optionally applies
23 /*	an address extension to each resulting address. The caller is
24 /*	expected to pass the result to argv_free().
25 /*
26 /*	With mail_addr_crunch_ext_to_int(), the string is in external
27 /*	form, and the result is in internal form. This API minimizes
28 /*	the number of conversions between internal and external forms.
29 /*
30 /*	mail_addr_crunch_opt() gives more control, at the cost of
31 /*	additional conversions between internal and external forms.
32 /*
33 /*	Arguments:
34 /* .IP string
35 /*	A string with zero or more addresses in external (quoted)
36 /*	form, or in the form specified with the in_form argument.
37 /* .IP extension
38 /*	A null pointer, or an address extension (including the recipient
39 /*	address delimiter) that is propagated to all result addresses.
40 /*	This is in internal (unquoted) form.
41 /* .IP in_form
42 /* .IP out_form
43 /*	Input and output address forms, either MA_FORM_INTERNAL
44 /*	(unquoted form) or MA_FORM_EXTERNAL (quoted form).
45 /* DIAGNOSTICS
46 /*	Fatal error: out of memory.
47 /* SEE ALSO
48 /*	tok822_parse(3), address parser
49 /*	canon_addr(3), address canonicalization
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /*	The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /*	Wietse Venema
56 /*	IBM T.J. Watson Research
57 /*	P.O. Box 704
58 /*	Yorktown Heights, NY 10598, USA
59 /*
60 /*	Wietse Venema
61 /*	Google, Inc.
62 /*	111 8th Avenue
63 /*	New York, NY 10011, USA
64 /*--*/
65 
66 /* System library. */
67 
68 #include <sys_defs.h>
69 #include <string.h>
70 
71 /* Utility library. */
72 
73 #include <mymalloc.h>
74 #include <argv.h>
75 #include <vstring.h>
76 
77 /* Global library. */
78 
79 #include <tok822.h>
80 #include <canon_addr.h>
81 #include <quote_822_local.h>
82 #include <mail_addr_crunch.h>
83 
84 /* mail_addr_crunch - break string into addresses, optionally add extension */
85 
mail_addr_crunch_opt(const char * string,const char * extension,int in_form,int out_form)86 ARGV   *mail_addr_crunch_opt(const char *string, const char *extension,
87 			             int in_form, int out_form)
88 {
89     VSTRING *intern_addr = vstring_alloc(100);
90     VSTRING *extern_addr = vstring_alloc(100);
91     VSTRING *canon_addr = vstring_alloc(100);
92     ARGV   *argv = argv_alloc(1);
93     TOK822 *tree;
94     TOK822 **addr_list;
95     TOK822 **tpp;
96     char   *ratsign;
97     ssize_t extlen;
98 
99     if (extension)
100 	extlen = strlen(extension);
101 
102 #define STR(x) vstring_str(x)
103 
104     /*
105      * Optionally convert input from internal form.
106      */
107     if (in_form == MA_FORM_INTERNAL) {
108 	quote_822_local(extern_addr, string);
109 	string = STR(extern_addr);
110     }
111 
112     /*
113      * Parse the string, rewrite each address to canonical form, and convert
114      * the result to external (quoted) form. Optionally apply the extension
115      * to each address found.
116      *
117      * XXX Workaround for the null address. This works for envelopes but
118      * produces ugly results for message headers.
119      */
120     if (*string == 0 || strcmp(string, "<>") == 0)
121 	string = "\"\"";
122     tree = tok822_parse(string);
123     /* string->extern_addr would be invalidated by tok822_externalize() */
124     string = 0;
125     addr_list = tok822_grep(tree, TOK822_ADDR);
126     for (tpp = addr_list; *tpp; tpp++) {
127 	tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL);
128 	canon_addr_external(canon_addr, STR(extern_addr));
129 	unquote_822_local(intern_addr, STR(canon_addr));
130 	if (extension) {
131 	    VSTRING_SPACE(intern_addr, extlen + 1);
132 	    if ((ratsign = strrchr(STR(intern_addr), '@')) == 0) {
133 		vstring_strcat(intern_addr, extension);
134 	    } else {
135 		memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1);
136 		memcpy(ratsign, extension, extlen);
137 		VSTRING_SKIP(intern_addr);
138 	    }
139 	}
140 	/* Optionally convert output to external form. */
141 	if (out_form == MA_FORM_EXTERNAL) {
142 	    quote_822_local(extern_addr, STR(intern_addr));
143 	    argv_add(argv, STR(extern_addr), ARGV_END);
144 	} else {
145 	    argv_add(argv, STR(intern_addr), ARGV_END);
146 	}
147     }
148     argv_terminate(argv);
149     myfree((void *) addr_list);
150     tok822_free_tree(tree);
151     vstring_free(canon_addr);
152     vstring_free(extern_addr);
153     vstring_free(intern_addr);
154     return (argv);
155 }
156 
157 #ifdef TEST
158 
159  /*
160   * Stand-alone test program, sort of interactive.
161   */
162 #include <stdlib.h>
163 #include <unistd.h>
164 #include <msg.h>
165 #include <vstream.h>
166 #include <vstring_vstream.h>
167 #include <mail_conf.h>
168 #include <mail_params.h>
169 
170 /* canon_addr_external - surrogate to avoid trivial-rewrite dependency */
171 
canon_addr_external(VSTRING * result,const char * addr)172 VSTRING *canon_addr_external(VSTRING *result, const char *addr)
173 {
174     return (vstring_strcpy(result, addr));
175 }
176 
get_addr_form(const char * prompt,VSTRING * buf)177 static int get_addr_form(const char *prompt, VSTRING *buf)
178 {
179     int     addr_form;
180 
181     if (prompt) {
182 	vstream_printf("%s: ", prompt);
183 	vstream_fflush(VSTREAM_OUT);
184     }
185     if (vstring_get_nonl(buf, VSTREAM_IN) == VSTREAM_EOF)
186 	exit(0);
187     if ((addr_form = mail_addr_form_from_string(STR(buf))) < 0)
188 	msg_fatal("bad address form: %s", STR(buf));
189     return (addr_form);
190 }
191 
main(int unused_argc,char ** unused_argv)192 int     main(int unused_argc, char **unused_argv)
193 {
194     VSTRING *extension = vstring_alloc(1);
195     VSTRING *buf = vstring_alloc(1);
196     ARGV   *argv;
197     char  **cpp;
198     int     do_prompt = isatty(0);
199     int     in_form;
200     int     out_form;
201 
202     mail_conf_read();
203     if (chdir(var_queue_dir) < 0)
204 	msg_fatal("chdir %s: %m", var_queue_dir);
205 
206     in_form = get_addr_form(do_prompt ? "input form" : 0, buf);
207     out_form = get_addr_form(do_prompt ? "output form" : 0, buf);
208     if (do_prompt) {
209 	vstream_printf("extension: (CR for none): ");
210 	vstream_fflush(VSTREAM_OUT);
211     }
212     if (vstring_get_nonl(extension, VSTREAM_IN) == VSTREAM_EOF)
213 	exit(0);
214 
215     if (do_prompt) {
216 	vstream_printf("print strings to be translated, one per line\n");
217 	vstream_fflush(VSTREAM_OUT);
218     }
219     while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
220 	argv = mail_addr_crunch_opt(STR(buf), (VSTRING_LEN(extension) ?
221 					       STR(extension) : 0),
222 				    in_form, out_form);
223 	for (cpp = argv->argv; *cpp; cpp++)
224 	    vstream_printf("|%s|\n", *cpp);
225 	vstream_fflush(VSTREAM_OUT);
226 	argv_free(argv);
227     }
228     vstring_free(extension);
229     vstring_free(buf);
230     return (0);
231 }
232 
233 #endif
234