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