Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: dict_file.c,v 1.3 2022/10/08 16:12:50 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	dict_file_to_buf 3
      6 /* SUMMARY
      7 /*	include content from file as blob
      8 /* SYNOPSIS
      9 /*	#include <dict.h>
     10 /*
     11 /*	VSTRING	*dict_file_to_buf(
     12 /*	DICT	*dict,
     13 /*	const char *pathnames)
     14 /*
     15 /*	VSTRING	*dict_file_to_b64(
     16 /*	DICT	*dict,
     17 /*	const char *pathnames)
     18 /*
     19 /*	VSTRING	*dict_file_from_b64(
     20 /*	DICT	*dict,
     21 /*	const char *value)
     22 /*
     23 /*	char	*dict_file_get_error(
     24 /*	DICT	*dict)
     25 /*
     26 /*	void	dict_file_purge_buffers(
     27 /*	DICT	*dict)
     28 /*
     29 /*	const char *dict_file_lookup(
     30 /*	DICT	*dict)
     31 /* DESCRIPTION
     32 /*	dict_file_to_buf() reads the content of the specified files,
     33 /*	with names separated by CHARS_COMMA_SP, while inserting a
     34 /*	gratuitous newline character between files. It returns a
     35 /*	pointer to a buffer which is owned by the DICT, or a null
     36 /*	pointer in case of error.
     37 /*
     38 /*	dict_file_to_b64() invokes dict_file_to_buf() and converts
     39 /*	the result to base64. It returns a pointer to a buffer which
     40 /*	is owned by the DICT, or a null pointer in case of error.
     41 /*
     42 /*	dict_file_from_b64() converts a value from base64. It returns
     43 /*	a pointer to a buffer which is owned by the DICT, or a null
     44 /*	pointer in case of error.
     45 /*
     46 /*	dict_file_purge_buffers() disposes of dict_file-related
     47 /*	memory that are associated with this DICT.
     48 /*
     49 /*	dict_file_get_error() should be called only after error;
     50 /*	it returns a description of the problem. Storage is owned
     51 /*	by the caller.
     52 /*
     53 /*	dict_file_lookup() wraps the dictionary lookup method and
     54 /*	decodes the base64 lookup result. The dictionary must be
     55 /*	opened with DICT_FLAG_SRC_RHS_IS_FILE. Sets dict->error to
     56 /*	DICT_ERR_CONFIG if the content is invalid. Decoding is not
     57 /*	built into the dict->lookup() method, because that would
     58 /*	complicate the implementation of map nesting (inline, thash),
     59 /*	map composition (pipemap, unionmap), and map proxying.
     60 /* DIAGNOSTICS
     61 /*	Panic: interface violation.
     62 /*
     63 /*	In case of error the VSTRING result value is a null pointer, and
     64 /*	an error description can be retrieved with dict_file_get_error().
     65 /*	The storage is owned by the caller.
     66 /* LICENSE
     67 /* .ad
     68 /* .fi
     69 /*	The Secure Mailer license must be distributed with this software.
     70 /* AUTHOR(S)
     71 /*	Wietse Venema
     72 /*	Google, Inc.
     73 /*	111 8th Avenue
     74 /*	New York, NY 10011, USA
     75 /*--*/
     76 
     77  /*
     78   * System library.
     79   */
     80 #include <sys_defs.h>
     81 #include <sys/stat.h>
     82 #include <string.h>
     83 
     84  /*
     85   * Utility library.
     86   */
     87 #include <base64_code.h>
     88 #include <dict.h>
     89 #include <msg.h>
     90 #include <mymalloc.h>
     91 #include <vstream.h>
     92 #include <vstring.h>
     93 
     94  /*
     95   * SLMs.
     96   */
     97 #define STR(x) vstring_str(x)
     98 #define LEN(x) VSTRING_LEN(x)
     99 
    100 /* dict_file_to_buf - read files into a buffer */
    101 
    102 VSTRING *dict_file_to_buf(DICT *dict, const char *pathnames)
    103 {
    104     struct stat st;
    105     VSTREAM *fp = 0;
    106     ARGV   *argv;
    107     char  **cpp;
    108 
    109     /* dict_file_to_buf() postcondition: dict->file_buf exists. */
    110     if (dict->file_buf == 0)
    111 	dict->file_buf = vstring_alloc(100);
    112 
    113 #define DICT_FILE_RETURN(retval) do { \
    114 	argv_free(argv); \
    115 	if (fp) vstream_fclose(fp); \
    116 	return (retval); \
    117     } while (0);
    118 
    119     argv = argv_split(pathnames, CHARS_COMMA_SP);
    120     if (argv->argc == 0) {
    121 	vstring_sprintf(dict->file_buf, "empty pathname list: >>%s<<'",
    122 			pathnames);
    123 	DICT_FILE_RETURN(0);
    124     }
    125     VSTRING_RESET(dict->file_buf);
    126     for (cpp = argv->argv; *cpp; cpp++) {
    127 	if ((fp = vstream_fopen(*cpp, O_RDONLY, 0)) == 0
    128 	    || fstat(vstream_fileno(fp), &st) < 0) {
    129 	    vstring_sprintf(dict->file_buf, "open %s: %m", *cpp);
    130 	    DICT_FILE_RETURN(0);
    131 	}
    132 	if (st.st_size > SSIZE_T_MAX - LEN(dict->file_buf)) {
    133 	    vstring_sprintf(dict->file_buf, "file too large: %s", pathnames);
    134 	    DICT_FILE_RETURN(0);
    135 	}
    136 	if (vstream_fread_app(fp, dict->file_buf, st.st_size) != st.st_size) {
    137 	    vstring_sprintf(dict->file_buf, "read %s: %m", *cpp);
    138 	    DICT_FILE_RETURN(0);
    139 	}
    140 	(void) vstream_fclose(fp);
    141 	fp = 0;
    142 	if (cpp[1] != 0)
    143 	    VSTRING_ADDCH(dict->file_buf, '\n');
    144     }
    145     VSTRING_TERMINATE(dict->file_buf);
    146     DICT_FILE_RETURN(dict->file_buf);
    147 }
    148 
    149 /* dict_file_to_b64 - read files into a base64-encoded buffer */
    150 
    151 VSTRING *dict_file_to_b64(DICT *dict, const char *pathnames)
    152 {
    153     ssize_t helper;
    154 
    155     if (dict_file_to_buf(dict, pathnames) == 0)
    156 	return (0);
    157     if (dict->file_b64 == 0)
    158 	dict->file_b64 = vstring_alloc(100);
    159     helper = (LEN(dict->file_buf) + 2) / 3;
    160     if (helper > SSIZE_T_MAX / 4) {
    161 	vstring_sprintf(dict->file_buf, "file too large: %s", pathnames);
    162 	return (0);
    163     }
    164     VSTRING_RESET(dict->file_b64);
    165     VSTRING_SPACE(dict->file_b64, helper * 4);
    166     return (base64_encode(dict->file_b64, STR(dict->file_buf),
    167 			  LEN(dict->file_buf)));
    168 }
    169 
    170 /* dict_file_from_b64 - convert value from base64 */
    171 
    172 VSTRING *dict_file_from_b64(DICT *dict, const char *value)
    173 {
    174     ssize_t helper;
    175     VSTRING *result;
    176 
    177     if (dict->file_buf == 0)
    178 	dict->file_buf = vstring_alloc(100);
    179     helper = strlen(value) / 4;
    180     VSTRING_RESET(dict->file_buf);
    181     VSTRING_SPACE(dict->file_buf, helper * 3);
    182     result = base64_decode(dict->file_buf, value, strlen(value));
    183     if (result == 0)
    184 	vstring_sprintf(dict->file_buf, "malformed BASE64 value: %.30s", value);
    185     return (result);
    186 }
    187 
    188 /* dict_file_get_error - return error text */
    189 
    190 char   *dict_file_get_error(DICT *dict)
    191 {
    192     if (dict->file_buf == 0)
    193 	msg_panic("dict_file_get_error: no buffer");
    194     return (mystrdup(STR(dict->file_buf)));
    195 }
    196 
    197 /* dict_file_purge_buffers - purge file buffers */
    198 
    199 void    dict_file_purge_buffers(DICT *dict)
    200 {
    201     if (dict->file_buf) {
    202 	vstring_free(dict->file_buf);
    203 	dict->file_buf = 0;
    204     }
    205     if (dict->file_b64) {
    206 	vstring_free(dict->file_b64);
    207 	dict->file_b64 = 0;
    208     }
    209 }
    210 
    211 /* dict_file_lookup - look up and decode dictionary entry */
    212 
    213 const char *dict_file_lookup(DICT *dict, const char *key)
    214 {
    215     const char myname[] = "dict_file_lookup";
    216     const char *res;
    217     VSTRING *unb64;
    218     char   *err;
    219 
    220     if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
    221 	msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE",
    222 		  myname);
    223     if ((res = dict->lookup(dict, key)) == 0)
    224 	return (0);
    225     if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
    226 	err = dict_file_get_error(dict);
    227 	msg_warn("table %s:%s: key %s: %s", dict->type, dict->name, key, err);
    228 	myfree(err);
    229 	dict->error = DICT_ERR_CONFIG;
    230 	return (0);
    231     }
    232     return STR(unb64);
    233 }
    234