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 */
32struct 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
56curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
57curl_free_callback Curl_cfree = (curl_free_callback)free;
58curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
59curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup;
60curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
61#if defined(WIN32) && defined(UNICODE)
62curl_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 */
77CURLcode Curl_convert_clone(struct Curl_easy *data,
78 const char *indata,
79 size_t insize,
80 char **outbuf);
81CURLcode 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
116static 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
178static 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
219static 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
278int 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