summaryrefslogtreecommitdiffstats
path: root/src/util/dict_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/dict_file.c')
-rw-r--r--src/util/dict_file.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/util/dict_file.c b/src/util/dict_file.c
new file mode 100644
index 0000000..c6ea74c
--- /dev/null
+++ b/src/util/dict_file.c
@@ -0,0 +1,231 @@
+/*++
+/* NAME
+/* dict_file_to_buf 3
+/* SUMMARY
+/* include content from file as blob
+/* SYNOPSIS
+/* #include <dict.h>
+/*
+/* VSTRING *dict_file_to_buf(
+/* DICT *dict,
+/* const char *pathnames)
+/*
+/* VSTRING *dict_file_to_b64(
+/* DICT *dict,
+/* const char *pathnames)
+/*
+/* VSTRING *dict_file_from_b64(
+/* DICT *dict,
+/* const char *value)
+/*
+/* char *dict_file_get_error(
+/* DICT *dict)
+/*
+/* void dict_file_purge_buffers(
+/* DICT *dict)
+/*
+/* const char *dict_file_lookup(
+/* DICT *dict)
+/* DESCRIPTION
+/* dict_file_to_buf() reads the content of the specified files,
+/* with names separated by CHARS_COMMA_SP, while inserting a
+/* gratuitous newline character between files. It returns a
+/* pointer to a buffer which is owned by the DICT, or a null
+/* pointer in case of error.
+/*
+/* dict_file_to_b64() invokes dict_file_to_buf() and converts
+/* the result to base64. It returns a pointer to a buffer which
+/* is owned by the DICT, or a null pointer in case of error.
+/*
+/* dict_file_from_b64() converts a value from base64. It returns
+/* a pointer to a buffer which is owned by the DICT, or a null
+/* pointer in case of error.
+/*
+/* dict_file_purge_buffers() disposes of dict_file-related
+/* memory that are associated with this DICT.
+/*
+/* dict_file_get_error() should be called only after error;
+/* it returns a description of the problem. Storage is owned
+/* by the caller.
+/*
+/* dict_file_lookup() wraps the dictionary lookup method and
+/* decodes the base64 lookup result. The dictionary must be
+/* opened with DICT_FLAG_SRC_RHS_IS_FILE. Sets dict->error to
+/* DICT_ERR_CONFIG if the content is invalid. Decoding is not
+/* built into the dict->lookup() method, because that would
+/* complicate the implementation of map nesting (inline, thash),
+/* map composition (pipemap, unionmap), and map proxying.
+/* DIAGNOSTICS
+/* Panic: interface violation.
+/*
+/* In case of error the VSTRING result value is a null pointer, and
+/* an error description can be retrieved with dict_file_get_error().
+/* The storage is owned by the caller.
+/* 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>
+#include <sys/stat.h>
+#include <string.h>
+
+ /*
+ * Utility library.
+ */
+#include <base64_code.h>
+#include <dict.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * SLMs.
+ */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* dict_file_to_buf - read files into a buffer */
+
+VSTRING *dict_file_to_buf(DICT *dict, const char *pathnames)
+{
+ struct stat st;
+ VSTREAM *fp = 0;
+ ARGV *argv;
+ char **cpp;
+
+ /* dict_file_to_buf() postcondition: dict->file_buf exists. */
+ if (dict->file_buf == 0)
+ dict->file_buf = vstring_alloc(100);
+
+#define DICT_FILE_RETURN(retval) do { \
+ argv_free(argv); \
+ if (fp) vstream_fclose(fp); \
+ return (retval); \
+ } while (0);
+
+ argv = argv_split(pathnames, CHARS_COMMA_SP);
+ if (argv->argc == 0) {
+ vstring_sprintf(dict->file_buf, "empty pathname list: >>%s<<'",
+ pathnames);
+ DICT_FILE_RETURN(0);
+ }
+ VSTRING_RESET(dict->file_buf);
+ for (cpp = argv->argv; *cpp; cpp++) {
+ if ((fp = vstream_fopen(*cpp, O_RDONLY, 0)) == 0
+ || fstat(vstream_fileno(fp), &st) < 0) {
+ vstring_sprintf(dict->file_buf, "open %s: %m", *cpp);
+ DICT_FILE_RETURN(0);
+ }
+ if (st.st_size > SSIZE_T_MAX - LEN(dict->file_buf)) {
+ vstring_sprintf(dict->file_buf, "file too large: %s", pathnames);
+ DICT_FILE_RETURN(0);
+ }
+ if (vstream_fread_app(fp, dict->file_buf, st.st_size) != st.st_size) {
+ vstring_sprintf(dict->file_buf, "read %s: %m", *cpp);
+ DICT_FILE_RETURN(0);
+ }
+ (void) vstream_fclose(fp);
+ fp = 0;
+ if (cpp[1] != 0)
+ VSTRING_ADDCH(dict->file_buf, '\n');
+ }
+ VSTRING_TERMINATE(dict->file_buf);
+ DICT_FILE_RETURN(dict->file_buf);
+}
+
+/* dict_file_to_b64 - read files into a base64-encoded buffer */
+
+VSTRING *dict_file_to_b64(DICT *dict, const char *pathnames)
+{
+ ssize_t helper;
+
+ if (dict_file_to_buf(dict, pathnames) == 0)
+ return (0);
+ if (dict->file_b64 == 0)
+ dict->file_b64 = vstring_alloc(100);
+ helper = (LEN(dict->file_buf) + 2) / 3;
+ if (helper > SSIZE_T_MAX / 4) {
+ vstring_sprintf(dict->file_buf, "file too large: %s", pathnames);
+ return (0);
+ }
+ VSTRING_RESET(dict->file_b64);
+ VSTRING_SPACE(dict->file_b64, helper * 4);
+ return (base64_encode(dict->file_b64, STR(dict->file_buf),
+ LEN(dict->file_buf)));
+}
+
+/* dict_file_from_b64 - convert value from base64 */
+
+VSTRING *dict_file_from_b64(DICT *dict, const char *value)
+{
+ ssize_t helper;
+ VSTRING *result;
+
+ if (dict->file_buf == 0)
+ dict->file_buf = vstring_alloc(100);
+ helper = strlen(value) / 4;
+ VSTRING_RESET(dict->file_buf);
+ VSTRING_SPACE(dict->file_buf, helper * 3);
+ result = base64_decode(dict->file_buf, value, strlen(value));
+ if (result == 0)
+ vstring_sprintf(dict->file_buf, "malformed BASE64 value: %.30s", value);
+ return (result);
+}
+
+/* dict_file_get_error - return error text */
+
+char *dict_file_get_error(DICT *dict)
+{
+ if (dict->file_buf == 0)
+ msg_panic("dict_file_get_error: no buffer");
+ return (mystrdup(STR(dict->file_buf)));
+}
+
+/* dict_file_purge_buffers - purge file buffers */
+
+void dict_file_purge_buffers(DICT *dict)
+{
+ if (dict->file_buf) {
+ vstring_free(dict->file_buf);
+ dict->file_buf = 0;
+ }
+ if (dict->file_b64) {
+ vstring_free(dict->file_b64);
+ dict->file_b64 = 0;
+ }
+}
+
+/* dict_file_lookup - look up and decode dictionary entry */
+
+const char *dict_file_lookup(DICT *dict, const char *key)
+{
+ const char myname[] = "dict_file_lookup";
+ const char *res;
+ VSTRING *unb64;
+ char *err;
+
+ if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
+ msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE",
+ myname);
+ if ((res = dict->lookup(dict, key)) == 0)
+ return (0);
+ if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
+ err = dict_file_get_error(dict);
+ msg_warn("table %s:%s: key %s: %s", dict->type, dict->name, key, err);
+ myfree(err);
+ dict->error = DICT_ERR_CONFIG;
+ return (0);
+ }
+ return STR(unb64);
+}