summaryrefslogtreecommitdiffstats
path: root/src/global/hfrom_format.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/hfrom_format.c')
-rw-r--r--src/global/hfrom_format.c281
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