xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/hfrom_format.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: hfrom_format.c,v 1.2 2022/10/08 16:12:45 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
61 extern 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   */
69 static 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 
hfrom_format_parse(const char * name,const char * value)77 int     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 
str_hfrom_format_code(int code)88 const 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 
vstream_swap(VSTREAM * one,VSTREAM * two)115 static void vstream_swap(VSTREAM *one, VSTREAM *two)
116 {
117     VSTREAM save;
118 
119     save = *one;
120     *one = *two;
121     *two = save;
122 }
123 
124 jmp_buf test_fatal_jbuf;
125 
126 #undef msg_fatal
127 
128 /* test_msg_fatal - does not return, and does not terminate */
129 
test_msg_fatal(const char * fmt,...)130 void    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 
140 struct 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 
147 static 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 
171 struct 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 
178 static 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 
main(int argc,char ** argv)197 int     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