hfrom_format.c revision 1.1
1/*	$NetBSD: hfrom_format.c,v 1.1 2022/10/08 16:09:07 christos Exp $	*/
2
3/*++
4/* NAME
5/*	hfrom_format 3
6/* SUMMARY
7/*	Parse a header_from_format setting
8/* SYNOPSIS
9/*	#include <hfrom_format.h>
10/*
11/*	int	hfrom_format_parse(
12/*	const char *name,
13/*	const char *value)
14/*
15/*	const char *str_hfrom_format_code(int code)
16/* DESCRIPTION
17/*	hfrom_format_parse() takes a parameter name (used for
18/*	diagnostics) and value, and maps it to the corresponding
19/*	code: HFROM_FORMAT_NAME_STD maps to HFROM_FORMAT_CODE_STD,
20/*	and HFROM_FORMAT_NAME_OBS maps to HFROM_FORMAT_CODE_OBS.
21/*
22/*	str_hfrom_format_code() does the reverse mapping.
23/* DIAGNOSTICS
24/*	All input errors are fatal.
25/* LICENSE
26/* .ad
27/* .fi
28/*	The Secure Mailer license must be distributed with this software.
29/* AUTHOR(S)
30/*	Wietse Venema
31/*	Google, Inc.
32/*	111 8th Avenue
33/*	New York, NY 10011, USA
34/*--*/
35
36 /*
37  * System library.
38  */
39#include <sys_defs.h>
40
41 /*
42  * Utility library.
43  */
44#include <name_code.h>
45#include <msg.h>
46
47 /*
48  * Global library.
49  */
50#include <mail_params.h>
51
52 /*
53  * Application-specific.
54  */
55#include <hfrom_format.h>
56
57 /*
58  * Primitive dependency injection.
59  */
60#ifdef TEST
61extern NORETURN PRINTFLIKE(1, 2) test_msg_fatal(const char *,...);
62
63#define msg_fatal test_msg_fatal
64#endif
65
66 /*
67  * The name-to-code mapping.
68  */
69static const NAME_CODE hfrom_format_table[] = {
70    HFROM_FORMAT_NAME_STD, HFROM_FORMAT_CODE_STD,
71    HFROM_FORMAT_NAME_OBS, HFROM_FORMAT_CODE_OBS,
72    0, -1,
73};
74
75/* hfrom_format_parse - parse header_from_format setting */
76
77int     hfrom_format_parse(const char *name, const char *value)
78{
79    int     code;
80
81    if ((code = name_code(hfrom_format_table, NAME_CODE_FLAG_NONE, value)) < 0)
82	msg_fatal("invalid setting: \"%s = %s\"", name, value);
83    return (code);
84}
85
86/* str_hfrom_format_code - convert code to string */
87
88const char *str_hfrom_format_code(int code)
89{
90    const char *name;
91
92    if ((name = str_name_code(hfrom_format_table, code)) == 0)
93	msg_fatal("invalid header format code: %d", code);
94    return (name);
95}
96
97#ifdef TEST
98#include <stdlib.h>
99#include <setjmp.h>
100#include <string.h>
101
102#include <vstream.h>
103#include <vstring.h>
104#include <msg_vstream.h>
105
106#define STR(x) vstring_str(x)
107
108 /*
109  * TODO(wietse) make this a proper VSTREAM interface. Instead of temporarily
110  * swapping streams, we could temporarily swap the stream's write function.
111  */
112
113/* vstream_swap - kludge to capture output for testing */
114
115static void vstream_swap(VSTREAM *one, VSTREAM *two)
116{
117    VSTREAM save;
118
119    save = *one;
120    *one = *two;
121    *two = save;
122}
123
124jmp_buf test_fatal_jbuf;
125
126#undef msg_fatal
127
128/* test_msg_fatal - does not return, and does not terminate */
129
130void    test_msg_fatal(const char *fmt,...)
131{
132    va_list ap;
133
134    va_start(ap, fmt);
135    vmsg_warn(fmt, ap);
136    va_end(ap);
137    longjmp(test_fatal_jbuf, 1);
138}
139
140struct name_test_case {
141    const char *label;			/* identifies test case */
142    const char *config;			/* configuration under test */
143    const char *exp_warning;		/* expected warning or empty */
144    const int exp_code;			/* expected code */
145};
146
147static struct name_test_case name_test_cases[] = {
148    {"hfrom_format_parse good-standard",
149	 /* config */ HFROM_FORMAT_NAME_STD,
150	 /* warning */ "",
151	 /* exp_code */ HFROM_FORMAT_CODE_STD
152    },
153    {"hfrom_format_parse good-obsolete",
154	 /* config */ HFROM_FORMAT_NAME_OBS,
155	 /* warning */ "",
156	 /* exp_code */ HFROM_FORMAT_CODE_OBS
157    },
158    {"hfrom_format_parse bad",
159	 /* config */ "does-not-exist",
160	 /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse bad = does-not-exist\"\n",
161	 /* code */ 0,
162    },
163    {"hfrom_format_parse empty",
164	 /* config */ "",
165	 /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse empty = \"\n",
166	 /* code */ 0,
167    },
168    0,
169};
170
171struct code_test_case {
172    const char *label;			/* identifies test case */
173    int     code;			/* code under test */
174    const char *exp_warning;		/* expected warning or empty */
175    const char *exp_name;		/* expected namme */
176};
177
178static struct code_test_case code_test_cases[] = {
179    {"str_hfrom_format_code good-standard",
180	 /* code */ HFROM_FORMAT_CODE_STD,
181	 /* warning */ "",
182	 /* exp_name */ HFROM_FORMAT_NAME_STD
183    },
184    {"str_hfrom_format_code good-obsolete",
185	 /* code */ HFROM_FORMAT_CODE_OBS,
186	 /* warning */ "",
187	 /* exp_name */ HFROM_FORMAT_NAME_OBS
188    },
189    {"str_hfrom_format_code bad",
190	 /* config */ 12345,
191	 /* warning */ "hfrom_format: warning: invalid header format code: 12345\n",
192	 /* exp_name */ 0
193    },
194    0,
195};
196
197int     main(int argc, char **argv)
198{
199    struct name_test_case *np;
200    int     code;
201    struct code_test_case *cp;
202    const char *name;
203    int     pass = 0;
204    int     fail = 0;
205    int     test_failed;
206    VSTRING *msg_buf;
207    VSTREAM *memory_stream;
208
209    msg_vstream_init("hfrom_format", VSTREAM_ERR);
210    msg_buf = vstring_alloc(100);
211
212    for (np = name_test_cases; np->label != 0; np++) {
213	VSTRING_RESET(msg_buf);
214	VSTRING_TERMINATE(msg_buf);
215	test_failed = 0;
216	if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
217	    msg_fatal("open memory stream: %m");
218	vstream_swap(VSTREAM_ERR, memory_stream);
219	if (setjmp(test_fatal_jbuf) == 0)
220	    code = hfrom_format_parse(np->label, np->config);
221	vstream_swap(memory_stream, VSTREAM_ERR);
222	if (vstream_fclose(memory_stream))
223	    msg_fatal("close memory stream: %m");
224	if (strcmp(STR(msg_buf), np->exp_warning) != 0) {
225	    msg_warn("test case %s: got error: \"%s\", want: \"%s\"",
226		     np->label, STR(msg_buf), np->exp_warning);
227	    test_failed = 1;
228	}
229	if (*np->exp_warning == 0) {
230	    if (code != np->exp_code) {
231		msg_warn("test case %s: got code: \"%d\", want: \"%d\"(%s)",
232			 np->label, code, np->exp_code,
233			 str_hfrom_format_code(np->exp_code));
234		test_failed = 1;
235	    }
236	}
237	if (test_failed) {
238	    msg_info("%s: FAIL", np->label);
239	    fail++;
240	} else {
241	    msg_info("%s: PASS", np->label);
242	    pass++;
243	}
244    }
245
246    for (cp = code_test_cases; cp->label != 0; cp++) {
247	VSTRING_RESET(msg_buf);
248	VSTRING_TERMINATE(msg_buf);
249	test_failed = 0;
250	if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
251	    msg_fatal("open memory stream: %m");
252	vstream_swap(VSTREAM_ERR, memory_stream);
253	if (setjmp(test_fatal_jbuf) == 0)
254	    name = str_hfrom_format_code(cp->code);
255	vstream_swap(memory_stream, VSTREAM_ERR);
256	if (vstream_fclose(memory_stream))
257	    msg_fatal("close memory stream: %m");
258	if (strcmp(STR(msg_buf), cp->exp_warning) != 0) {
259	    msg_warn("test case %s: got error: \"%s\", want: \"%s\"",
260		     cp->label, STR(msg_buf), cp->exp_warning);
261	    test_failed = 1;
262	} else if (*cp->exp_warning == 0) {
263	    if (strcmp(name, cp->exp_name)) {
264		msg_warn("test case %s: got name: \"%s\", want: \"%s\"",
265			 cp->label, name, cp->exp_name);
266		test_failed = 1;
267	    }
268	}
269	if (test_failed) {
270	    msg_info("%s: FAIL", cp->label);
271	    fail++;
272	} else {
273	    msg_info("%s: PASS", cp->label);
274	    pass++;
275	}
276    }
277
278    msg_info("PASS=%d FAIL=%d", pass, fail);
279    vstring_free(msg_buf);
280    exit(fail != 0);
281}
282
283#endif
284