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