1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "strescape.h"
6 
str_nescape(const void * str,size_t len)7 const char *str_nescape(const void *str, size_t len)
8 {
9 	string_t *dest = t_str_new(len*2);
10 	str_append_escaped(dest, str, len);
11 	return str_c(dest);
12 }
13 
str_append_escaped(string_t * dest,const void * src,size_t src_size)14 void str_append_escaped(string_t *dest, const void *src, size_t src_size)
15 {
16 	const unsigned char *pstart = src, *p = src, *pend = pstart + src_size;
17 	/* see if we need to quote it */
18 	for (; p < pend; p++) {
19 		if (IS_ESCAPED_CHAR(*p))
20 			break;
21 	}
22 
23 	/* quote */
24 	str_append_data(dest, pstart, (size_t)(p - pstart));
25 
26 	for (; p < pend; p++) {
27 		if (IS_ESCAPED_CHAR(*p))
28 			str_append_c(dest, '\\');
29 		str_append_data(dest, p, 1);
30 	}
31 }
32 
str_append_unescaped(string_t * dest,const void * src,size_t src_size)33 void str_append_unescaped(string_t *dest, const void *src, size_t src_size)
34 {
35 	const unsigned char *src_c = src;
36 	size_t start = 0, i = 0;
37 
38 	while (i < src_size) {
39 		for (; i < src_size; i++) {
40 			if (src_c[i] == '\\')
41 				break;
42 		}
43 
44 		str_append_data(dest, src_c + start, i-start);
45 
46 		if (i < src_size) {
47 			if (++i == src_size)
48 				break;
49 			str_append_c(dest, src_c[i++]);
50 		}
51 		start = i;
52 	}
53 }
54 
str_unescape(char * str)55 char *str_unescape(char *str)
56 {
57 	/* @UNSAFE */
58 	char *dest, *start = str;
59 
60 	while (*str != '\\') {
61 		if (*str == '\0')
62 			return start;
63 		str++;
64 	}
65 
66 	for (dest = str; *str != '\0'; str++) {
67 		if (*str == '\\') {
68 			str++;
69 			if (*str == '\0')
70 				break;
71 		}
72 
73 		*dest++ = *str;
74 	}
75 
76 	*dest = '\0';
77 	return start;
78 }
79 
str_unescape_next(const char ** str,const char ** unescaped_r)80 int str_unescape_next(const char **str, const char **unescaped_r)
81 {
82 	const char *p;
83 	char *escaped;
84 	bool esc_found = FALSE;
85 
86 	for (p = *str; *p != '\0'; p++) {
87 		if (*p == '"')
88 			break;
89 		else if (*p == '\\') {
90 			if (p[1] == '\0')
91 				return -1;
92 			esc_found = TRUE;
93 			p++;
94 		}
95 	}
96 	if (*p != '"')
97 		return -1;
98 	escaped = p_strdup_until(unsafe_data_stack_pool, *str, p);
99 	*str = p+1;
100 	*unescaped_r = !esc_found ? escaped : str_unescape(escaped);
101 	return 0;
102 }
103 
str_append_tabescaped_n(string_t * dest,const unsigned char * src,size_t src_size)104 void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size)
105 {
106 	size_t prev_pos = 0;
107 	char esc[2] = { '\001', '\0' };
108 
109 	for (size_t i = 0; i < src_size; i++) {
110 		switch (src[i]) {
111 		case '\000':
112 			esc[1] = '0';
113 			break;
114 		case '\001':
115 			esc[1] = '1';
116 			break;
117 		case '\t':
118 			esc[1] = 't';
119 			break;
120 		case '\r':
121 			esc[1] = 'r';
122 			break;
123 		case '\n':
124 			esc[1] = 'n';
125 			break;
126 		default:
127 			continue;
128 		}
129 		str_append_data(dest, src + prev_pos, i - prev_pos);
130 		str_append_data(dest, esc, 2);
131 		prev_pos = i + 1;
132 	}
133 	str_append_data(dest, src + prev_pos, src_size - prev_pos);
134 }
135 
str_append_tabescaped(string_t * dest,const char * src)136 void str_append_tabescaped(string_t *dest, const char *src)
137 {
138 	size_t pos, prev_pos = 0;
139 	char esc[2] = { '\001', '\0' };
140 
141 	for (;;) {
142 		pos = prev_pos + strcspn(src + prev_pos, "\001\t\r\n");
143 		str_append_data(dest, src + prev_pos, pos - prev_pos);
144 		prev_pos = pos + 1;
145 
146 		switch (src[pos]) {
147 		case '\000':
148 			/* end of src string reached */
149 			return;
150 		case '\001':
151 			esc[1] = '1';
152 			break;
153 		case '\t':
154 			esc[1] = 't';
155 			break;
156 		case '\r':
157 			esc[1] = 'r';
158 			break;
159 		case '\n':
160 			esc[1] = 'n';
161 			break;
162 		default:
163 			i_unreached();
164 		}
165 		str_append_data(dest, esc, 2);
166 	}
167 }
168 
169 
str_tabescape(const char * str)170 const char *str_tabescape(const char *str)
171 {
172 	string_t *tmp;
173 	const char *p;
174 
175 	if ((p = strpbrk(str, "\001\t\r\n")) != NULL) {
176 		tmp = t_str_new(128);
177 		str_append_data(tmp, str, p-str);
178 		str_append_tabescaped(tmp, p);
179 		return str_c(tmp);
180 	}
181 	return str;
182 }
183 
str_append_tabunescaped(string_t * dest,const void * src,size_t src_size)184 void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size)
185 {
186 	const unsigned char *src_c = src;
187 	size_t start = 0, i = 0;
188 
189 	while (i < src_size) {
190 		for (; i < src_size; i++) {
191 			if (src_c[i] == '\001')
192 				break;
193 		}
194 
195 		str_append_data(dest, src_c + start, i-start);
196 
197 		if (i < src_size) {
198 			i++;
199 			if (i < src_size) {
200 				switch (src_c[i]) {
201 				case '0':
202 					str_append_c(dest, '\000');
203 					break;
204 				case '1':
205 					str_append_c(dest, '\001');
206 					break;
207 				case 't':
208 					str_append_c(dest, '\t');
209 					break;
210 				case 'r':
211 					str_append_c(dest, '\r');
212 					break;
213 				case 'n':
214 					str_append_c(dest, '\n');
215 					break;
216 				default:
217 					str_append_c(dest, src_c[i]);
218 					break;
219 				}
220 				i++;
221 			}
222 		}
223 		start = i;
224 	}
225 }
226 
str_tabunescape_from(char * str,char * src)227 static char *str_tabunescape_from(char *str, char *src)
228 {
229 	/* @UNSAFE */
230 	char *dest, *p;
231 
232 	dest = src;
233 	for (;;) {
234 		switch (src[1]) {
235 		case '\0':
236 			/* truncated input */
237 			*dest = '\0';
238 			return str;
239 		case '0':
240 			*dest++ = '\000';
241 			break;
242 		case '1':
243 			*dest++ = '\001';
244 			break;
245 		case 't':
246 			*dest++ = '\t';
247 			break;
248 		case 'r':
249 			*dest++ = '\r';
250 			break;
251 		case 'n':
252 			*dest++ = '\n';
253 			break;
254 		default:
255 			*dest++ = src[1];
256 			break;
257 		}
258 		src += 2;
259 
260 		p = strchr(src, '\001');
261 		if (p == NULL) {
262 			memmove(dest, src, strlen(src)+1);
263 			break;
264 		}
265 
266 		size_t copy_len = p - src;
267 		memmove(dest, src, copy_len);
268 		dest += copy_len;
269 		src = p;
270 	}
271 	return str;
272 }
273 
str_tabunescape(char * str)274 char *str_tabunescape(char *str)
275 {
276 	char *src = strchr(str, '\001');
277 	if (src == NULL) {
278 		/* no unescaping needed */
279 		return str;
280 	}
281 	return str_tabunescape_from(str, src);
282 }
283 
t_str_tabunescape(const char * str)284 const char *t_str_tabunescape(const char *str)
285 {
286 	const char *p;
287 
288 	p = strchr(str, '\001');
289 	if (p == NULL)
290 		return str;
291 
292 	char *dest = t_strdup_noconst(str);
293 	return str_tabunescape_from(dest, dest + (p - str));
294 }
295 
p_strsplit_tabescaped_inplace(pool_t pool,char * data)296 static char **p_strsplit_tabescaped_inplace(pool_t pool, char *data)
297 {
298 	/* @UNSAFE */
299 	char **array;
300 	unsigned int count, new_alloc_count, alloc_count;
301 
302 	if (*data == '\0')
303 		return p_new(pool, char *, 1);
304 
305 	alloc_count = 32;
306 	array = pool == unsafe_data_stack_pool ?
307 		t_malloc_no0(sizeof(char *) * alloc_count) :
308 		p_malloc(pool, sizeof(char *) * alloc_count);
309 
310 	array[0] = data; count = 1;
311 	char *need_unescape = NULL;
312 	while ((data = strpbrk(data, "\t\001")) != NULL) {
313 		/* separator or escape char found */
314 		if (*data == '\001') {
315 			if (need_unescape == NULL)
316 				need_unescape = data;
317 			data++;
318 			continue;
319 		}
320 		if (count+1 >= alloc_count) {
321 			new_alloc_count = nearest_power(alloc_count+1);
322 			array = p_realloc(pool, array,
323 					  sizeof(char *) * alloc_count,
324 					  sizeof(char *) *
325 					  new_alloc_count);
326 			alloc_count = new_alloc_count;
327 		}
328 		*data++ = '\0';
329 		if (need_unescape != NULL) {
330 			str_tabunescape_from(array[count-1], need_unescape);
331 			need_unescape = NULL;
332 		}
333 		array[count++] = data;
334 	}
335 	if (need_unescape != NULL)
336 		str_tabunescape_from(array[count-1], need_unescape);
337 	i_assert(count < alloc_count);
338 	array[count] = NULL;
339 
340 	return array;
341 }
342 
t_strsplit_tabescaped_inplace(char * data)343 const char *const *t_strsplit_tabescaped_inplace(char *data)
344 {
345 	char *const *escaped =
346 		p_strsplit_tabescaped_inplace(unsafe_data_stack_pool, data);
347 	return (const char *const *)escaped;
348 }
349 
p_strsplit_tabescaped(pool_t pool,const char * str)350 char **p_strsplit_tabescaped(pool_t pool, const char *str)
351 {
352 	return p_strsplit_tabescaped_inplace(pool, p_strdup(pool, str));
353 }
354 
t_strsplit_tabescaped(const char * str)355 const char *const *t_strsplit_tabescaped(const char *str)
356 {
357 	return t_strsplit_tabescaped_inplace(t_strdup_noconst(str));
358 }
359