summaryrefslogtreecommitdiffstats
path: root/src/global/mail_addr_crunch.c
blob: faaf741468491393fee4a2afad7e4492fb1babf7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*++
/* NAME
/*	mail_addr_crunch 3
/* SUMMARY
/*	parse and canonicalize addresses, apply address extension
/* SYNOPSIS
/*	#include <mail_addr_crunch.h>
/*
/*	ARGV	*mail_addr_crunch_ext_to_int(string, extension)
/*	const char *string;
/*	const char *extension;
/*
/*	ARGV	*mail_addr_crunch_opt(string, extension, in_form, out_form)
/*	const char *string;
/*	const char *extension;
/*	int	in_form;
/*	int	out_form;
/* DESCRIPTION
/*	mail_addr_crunch_*() parses a string with zero or more addresses,
/*	rewrites each address to canonical form, and optionally applies
/*	an address extension to each resulting address. The caller is
/*	expected to pass the result to argv_free().
/*
/*	With mail_addr_crunch_ext_to_int(), the string is in external
/*	form, and the result is in internal form. This API minimizes
/*	the number of conversions between internal and external forms.
/*
/*	mail_addr_crunch_opt() gives more control, at the cost of
/*	additional conversions between internal and external forms.
/*
/*	Arguments:
/* .IP string
/*	A string with zero or more addresses in external (quoted)
/*	form, or in the form specified with the in_form argument.
/* .IP extension
/*	A null pointer, or an address extension (including the recipient
/*	address delimiter) that is propagated to all result addresses.
/*	This is in internal (unquoted) form.
/* .IP in_form
/* .IP out_form
/*	Input and output address forms, either MA_FORM_INTERNAL
/*	(unquoted form) or MA_FORM_EXTERNAL (quoted form).
/* DIAGNOSTICS
/*	Fatal error: out of memory.
/* SEE ALSO
/*	tok822_parse(3), address parser
/*	canon_addr(3), address canonicalization
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <string.h>

/* Utility library. */

#include <mymalloc.h>
#include <argv.h>
#include <vstring.h>

/* Global library. */

#include <tok822.h>
#include <canon_addr.h>
#include <quote_822_local.h>
#include <mail_addr_crunch.h>

/* mail_addr_crunch - break string into addresses, optionally add extension */

ARGV   *mail_addr_crunch_opt(const char *string, const char *extension,
			             int in_form, int out_form)
{
    VSTRING *intern_addr = vstring_alloc(100);
    VSTRING *extern_addr = vstring_alloc(100);
    VSTRING *canon_addr = vstring_alloc(100);
    ARGV   *argv = argv_alloc(1);
    TOK822 *tree;
    TOK822 **addr_list;
    TOK822 **tpp;
    char   *ratsign;
    ssize_t extlen;

    if (extension)
	extlen = strlen(extension);

#define STR(x) vstring_str(x)

    /*
     * Optionally convert input from internal form.
     */
    if (in_form == MA_FORM_INTERNAL) {
	quote_822_local(extern_addr, string);
	string = STR(extern_addr);
    }

    /*
     * Parse the string, rewrite each address to canonical form, and convert
     * the result to external (quoted) form. Optionally apply the extension
     * to each address found.
     * 
     * XXX Workaround for the null address. This works for envelopes but
     * produces ugly results for message headers.
     */
    if (*string == 0 || strcmp(string, "<>") == 0)
	string = "\"\"";
    tree = tok822_parse(string);
    /* string->extern_addr would be invalidated by tok822_externalize() */
    string = 0;
    addr_list = tok822_grep(tree, TOK822_ADDR);
    for (tpp = addr_list; *tpp; tpp++) {
	tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL);
	canon_addr_external(canon_addr, STR(extern_addr));
	unquote_822_local(intern_addr, STR(canon_addr));
	if (extension) {
	    VSTRING_SPACE(intern_addr, extlen + 1);
	    if ((ratsign = strrchr(STR(intern_addr), '@')) == 0) {
		vstring_strcat(intern_addr, extension);
	    } else {
		memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1);
		memcpy(ratsign, extension, extlen);
		VSTRING_SKIP(intern_addr);
	    }
	}
	/* Optionally convert output to external form. */
	if (out_form == MA_FORM_EXTERNAL) {
	    quote_822_local(extern_addr, STR(intern_addr));
	    argv_add(argv, STR(extern_addr), ARGV_END);
	} else {
	    argv_add(argv, STR(intern_addr), ARGV_END);
	}
    }
    argv_terminate(argv);
    myfree((void *) addr_list);
    tok822_free_tree(tree);
    vstring_free(canon_addr);
    vstring_free(extern_addr);
    vstring_free(intern_addr);
    return (argv);
}

#ifdef TEST

 /*
  * Stand-alone test program, sort of interactive.
  */
#include <stdlib.h>
#include <unistd.h>
#include <msg.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <mail_conf.h>
#include <mail_params.h>

/* canon_addr_external - surrogate to avoid trivial-rewrite dependency */

VSTRING *canon_addr_external(VSTRING *result, const char *addr)
{
    return (vstring_strcpy(result, addr));
}

static int get_addr_form(const char *prompt, VSTRING *buf)
{
    int     addr_form;

    if (prompt) {
	vstream_printf("%s: ", prompt);
	vstream_fflush(VSTREAM_OUT);
    }
    if (vstring_get_nonl(buf, VSTREAM_IN) == VSTREAM_EOF)
	exit(0);
    if ((addr_form = mail_addr_form_from_string(STR(buf))) < 0)
	msg_fatal("bad address form: %s", STR(buf));
    return (addr_form);
}

int     main(int unused_argc, char **unused_argv)
{
    VSTRING *extension = vstring_alloc(1);
    VSTRING *buf = vstring_alloc(1);
    ARGV   *argv;
    char  **cpp;
    int     do_prompt = isatty(0);
    int     in_form;
    int     out_form;

    mail_conf_read();
    if (chdir(var_queue_dir) < 0)
	msg_fatal("chdir %s: %m", var_queue_dir);

    in_form = get_addr_form(do_prompt ? "input form" : 0, buf);
    out_form = get_addr_form(do_prompt ? "output form" : 0, buf);
    if (do_prompt) {
	vstream_printf("extension: (CR for none): ");
	vstream_fflush(VSTREAM_OUT);
    }
    if (vstring_get_nonl(extension, VSTREAM_IN) == VSTREAM_EOF)
	exit(0);

    if (do_prompt) {
	vstream_printf("print strings to be translated, one per line\n");
	vstream_fflush(VSTREAM_OUT);
    }
    while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
	argv = mail_addr_crunch_opt(STR(buf), (VSTRING_LEN(extension) ?
					       STR(extension) : 0),
				    in_form, out_form);
	for (cpp = argv->argv; *cpp; cpp++)
	    vstream_printf("|%s|\n", *cpp);
	vstream_fflush(VSTREAM_OUT);
	argv_free(argv);
    }
    vstring_free(extension);
    vstring_free(buf);
    return (0);
}

#endif