Home | History | Annotate | Line # | Download | only in global
      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 
     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 
     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 
    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 
    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 
    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