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 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 172 VSTRING *canon_addr_external(VSTRING *result, const char *addr) 173 { 174 return (vstring_strcpy(result, addr)); 175 } 176 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 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