xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_file.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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 
dict_file_to_buf(DICT * dict,const char * pathnames)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 
dict_file_to_b64(DICT * dict,const char * pathnames)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 
dict_file_from_b64(DICT * dict,const char * value)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 
dict_file_get_error(DICT * dict)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 
dict_file_purge_buffers(DICT * dict)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 
dict_file_lookup(DICT * dict,const char * key)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