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