1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "server_setup.h" |
23 | |
24 | #include "getpart.h" |
25 | |
26 | #define ENABLE_CURLX_PRINTF |
27 | /* make the curlx header define all printf() functions to use the curlx_* |
28 | versions instead */ |
29 | #include "curlx.h" /* from the private lib dir */ |
30 | |
31 | /* just to please curl_base64.h we create a fake struct */ |
32 | struct Curl_easy { |
33 | int fake; |
34 | }; |
35 | |
36 | #include "curl_base64.h" |
37 | #include "curl_memory.h" |
38 | |
39 | /* include memdebug.h last */ |
40 | #include "memdebug.h" |
41 | |
42 | #define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++ |
43 | |
44 | #define EAT_WORD(p) while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++ |
45 | |
46 | #ifdef DEBUG_GETPART |
47 | #define show(x) printf x |
48 | #else |
49 | #define show(x) Curl_nop_stmt |
50 | #endif |
51 | |
52 | #if defined(_MSC_VER) && defined(_DLL) |
53 | # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ |
54 | #endif |
55 | |
56 | curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; |
57 | curl_free_callback Curl_cfree = (curl_free_callback)free; |
58 | curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; |
59 | curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup; |
60 | curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; |
61 | #if defined(WIN32) && defined(UNICODE) |
62 | curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; |
63 | #endif |
64 | |
65 | #if defined(_MSC_VER) && defined(_DLL) |
66 | # pragma warning(default:4232) /* MSVC extension, dllimport identity */ |
67 | #endif |
68 | |
69 | |
70 | /* |
71 | * Curl_convert_clone() returns a malloced copy of the source string (if |
72 | * returning CURLE_OK), with the data converted to network format. This |
73 | * function is used by base64 code in libcurl built to support data |
74 | * conversion. This is a DUMMY VERSION that returns data unmodified - for |
75 | * use by the test server only. |
76 | */ |
77 | CURLcode Curl_convert_clone(struct Curl_easy *data, |
78 | const char *indata, |
79 | size_t insize, |
80 | char **outbuf); |
81 | CURLcode Curl_convert_clone(struct Curl_easy *data, |
82 | const char *indata, |
83 | size_t insize, |
84 | char **outbuf) |
85 | { |
86 | char *convbuf; |
87 | (void)data; |
88 | |
89 | convbuf = malloc(insize); |
90 | if(!convbuf) |
91 | return CURLE_OUT_OF_MEMORY; |
92 | |
93 | memcpy(convbuf, indata, insize); |
94 | *outbuf = convbuf; |
95 | return CURLE_OK; |
96 | } |
97 | |
98 | /* |
99 | * readline() |
100 | * |
101 | * Reads a complete line from a file into a dynamically allocated buffer. |
102 | * |
103 | * Calling function may call this multiple times with same 'buffer' |
104 | * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer |
105 | * will be reallocated and 'bufsize' increased until whole line fits in |
106 | * buffer before returning it. |
107 | * |
108 | * Calling function is responsible to free allocated buffer. |
109 | * |
110 | * This function may return: |
111 | * GPE_OUT_OF_MEMORY |
112 | * GPE_END_OF_FILE |
113 | * GPE_OK |
114 | */ |
115 | |
116 | static int readline(char **buffer, size_t *bufsize, FILE *stream) |
117 | { |
118 | size_t offset = 0; |
119 | char *newptr; |
120 | |
121 | if(!*buffer) { |
122 | *buffer = malloc(128); |
123 | if(!*buffer) |
124 | return GPE_OUT_OF_MEMORY; |
125 | *bufsize = 128; |
126 | } |
127 | |
128 | for(;;) { |
129 | size_t length; |
130 | int bytestoread = curlx_uztosi(*bufsize - offset); |
131 | |
132 | if(!fgets(*buffer + offset, bytestoread, stream)) |
133 | return (offset != 0) ? GPE_OK : GPE_END_OF_FILE; |
134 | |
135 | length = offset + strlen(*buffer + offset); |
136 | if(*(*buffer + length - 1) == '\n') |
137 | break; |
138 | offset = length; |
139 | if(length < *bufsize - 1) |
140 | continue; |
141 | |
142 | newptr = realloc(*buffer, *bufsize * 2); |
143 | if(!newptr) |
144 | return GPE_OUT_OF_MEMORY; |
145 | *buffer = newptr; |
146 | *bufsize *= 2; |
147 | } |
148 | |
149 | return GPE_OK; |
150 | } |
151 | |
152 | /* |
153 | * appenddata() |
154 | * |
155 | * This appends data from a given source buffer to the end of the used part of |
156 | * a destination buffer. Arguments relative to the destination buffer are, the |
157 | * address of a pointer to the destination buffer 'dst_buf', the length of data |
158 | * in destination buffer excluding potential null string termination 'dst_len', |
159 | * the allocated size of destination buffer 'dst_alloc'. All three destination |
160 | * buffer arguments may be modified by this function. Arguments relative to the |
161 | * source buffer are, a pointer to the source buffer 'src_buf' and indication |
162 | * whether the source buffer is base64 encoded or not 'src_b64'. |
163 | * |
164 | * If the source buffer is indicated to be base64 encoded, this appends the |
165 | * decoded data, binary or whatever, to the destination. The source buffer |
166 | * may not hold binary data, only a null terminated string is valid content. |
167 | * |
168 | * Destination buffer will be enlarged and relocated as needed. |
169 | * |
170 | * Calling function is responsible to provide preallocated destination |
171 | * buffer and also to deallocate it when no longer needed. |
172 | * |
173 | * This function may return: |
174 | * GPE_OUT_OF_MEMORY |
175 | * GPE_OK |
176 | */ |
177 | |
178 | static int appenddata(char **dst_buf, /* dest buffer */ |
179 | size_t *dst_len, /* dest buffer data length */ |
180 | size_t *dst_alloc, /* dest buffer allocated size */ |
181 | char *src_buf, /* source buffer */ |
182 | int src_b64) /* != 0 if source is base64 encoded */ |
183 | { |
184 | size_t need_alloc = 0; |
185 | size_t src_len = strlen(src_buf); |
186 | |
187 | if(!src_len) |
188 | return GPE_OK; |
189 | |
190 | need_alloc = src_len + *dst_len + 1; |
191 | |
192 | if(src_b64) { |
193 | if(src_buf[src_len - 1] == '\r') |
194 | src_len--; |
195 | |
196 | if(src_buf[src_len - 1] == '\n') |
197 | src_len--; |
198 | } |
199 | |
200 | /* enlarge destination buffer if required */ |
201 | if(need_alloc > *dst_alloc) { |
202 | size_t newsize = need_alloc * 2; |
203 | char *newptr = realloc(*dst_buf, newsize); |
204 | if(!newptr) { |
205 | return GPE_OUT_OF_MEMORY; |
206 | } |
207 | *dst_alloc = newsize; |
208 | *dst_buf = newptr; |
209 | } |
210 | |
211 | /* memcpy to support binary blobs */ |
212 | memcpy(*dst_buf + *dst_len, src_buf, src_len); |
213 | *dst_len += src_len; |
214 | *(*dst_buf + *dst_len) = '\0'; |
215 | |
216 | return GPE_OK; |
217 | } |
218 | |
219 | static int decodedata(char **buf, /* dest buffer */ |
220 | size_t *len) /* dest buffer data length */ |
221 | { |
222 | CURLcode error = CURLE_OK; |
223 | unsigned char *buf64 = NULL; |
224 | size_t src_len = 0; |
225 | |
226 | if(!*len) |
227 | return GPE_OK; |
228 | |
229 | /* base64 decode the given buffer */ |
230 | error = Curl_base64_decode(*buf, &buf64, &src_len); |
231 | if(error) |
232 | return GPE_OUT_OF_MEMORY; |
233 | |
234 | if(!src_len) { |
235 | /* |
236 | ** currently there is no way to tell apart an OOM condition in |
237 | ** Curl_base64_decode() from zero length decoded data. For now, |
238 | ** let's just assume it is an OOM condition, currently we have |
239 | ** no input for this function that decodes to zero length data. |
240 | */ |
241 | free(buf64); |
242 | |
243 | return GPE_OUT_OF_MEMORY; |
244 | } |
245 | |
246 | /* memcpy to support binary blobs */ |
247 | memcpy(*buf, buf64, src_len); |
248 | *len = src_len; |
249 | *(*buf + src_len) = '\0'; |
250 | |
251 | free(buf64); |
252 | |
253 | return GPE_OK; |
254 | } |
255 | |
256 | /* |
257 | * getpart() |
258 | * |
259 | * This returns whole contents of specified XML-like section and subsection |
260 | * from the given file. This is mostly used to retrieve a specific part from |
261 | * a test definition file for consumption by test suite servers. |
262 | * |
263 | * Data is returned in a dynamically allocated buffer, a pointer to this data |
264 | * and the size of the data is stored at the addresses that caller specifies. |
265 | * |
266 | * If the returned data is a string the returned size will be the length of |
267 | * the string excluding null termination. Otherwise it will just be the size |
268 | * of the returned binary data. |
269 | * |
270 | * Calling function is responsible to free returned buffer. |
271 | * |
272 | * This function may return: |
273 | * GPE_NO_BUFFER_SPACE |
274 | * GPE_OUT_OF_MEMORY |
275 | * GPE_OK |
276 | */ |
277 | |
278 | int getpart(char **outbuf, size_t *outlen, |
279 | const char *main, const char *sub, FILE *stream) |
280 | { |
281 | # define MAX_TAG_LEN 200 |
282 | char couter[MAX_TAG_LEN + 1]; /* current outermost section */ |
283 | char cmain[MAX_TAG_LEN + 1]; /* current main section */ |
284 | char csub[MAX_TAG_LEN + 1]; /* current sub section */ |
285 | char ptag[MAX_TAG_LEN + 1]; /* potential tag */ |
286 | char patt[MAX_TAG_LEN + 1]; /* potential attributes */ |
287 | char *buffer = NULL; |
288 | char *ptr; |
289 | char *end; |
290 | union { |
291 | ssize_t sig; |
292 | size_t uns; |
293 | } len; |
294 | size_t bufsize = 0; |
295 | size_t outalloc = 256; |
296 | int in_wanted_part = 0; |
297 | int base64 = 0; |
298 | int error; |
299 | |
300 | enum { |
301 | STATE_OUTSIDE = 0, |
302 | STATE_OUTER = 1, |
303 | STATE_INMAIN = 2, |
304 | STATE_INSUB = 3, |
305 | STATE_ILLEGAL = 4 |
306 | } state = STATE_OUTSIDE; |
307 | |
308 | *outlen = 0; |
309 | *outbuf = malloc(outalloc); |
310 | if(!*outbuf) |
311 | return GPE_OUT_OF_MEMORY; |
312 | *(*outbuf) = '\0'; |
313 | |
314 | couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0'; |
315 | |
316 | while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) { |
317 | |
318 | ptr = buffer; |
319 | EAT_SPACE(ptr); |
320 | |
321 | if('<' != *ptr) { |
322 | if(in_wanted_part) { |
323 | show(("=> %s" , buffer)); |
324 | error = appenddata(outbuf, outlen, &outalloc, buffer, base64); |
325 | if(error) |
326 | break; |
327 | } |
328 | continue; |
329 | } |
330 | |
331 | ptr++; |
332 | |
333 | if('/' == *ptr) { |
334 | /* |
335 | ** closing section tag |
336 | */ |
337 | |
338 | ptr++; |
339 | end = ptr; |
340 | EAT_WORD(end); |
341 | len.sig = end - ptr; |
342 | if(len.sig > MAX_TAG_LEN) { |
343 | error = GPE_NO_BUFFER_SPACE; |
344 | break; |
345 | } |
346 | memcpy(ptag, ptr, len.uns); |
347 | ptag[len.uns] = '\0'; |
348 | |
349 | if((STATE_INSUB == state) && !strcmp(csub, ptag)) { |
350 | /* end of current sub section */ |
351 | state = STATE_INMAIN; |
352 | csub[0] = '\0'; |
353 | if(in_wanted_part) { |
354 | /* end of wanted part */ |
355 | in_wanted_part = 0; |
356 | |
357 | /* Do we need to base64 decode the data? */ |
358 | if(base64) { |
359 | error = decodedata(outbuf, outlen); |
360 | if(error) |
361 | return error; |
362 | } |
363 | break; |
364 | } |
365 | } |
366 | else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) { |
367 | /* end of current main section */ |
368 | state = STATE_OUTER; |
369 | cmain[0] = '\0'; |
370 | if(in_wanted_part) { |
371 | /* end of wanted part */ |
372 | in_wanted_part = 0; |
373 | |
374 | /* Do we need to base64 decode the data? */ |
375 | if(base64) { |
376 | error = decodedata(outbuf, outlen); |
377 | if(error) |
378 | return error; |
379 | } |
380 | break; |
381 | } |
382 | } |
383 | else if((STATE_OUTER == state) && !strcmp(couter, ptag)) { |
384 | /* end of outermost file section */ |
385 | state = STATE_OUTSIDE; |
386 | couter[0] = '\0'; |
387 | if(in_wanted_part) { |
388 | /* end of wanted part */ |
389 | in_wanted_part = 0; |
390 | break; |
391 | } |
392 | } |
393 | |
394 | } |
395 | else if(!in_wanted_part) { |
396 | /* |
397 | ** opening section tag |
398 | */ |
399 | |
400 | /* get potential tag */ |
401 | end = ptr; |
402 | EAT_WORD(end); |
403 | len.sig = end - ptr; |
404 | if(len.sig > MAX_TAG_LEN) { |
405 | error = GPE_NO_BUFFER_SPACE; |
406 | break; |
407 | } |
408 | memcpy(ptag, ptr, len.uns); |
409 | ptag[len.uns] = '\0'; |
410 | |
411 | /* ignore comments, doctypes and xml declarations */ |
412 | if(('!' == ptag[0]) || ('?' == ptag[0])) { |
413 | show(("* ignoring (%s)" , buffer)); |
414 | continue; |
415 | } |
416 | |
417 | /* get all potential attributes */ |
418 | ptr = end; |
419 | EAT_SPACE(ptr); |
420 | end = ptr; |
421 | while(*end && ('>' != *end)) |
422 | end++; |
423 | len.sig = end - ptr; |
424 | if(len.sig > MAX_TAG_LEN) { |
425 | error = GPE_NO_BUFFER_SPACE; |
426 | break; |
427 | } |
428 | memcpy(patt, ptr, len.uns); |
429 | patt[len.uns] = '\0'; |
430 | |
431 | if(STATE_OUTSIDE == state) { |
432 | /* outermost element (<testcase>) */ |
433 | strcpy(couter, ptag); |
434 | state = STATE_OUTER; |
435 | continue; |
436 | } |
437 | else if(STATE_OUTER == state) { |
438 | /* start of a main section */ |
439 | strcpy(cmain, ptag); |
440 | state = STATE_INMAIN; |
441 | continue; |
442 | } |
443 | else if(STATE_INMAIN == state) { |
444 | /* start of a sub section */ |
445 | strcpy(csub, ptag); |
446 | state = STATE_INSUB; |
447 | if(!strcmp(cmain, main) && !strcmp(csub, sub)) { |
448 | /* start of wanted part */ |
449 | in_wanted_part = 1; |
450 | if(strstr(patt, "base64=" )) |
451 | /* bit rough test, but "mostly" functional, */ |
452 | /* treat wanted part data as base64 encoded */ |
453 | base64 = 1; |
454 | } |
455 | continue; |
456 | } |
457 | |
458 | } |
459 | |
460 | if(in_wanted_part) { |
461 | show(("=> %s" , buffer)); |
462 | error = appenddata(outbuf, outlen, &outalloc, buffer, base64); |
463 | if(error) |
464 | break; |
465 | } |
466 | |
467 | } /* while */ |
468 | |
469 | free(buffer); |
470 | |
471 | if(error != GPE_OK) { |
472 | if(error == GPE_END_OF_FILE) |
473 | error = GPE_OK; |
474 | else { |
475 | free(*outbuf); |
476 | *outbuf = NULL; |
477 | *outlen = 0; |
478 | } |
479 | } |
480 | |
481 | return error; |
482 | } |
483 | |