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