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