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