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