1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) 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.haxx.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 79 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