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