diff options
Diffstat (limited to '')
-rw-r--r-- | src/global/hfrom_format.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/src/global/hfrom_format.c b/src/global/hfrom_format.c new file mode 100644 index 0000000..f0f850a --- /dev/null +++ b/src/global/hfrom_format.c @@ -0,0 +1,281 @@ +/*++ +/* NAME +/* hfrom_format 3 +/* SUMMARY +/* Parse a header_from_format setting +/* SYNOPSIS +/* #include <hfrom_format.h> +/* +/* int hfrom_format_parse( +/* const char *name, +/* const char *value) +/* +/* const char *str_hfrom_format_code(int code) +/* DESCRIPTION +/* hfrom_format_parse() takes a parameter name (used for +/* diagnostics) and value, and maps it to the corresponding +/* code: HFROM_FORMAT_NAME_STD maps to HFROM_FORMAT_CODE_STD, +/* and HFROM_FORMAT_NAME_OBS maps to HFROM_FORMAT_CODE_OBS. +/* +/* str_hfrom_format_code() does the reverse mapping. +/* DIAGNOSTICS +/* All input errors are fatal. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include <sys_defs.h> + + /* + * Utility library. + */ +#include <name_code.h> +#include <msg.h> + + /* + * Global library. + */ +#include <mail_params.h> + + /* + * Application-specific. + */ +#include <hfrom_format.h> + + /* + * Primitive dependency injection. + */ +#ifdef TEST +extern NORETURN PRINTFLIKE(1, 2) test_msg_fatal(const char *,...); + +#define msg_fatal test_msg_fatal +#endif + + /* + * The name-to-code mapping. + */ +static const NAME_CODE hfrom_format_table[] = { + HFROM_FORMAT_NAME_STD, HFROM_FORMAT_CODE_STD, + HFROM_FORMAT_NAME_OBS, HFROM_FORMAT_CODE_OBS, + 0, -1, +}; + +/* hfrom_format_parse - parse header_from_format setting */ + +int hfrom_format_parse(const char *name, const char *value) +{ + int code; + + if ((code = name_code(hfrom_format_table, NAME_CODE_FLAG_NONE, value)) < 0) + msg_fatal("invalid setting: \"%s = %s\"", name, value); + return (code); +} + +/* str_hfrom_format_code - convert code to string */ + +const char *str_hfrom_format_code(int code) +{ + const char *name; + + if ((name = str_name_code(hfrom_format_table, code)) == 0) + msg_fatal("invalid header format code: %d", code); + return (name); +} + +#ifdef TEST +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> + +#include <vstream.h> +#include <vstring.h> +#include <msg_vstream.h> + +#define STR(x) vstring_str(x) + + /* + * TODO(wietse) make this a proper VSTREAM interface. Instead of temporarily + * swapping streams, we could temporarily swap the stream's write function. + */ + +/* vstream_swap - kludge to capture output for testing */ + +static void vstream_swap(VSTREAM *one, VSTREAM *two) +{ + VSTREAM save; + + save = *one; + *one = *two; + *two = save; +} + +jmp_buf test_fatal_jbuf; + +#undef msg_fatal + +/* test_msg_fatal - does not return, and does not terminate */ + +void test_msg_fatal(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vmsg_warn(fmt, ap); + va_end(ap); + longjmp(test_fatal_jbuf, 1); +} + +struct name_test_case { + const char *label; /* identifies test case */ + const char *config; /* configuration under test */ + const char *exp_warning; /* expected warning or empty */ + const int exp_code; /* expected code */ +}; + +static struct name_test_case name_test_cases[] = { + {"hfrom_format_parse good-standard", + /* config */ HFROM_FORMAT_NAME_STD, + /* warning */ "", + /* exp_code */ HFROM_FORMAT_CODE_STD + }, + {"hfrom_format_parse good-obsolete", + /* config */ HFROM_FORMAT_NAME_OBS, + /* warning */ "", + /* exp_code */ HFROM_FORMAT_CODE_OBS + }, + {"hfrom_format_parse bad", + /* config */ "does-not-exist", + /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse bad = does-not-exist\"\n", + /* code */ 0, + }, + {"hfrom_format_parse empty", + /* config */ "", + /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse empty = \"\n", + /* code */ 0, + }, + 0, +}; + +struct code_test_case { + const char *label; /* identifies test case */ + int code; /* code under test */ + const char *exp_warning; /* expected warning or empty */ + const char *exp_name; /* expected namme */ +}; + +static struct code_test_case code_test_cases[] = { + {"str_hfrom_format_code good-standard", + /* code */ HFROM_FORMAT_CODE_STD, + /* warning */ "", + /* exp_name */ HFROM_FORMAT_NAME_STD + }, + {"str_hfrom_format_code good-obsolete", + /* code */ HFROM_FORMAT_CODE_OBS, + /* warning */ "", + /* exp_name */ HFROM_FORMAT_NAME_OBS + }, + {"str_hfrom_format_code bad", + /* config */ 12345, + /* warning */ "hfrom_format: warning: invalid header format code: 12345\n", + /* exp_name */ 0 + }, + 0, +}; + +int main(int argc, char **argv) +{ + struct name_test_case *np; + int code; + struct code_test_case *cp; + const char *name; + int pass = 0; + int fail = 0; + int test_failed; + VSTRING *msg_buf; + VSTREAM *memory_stream; + + msg_vstream_init("hfrom_format", VSTREAM_ERR); + msg_buf = vstring_alloc(100); + + for (np = name_test_cases; np->label != 0; np++) { + VSTRING_RESET(msg_buf); + VSTRING_TERMINATE(msg_buf); + test_failed = 0; + if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) + msg_fatal("open memory stream: %m"); + vstream_swap(VSTREAM_ERR, memory_stream); + if (setjmp(test_fatal_jbuf) == 0) + code = hfrom_format_parse(np->label, np->config); + vstream_swap(memory_stream, VSTREAM_ERR); + if (vstream_fclose(memory_stream)) + msg_fatal("close memory stream: %m"); + if (strcmp(STR(msg_buf), np->exp_warning) != 0) { + msg_warn("test case %s: got error: \"%s\", want: \"%s\"", + np->label, STR(msg_buf), np->exp_warning); + test_failed = 1; + } + if (*np->exp_warning == 0) { + if (code != np->exp_code) { + msg_warn("test case %s: got code: \"%d\", want: \"%d\"(%s)", + np->label, code, np->exp_code, + str_hfrom_format_code(np->exp_code)); + test_failed = 1; + } + } + if (test_failed) { + msg_info("%s: FAIL", np->label); + fail++; + } else { + msg_info("%s: PASS", np->label); + pass++; + } + } + + for (cp = code_test_cases; cp->label != 0; cp++) { + VSTRING_RESET(msg_buf); + VSTRING_TERMINATE(msg_buf); + test_failed = 0; + if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) + msg_fatal("open memory stream: %m"); + vstream_swap(VSTREAM_ERR, memory_stream); + if (setjmp(test_fatal_jbuf) == 0) + name = str_hfrom_format_code(cp->code); + vstream_swap(memory_stream, VSTREAM_ERR); + if (vstream_fclose(memory_stream)) + msg_fatal("close memory stream: %m"); + if (strcmp(STR(msg_buf), cp->exp_warning) != 0) { + msg_warn("test case %s: got error: \"%s\", want: \"%s\"", + cp->label, STR(msg_buf), cp->exp_warning); + test_failed = 1; + } else if (*cp->exp_warning == 0) { + if (strcmp(name, cp->exp_name)) { + msg_warn("test case %s: got name: \"%s\", want: \"%s\"", + cp->label, name, cp->exp_name); + test_failed = 1; + } + } + if (test_failed) { + msg_info("%s: FAIL", cp->label); + fail++; + } else { + msg_info("%s: PASS", cp->label); + pass++; + } + } + + msg_info("PASS=%d FAIL=%d", pass, fail); + vstring_free(msg_buf); + exit(fail != 0); +} + +#endif |