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