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