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 = malloc(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 *buffer = newptr;
175 *bufsize *= 2;
176 }
177
178 return GPE_OK;
179 }
180
181 /*
182 * appenddata()
183 *
184 * This appends data from a given source buffer to the end of the used part of
185 * a destination buffer. Arguments relative to the destination buffer are, the
186 * address of a pointer to the destination buffer 'dst_buf', the length of data
187 * in destination buffer excluding potential null string termination 'dst_len',
188 * the allocated size of destination buffer 'dst_alloc'. All three destination
189 * buffer arguments may be modified by this function. Arguments relative to the
190 * source buffer are, a pointer to the source buffer 'src_buf' and indication
191 * whether the source buffer is base64 encoded or not 'src_b64'.
192 *
193 * If the source buffer is indicated to be base64 encoded, this appends the
194 * decoded data, binary or whatever, to the destination. The source buffer
195 * may not hold binary data, only a null terminated string is valid content.
196 *
197 * Destination buffer will be enlarged and relocated as needed.
198 *
199 * Calling function is responsible to provide preallocated destination
200 * buffer and also to deallocate it when no longer needed.
201 *
202 * This function may return:
203 * GPE_OUT_OF_MEMORY
204 * GPE_OK
205 */
206
appenddata(char ** dst_buf,size_t * dst_len,size_t * dst_alloc,char * src_buf,size_t src_len,int src_b64)207 static int appenddata(char **dst_buf, /* dest buffer */
208 size_t *dst_len, /* dest buffer data length */
209 size_t *dst_alloc, /* dest buffer allocated size */
210 char *src_buf, /* source buffer */
211 size_t src_len, /* source buffer length */
212 int src_b64) /* != 0 if source is base64 encoded */
213 {
214 size_t need_alloc = 0;
215
216 if(!src_len)
217 return GPE_OK;
218
219 need_alloc = src_len + *dst_len + 1;
220
221 if(src_b64) {
222 if(src_buf[src_len - 1] == '\r')
223 src_len--;
224
225 if(src_buf[src_len - 1] == '\n')
226 src_len--;
227 }
228
229 /* enlarge destination buffer if required */
230 if(need_alloc > *dst_alloc) {
231 size_t newsize = need_alloc * 2;
232 char *newptr = realloc(*dst_buf, newsize);
233 if(!newptr) {
234 return GPE_OUT_OF_MEMORY;
235 }
236 *dst_alloc = newsize;
237 *dst_buf = newptr;
238 }
239
240 /* memcpy to support binary blobs */
241 memcpy(*dst_buf + *dst_len, src_buf, src_len);
242 *dst_len += src_len;
243 *(*dst_buf + *dst_len) = '\0';
244
245 return GPE_OK;
246 }
247
decodedata(char ** buf,size_t * len)248 static int decodedata(char **buf, /* dest buffer */
249 size_t *len) /* dest buffer data length */
250 {
251 CURLcode error = CURLE_OK;
252 unsigned char *buf64 = NULL;
253 size_t src_len = 0;
254
255 if(!*len)
256 return GPE_OK;
257
258 /* base64 decode the given buffer */
259 error = Curl_base64_decode(*buf, &buf64, &src_len);
260 if(error)
261 return GPE_OUT_OF_MEMORY;
262
263 if(!src_len) {
264 /*
265 ** currently there is no way to tell apart an OOM condition in
266 ** Curl_base64_decode() from zero length decoded data. For now,
267 ** let's just assume it is an OOM condition, currently we have
268 ** no input for this function that decodes to zero length data.
269 */
270 free(buf64);
271
272 return GPE_OUT_OF_MEMORY;
273 }
274
275 /* memcpy to support binary blobs */
276 memcpy(*buf, buf64, src_len);
277 *len = src_len;
278 *(*buf + src_len) = '\0';
279
280 free(buf64);
281
282 return GPE_OK;
283 }
284
285 /*
286 * getpart()
287 *
288 * This returns whole contents of specified XML-like section and subsection
289 * from the given file. This is mostly used to retrieve a specific part from
290 * a test definition file for consumption by test suite servers.
291 *
292 * Data is returned in a dynamically allocated buffer, a pointer to this data
293 * and the size of the data is stored at the addresses that caller specifies.
294 *
295 * If the returned data is a string the returned size will be the length of
296 * the string excluding null termination. Otherwise it will just be the size
297 * of the returned binary data.
298 *
299 * Calling function is responsible to free returned buffer.
300 *
301 * This function may return:
302 * GPE_NO_BUFFER_SPACE
303 * GPE_OUT_OF_MEMORY
304 * GPE_OK
305 */
306
getpart(char ** outbuf,size_t * outlen,const char * main,const char * sub,FILE * stream)307 int getpart(char **outbuf, size_t *outlen,
308 const char *main, const char *sub, FILE *stream)
309 {
310 # define MAX_TAG_LEN 200
311 char couter[MAX_TAG_LEN + 1]; /* current outermost section */
312 char cmain[MAX_TAG_LEN + 1]; /* current main section */
313 char csub[MAX_TAG_LEN + 1]; /* current sub section */
314 char ptag[MAX_TAG_LEN + 1]; /* potential tag */
315 char patt[MAX_TAG_LEN + 1]; /* potential attributes */
316 char *buffer = NULL;
317 char *ptr;
318 char *end;
319 union {
320 ssize_t sig;
321 size_t uns;
322 } len;
323 size_t bufsize = 0;
324 size_t outalloc = 256;
325 size_t datalen;
326 int in_wanted_part = 0;
327 int base64 = 0;
328 int nonewline = 0;
329 int error;
330
331 enum {
332 STATE_OUTSIDE = 0,
333 STATE_OUTER = 1,
334 STATE_INMAIN = 2,
335 STATE_INSUB = 3,
336 STATE_ILLEGAL = 4
337 } state = STATE_OUTSIDE;
338
339 *outlen = 0;
340 *outbuf = malloc(outalloc);
341 if(!*outbuf)
342 return GPE_OUT_OF_MEMORY;
343 *(*outbuf) = '\0';
344
345 couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
346
347 while((error = readline(&buffer, &bufsize, &datalen, stream)) == GPE_OK) {
348
349 ptr = buffer;
350 EAT_SPACE(ptr);
351
352 if('<' != *ptr) {
353 if(in_wanted_part) {
354 show(("=> %s", buffer));
355 error = appenddata(outbuf, outlen, &outalloc, buffer, datalen,
356 base64);
357 if(error)
358 break;
359 }
360 continue;
361 }
362
363 ptr++;
364
365 if('/' == *ptr) {
366 /*
367 ** closing section tag
368 */
369
370 ptr++;
371 end = ptr;
372 EAT_WORD(end);
373 len.sig = end - ptr;
374 if(len.sig > MAX_TAG_LEN) {
375 error = GPE_NO_BUFFER_SPACE;
376 break;
377 }
378 memcpy(ptag, ptr, len.uns);
379 ptag[len.uns] = '\0';
380
381 if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
382 /* end of current sub section */
383 state = STATE_INMAIN;
384 csub[0] = '\0';
385 if(in_wanted_part) {
386 /* end of wanted part */
387 in_wanted_part = 0;
388
389 /* Do we need to base64 decode the data? */
390 if(base64) {
391 error = decodedata(outbuf, outlen);
392 if(error)
393 return error;
394 }
395 if(nonewline)
396 (*outlen)--;
397 break;
398 }
399 }
400 else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
401 /* end of current main section */
402 state = STATE_OUTER;
403 cmain[0] = '\0';
404 if(in_wanted_part) {
405 /* end of wanted part */
406 in_wanted_part = 0;
407
408 /* Do we need to base64 decode the data? */
409 if(base64) {
410 error = decodedata(outbuf, outlen);
411 if(error)
412 return error;
413 }
414 if(nonewline)
415 (*outlen)--;
416 break;
417 }
418 }
419 else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
420 /* end of outermost file section */
421 state = STATE_OUTSIDE;
422 couter[0] = '\0';
423 if(in_wanted_part) {
424 /* end of wanted part */
425 in_wanted_part = 0;
426 break;
427 }
428 }
429
430 }
431 else if(!in_wanted_part) {
432 /*
433 ** opening section tag
434 */
435
436 /* get potential tag */
437 end = ptr;
438 EAT_WORD(end);
439 len.sig = end - ptr;
440 if(len.sig > MAX_TAG_LEN) {
441 error = GPE_NO_BUFFER_SPACE;
442 break;
443 }
444 memcpy(ptag, ptr, len.uns);
445 ptag[len.uns] = '\0';
446
447 /* ignore comments, doctypes and xml declarations */
448 if(('!' == ptag[0]) || ('?' == ptag[0])) {
449 show(("* ignoring (%s)", buffer));
450 continue;
451 }
452
453 /* get all potential attributes */
454 ptr = end;
455 EAT_SPACE(ptr);
456 end = ptr;
457 while(*end && ('>' != *end))
458 end++;
459 len.sig = end - ptr;
460 if(len.sig > MAX_TAG_LEN) {
461 error = GPE_NO_BUFFER_SPACE;
462 break;
463 }
464 memcpy(patt, ptr, len.uns);
465 patt[len.uns] = '\0';
466
467 if(STATE_OUTSIDE == state) {
468 /* outermost element (<testcase>) */
469 strcpy(couter, ptag);
470 state = STATE_OUTER;
471 continue;
472 }
473 else if(STATE_OUTER == state) {
474 /* start of a main section */
475 strcpy(cmain, ptag);
476 state = STATE_INMAIN;
477 continue;
478 }
479 else if(STATE_INMAIN == state) {
480 /* start of a sub section */
481 strcpy(csub, ptag);
482 state = STATE_INSUB;
483 if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
484 /* start of wanted part */
485 in_wanted_part = 1;
486 if(strstr(patt, "base64="))
487 /* bit rough test, but "mostly" functional, */
488 /* treat wanted part data as base64 encoded */
489 base64 = 1;
490 if(strstr(patt, "nonewline=")) {
491 show(("* setting nonewline\n"));
492 nonewline = 1;
493 }
494 }
495 continue;
496 }
497
498 }
499
500 if(in_wanted_part) {
501 show(("=> %s", buffer));
502 error = appenddata(outbuf, outlen, &outalloc, buffer, datalen, base64);
503 if(error)
504 break;
505 }
506
507 } /* while */
508
509 free(buffer);
510
511 if(error != GPE_OK) {
512 if(error == GPE_END_OF_FILE)
513 error = GPE_OK;
514 else {
515 free(*outbuf);
516 *outbuf = NULL;
517 *outlen = 0;
518 }
519 }
520
521 return error;
522 }
523