| /*************************************************************************** | 
 |  *                                  _   _ ____  _ | 
 |  *  Project                     ___| | | |  _ \| | | 
 |  *                             / __| | | | |_) | | | 
 |  *                            | (__| |_| |  _ <| |___ | 
 |  *                             \___|\___/|_| \_\_____| | 
 |  * | 
 |  * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al. | 
 |  * | 
 |  * This software is licensed as described in the file COPYING, which | 
 |  * you should have received as part of this distribution. The terms | 
 |  * are also available at https://curl.haxx.se/docs/copyright.html. | 
 |  * | 
 |  * You may opt to use, copy, modify, merge, publish, distribute and/or sell | 
 |  * copies of the Software, and permit persons to whom the Software is | 
 |  * furnished to do so, under the terms of the COPYING file. | 
 |  * | 
 |  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
 |  * KIND, either express or implied. | 
 |  * | 
 |  ***************************************************************************/ | 
 | #include "server_setup.h" | 
 |  | 
 | #include "getpart.h" | 
 |  | 
 | #define ENABLE_CURLX_PRINTF | 
 | /* make the curlx header define all printf() functions to use the curlx_* | 
 |    versions instead */ | 
 | #include "curlx.h" /* from the private lib dir */ | 
 |  | 
 | /* just to please curl_base64.h we create a fake struct */ | 
 | struct Curl_easy { | 
 |   int fake; | 
 | }; | 
 |  | 
 | #include "curl_base64.h" | 
 | #include "curl_memory.h" | 
 |  | 
 | /* include memdebug.h last */ | 
 | #include "memdebug.h" | 
 |  | 
 | #define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++ | 
 |  | 
 | #define EAT_WORD(p)  while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++ | 
 |  | 
 | #ifdef DEBUG_GETPART | 
 | #define show(x) printf x | 
 | #else | 
 | #define show(x) Curl_nop_stmt | 
 | #endif | 
 |  | 
 | #if defined(_MSC_VER) && defined(_DLL) | 
 | #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */ | 
 | #endif | 
 |  | 
 | curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; | 
 | curl_free_callback Curl_cfree = (curl_free_callback)free; | 
 | curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; | 
 | curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup; | 
 | curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; | 
 | #if defined(WIN32) && defined(UNICODE) | 
 | curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; | 
 | #endif | 
 |  | 
 | #if defined(_MSC_VER) && defined(_DLL) | 
 | #  pragma warning(default:4232) /* MSVC extension, dllimport identity */ | 
 | #endif | 
 |  | 
 |  | 
 | /* | 
 |  * Curl_convert_clone() returns a malloced copy of the source string (if | 
 |  * returning CURLE_OK), with the data converted to network format. This | 
 |  * function is used by base64 code in libcurl built to support data | 
 |  * conversion. This is a DUMMY VERSION that returns data unmodified - for | 
 |  * use by the test server only. | 
 |  */ | 
 | CURLcode Curl_convert_clone(struct Curl_easy *data, | 
 |                             const char *indata, | 
 |                             size_t insize, | 
 |                             char **outbuf); | 
 | CURLcode Curl_convert_clone(struct Curl_easy *data, | 
 |                             const char *indata, | 
 |                             size_t insize, | 
 |                             char **outbuf) | 
 | { | 
 |   char *convbuf; | 
 |   (void)data; | 
 |  | 
 |   convbuf = malloc(insize); | 
 |   if(!convbuf) | 
 |     return CURLE_OUT_OF_MEMORY; | 
 |  | 
 |   memcpy(convbuf, indata, insize); | 
 |   *outbuf = convbuf; | 
 |   return CURLE_OK; | 
 | } | 
 |  | 
 | /* | 
 |  * readline() | 
 |  * | 
 |  * Reads a complete line from a file into a dynamically allocated buffer. | 
 |  * | 
 |  * Calling function may call this multiple times with same 'buffer' | 
 |  * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer | 
 |  * will be reallocated and 'bufsize' increased until whole line fits in | 
 |  * buffer before returning it. | 
 |  * | 
 |  * Calling function is responsible to free allocated buffer. | 
 |  * | 
 |  * This function may return: | 
 |  *   GPE_OUT_OF_MEMORY | 
 |  *   GPE_END_OF_FILE | 
 |  *   GPE_OK | 
 |  */ | 
 |  | 
 | static int readline(char **buffer, size_t *bufsize, FILE *stream) | 
 | { | 
 |   size_t offset = 0; | 
 |   char *newptr; | 
 |  | 
 |   if(!*buffer) { | 
 |     *buffer = malloc(128); | 
 |     if(!*buffer) | 
 |       return GPE_OUT_OF_MEMORY; | 
 |     *bufsize = 128; | 
 |   } | 
 |  | 
 |   for(;;) { | 
 |     size_t length; | 
 |     int bytestoread = curlx_uztosi(*bufsize - offset); | 
 |  | 
 |     if(!fgets(*buffer + offset, bytestoread, stream)) | 
 |       return (offset != 0) ? GPE_OK : GPE_END_OF_FILE; | 
 |  | 
 |     length = offset + strlen(*buffer + offset); | 
 |     if(*(*buffer + length - 1) == '\n') | 
 |       break; | 
 |     offset = length; | 
 |     if(length < *bufsize - 1) | 
 |       continue; | 
 |  | 
 |     newptr = realloc(*buffer, *bufsize * 2); | 
 |     if(!newptr) | 
 |       return GPE_OUT_OF_MEMORY; | 
 |     *buffer = newptr; | 
 |     *bufsize *= 2; | 
 |   } | 
 |  | 
 |   return GPE_OK; | 
 | } | 
 |  | 
 | /* | 
 |  * appenddata() | 
 |  * | 
 |  * This appends data from a given source buffer to the end of the used part of | 
 |  * a destination buffer. Arguments relative to the destination buffer are, the | 
 |  * address of a pointer to the destination buffer 'dst_buf', the length of data | 
 |  * in destination buffer excluding potential null string termination 'dst_len', | 
 |  * the allocated size of destination buffer 'dst_alloc'. All three destination | 
 |  * buffer arguments may be modified by this function. Arguments relative to the | 
 |  * source buffer are, a pointer to the source buffer 'src_buf' and indication | 
 |  * whether the source buffer is base64 encoded or not 'src_b64'. | 
 |  * | 
 |  * If the source buffer is indicated to be base64 encoded, this appends the | 
 |  * decoded data, binary or whatever, to the destination. The source buffer | 
 |  * may not hold binary data, only a null terminated string is valid content. | 
 |  * | 
 |  * Destination buffer will be enlarged and relocated as needed. | 
 |  * | 
 |  * Calling function is responsible to provide preallocated destination | 
 |  * buffer and also to deallocate it when no longer needed. | 
 |  * | 
 |  * This function may return: | 
 |  *   GPE_OUT_OF_MEMORY | 
 |  *   GPE_OK | 
 |  */ | 
 |  | 
 | static int appenddata(char  **dst_buf,   /* dest buffer */ | 
 |                       size_t *dst_len,   /* dest buffer data length */ | 
 |                       size_t *dst_alloc, /* dest buffer allocated size */ | 
 |                       char   *src_buf,   /* source buffer */ | 
 |                       int     src_b64)   /* != 0 if source is base64 encoded */ | 
 | { | 
 |   size_t need_alloc = 0; | 
 |   size_t src_len = strlen(src_buf); | 
 |  | 
 |   if(!src_len) | 
 |     return GPE_OK; | 
 |  | 
 |   need_alloc = src_len + *dst_len + 1; | 
 |  | 
 |   if(src_b64) { | 
 |     if(src_buf[src_len - 1] == '\r') | 
 |       src_len--; | 
 |  | 
 |     if(src_buf[src_len - 1] == '\n') | 
 |       src_len--; | 
 |   } | 
 |  | 
 |   /* enlarge destination buffer if required */ | 
 |   if(need_alloc > *dst_alloc) { | 
 |     size_t newsize = need_alloc * 2; | 
 |     char *newptr = realloc(*dst_buf, newsize); | 
 |     if(!newptr) { | 
 |       return GPE_OUT_OF_MEMORY; | 
 |     } | 
 |     *dst_alloc = newsize; | 
 |     *dst_buf = newptr; | 
 |   } | 
 |  | 
 |   /* memcpy to support binary blobs */ | 
 |   memcpy(*dst_buf + *dst_len, src_buf, src_len); | 
 |   *dst_len += src_len; | 
 |   *(*dst_buf + *dst_len) = '\0'; | 
 |  | 
 |   return GPE_OK; | 
 | } | 
 |  | 
 | static int decodedata(char  **buf,   /* dest buffer */ | 
 |                       size_t *len)   /* dest buffer data length */ | 
 | { | 
 |   CURLcode error = CURLE_OK; | 
 |   unsigned char *buf64 = NULL; | 
 |   size_t src_len = 0; | 
 |  | 
 |   if(!*len) | 
 |     return GPE_OK; | 
 |  | 
 |   /* base64 decode the given buffer */ | 
 |   error = Curl_base64_decode(*buf, &buf64, &src_len); | 
 |   if(error) | 
 |     return GPE_OUT_OF_MEMORY; | 
 |  | 
 |   if(!src_len) { | 
 |     /* | 
 |     ** currently there is no way to tell apart an OOM condition in | 
 |     ** Curl_base64_decode() from zero length decoded data. For now, | 
 |     ** let's just assume it is an OOM condition, currently we have | 
 |     ** no input for this function that decodes to zero length data. | 
 |     */ | 
 |     free(buf64); | 
 |  | 
 |     return GPE_OUT_OF_MEMORY; | 
 |   } | 
 |  | 
 |   /* memcpy to support binary blobs */ | 
 |   memcpy(*buf, buf64, src_len); | 
 |   *len = src_len; | 
 |   *(*buf + src_len) = '\0'; | 
 |  | 
 |   free(buf64); | 
 |  | 
 |   return GPE_OK; | 
 | } | 
 |  | 
 | /* | 
 |  * getpart() | 
 |  * | 
 |  * This returns whole contents of specified XML-like section and subsection | 
 |  * from the given file. This is mostly used to retrieve a specific part from | 
 |  * a test definition file for consumption by test suite servers. | 
 |  * | 
 |  * Data is returned in a dynamically allocated buffer, a pointer to this data | 
 |  * and the size of the data is stored at the addresses that caller specifies. | 
 |  * | 
 |  * If the returned data is a string the returned size will be the length of | 
 |  * the string excluding null termination. Otherwise it will just be the size | 
 |  * of the returned binary data. | 
 |  * | 
 |  * Calling function is responsible to free returned buffer. | 
 |  * | 
 |  * This function may return: | 
 |  *   GPE_NO_BUFFER_SPACE | 
 |  *   GPE_OUT_OF_MEMORY | 
 |  *   GPE_OK | 
 |  */ | 
 |  | 
 | int getpart(char **outbuf, size_t *outlen, | 
 |             const char *main, const char *sub, FILE *stream) | 
 | { | 
 | # define MAX_TAG_LEN 79 | 
 |   char couter[MAX_TAG_LEN + 1]; /* current outermost section */ | 
 |   char cmain[MAX_TAG_LEN + 1];  /* current main section */ | 
 |   char csub[MAX_TAG_LEN + 1];   /* current sub section */ | 
 |   char ptag[MAX_TAG_LEN + 1];   /* potential tag */ | 
 |   char patt[MAX_TAG_LEN + 1];   /* potential attributes */ | 
 |   char *buffer = NULL; | 
 |   char *ptr; | 
 |   char *end; | 
 |   union { | 
 |     ssize_t sig; | 
 |      size_t uns; | 
 |   } len; | 
 |   size_t bufsize = 0; | 
 |   size_t outalloc = 256; | 
 |   int in_wanted_part = 0; | 
 |   int base64 = 0; | 
 |   int error; | 
 |  | 
 |   enum { | 
 |     STATE_OUTSIDE = 0, | 
 |     STATE_OUTER   = 1, | 
 |     STATE_INMAIN  = 2, | 
 |     STATE_INSUB   = 3, | 
 |     STATE_ILLEGAL = 4 | 
 |   } state = STATE_OUTSIDE; | 
 |  | 
 |   *outlen = 0; | 
 |   *outbuf = malloc(outalloc); | 
 |   if(!*outbuf) | 
 |     return GPE_OUT_OF_MEMORY; | 
 |   *(*outbuf) = '\0'; | 
 |  | 
 |   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0'; | 
 |  | 
 |   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) { | 
 |  | 
 |     ptr = buffer; | 
 |     EAT_SPACE(ptr); | 
 |  | 
 |     if('<' != *ptr) { | 
 |       if(in_wanted_part) { | 
 |         show(("=> %s", buffer)); | 
 |         error = appenddata(outbuf, outlen, &outalloc, buffer, base64); | 
 |         if(error) | 
 |           break; | 
 |       } | 
 |       continue; | 
 |     } | 
 |  | 
 |     ptr++; | 
 |  | 
 |     if('/' == *ptr) { | 
 |       /* | 
 |       ** closing section tag | 
 |       */ | 
 |  | 
 |       ptr++; | 
 |       end = ptr; | 
 |       EAT_WORD(end); | 
 |       len.sig = end - ptr; | 
 |       if(len.sig > MAX_TAG_LEN) { | 
 |         error = GPE_NO_BUFFER_SPACE; | 
 |         break; | 
 |       } | 
 |       memcpy(ptag, ptr, len.uns); | 
 |       ptag[len.uns] = '\0'; | 
 |  | 
 |       if((STATE_INSUB == state) && !strcmp(csub, ptag)) { | 
 |         /* end of current sub section */ | 
 |         state = STATE_INMAIN; | 
 |         csub[0] = '\0'; | 
 |         if(in_wanted_part) { | 
 |           /* end of wanted part */ | 
 |           in_wanted_part = 0; | 
 |  | 
 |           /* Do we need to base64 decode the data? */ | 
 |           if(base64) { | 
 |             error = decodedata(outbuf, outlen); | 
 |             if(error) | 
 |               return error; | 
 |           } | 
 |           break; | 
 |         } | 
 |       } | 
 |       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) { | 
 |         /* end of current main section */ | 
 |         state = STATE_OUTER; | 
 |         cmain[0] = '\0'; | 
 |         if(in_wanted_part) { | 
 |           /* end of wanted part */ | 
 |           in_wanted_part = 0; | 
 |  | 
 |           /* Do we need to base64 decode the data? */ | 
 |           if(base64) { | 
 |             error = decodedata(outbuf, outlen); | 
 |             if(error) | 
 |               return error; | 
 |           } | 
 |           break; | 
 |         } | 
 |       } | 
 |       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) { | 
 |         /* end of outermost file section */ | 
 |         state = STATE_OUTSIDE; | 
 |         couter[0] = '\0'; | 
 |         if(in_wanted_part) { | 
 |           /* end of wanted part */ | 
 |           in_wanted_part = 0; | 
 |           break; | 
 |         } | 
 |       } | 
 |  | 
 |     } | 
 |     else if(!in_wanted_part) { | 
 |       /* | 
 |       ** opening section tag | 
 |       */ | 
 |  | 
 |       /* get potential tag */ | 
 |       end = ptr; | 
 |       EAT_WORD(end); | 
 |       len.sig = end - ptr; | 
 |       if(len.sig > MAX_TAG_LEN) { | 
 |         error = GPE_NO_BUFFER_SPACE; | 
 |         break; | 
 |       } | 
 |       memcpy(ptag, ptr, len.uns); | 
 |       ptag[len.uns] = '\0'; | 
 |  | 
 |       /* ignore comments, doctypes and xml declarations */ | 
 |       if(('!' == ptag[0]) || ('?' == ptag[0])) { | 
 |         show(("* ignoring (%s)", buffer)); | 
 |         continue; | 
 |       } | 
 |  | 
 |       /* get all potential attributes */ | 
 |       ptr = end; | 
 |       EAT_SPACE(ptr); | 
 |       end = ptr; | 
 |       while(*end && ('>' != *end)) | 
 |         end++; | 
 |       len.sig = end - ptr; | 
 |       if(len.sig > MAX_TAG_LEN) { | 
 |         error = GPE_NO_BUFFER_SPACE; | 
 |         break; | 
 |       } | 
 |       memcpy(patt, ptr, len.uns); | 
 |       patt[len.uns] = '\0'; | 
 |  | 
 |       if(STATE_OUTSIDE == state) { | 
 |         /* outermost element (<testcase>) */ | 
 |         strcpy(couter, ptag); | 
 |         state = STATE_OUTER; | 
 |         continue; | 
 |       } | 
 |       else if(STATE_OUTER == state) { | 
 |         /* start of a main section */ | 
 |         strcpy(cmain, ptag); | 
 |         state = STATE_INMAIN; | 
 |         continue; | 
 |       } | 
 |       else if(STATE_INMAIN == state) { | 
 |         /* start of a sub section */ | 
 |         strcpy(csub, ptag); | 
 |         state = STATE_INSUB; | 
 |         if(!strcmp(cmain, main) && !strcmp(csub, sub)) { | 
 |           /* start of wanted part */ | 
 |           in_wanted_part = 1; | 
 |           if(strstr(patt, "base64=")) | 
 |               /* bit rough test, but "mostly" functional, */ | 
 |               /* treat wanted part data as base64 encoded */ | 
 |               base64 = 1; | 
 |         } | 
 |         continue; | 
 |       } | 
 |  | 
 |     } | 
 |  | 
 |     if(in_wanted_part) { | 
 |       show(("=> %s", buffer)); | 
 |       error = appenddata(outbuf, outlen, &outalloc, buffer, base64); | 
 |       if(error) | 
 |         break; | 
 |     } | 
 |  | 
 |   } /* while */ | 
 |  | 
 |   free(buffer); | 
 |  | 
 |   if(error != GPE_OK) { | 
 |     if(error == GPE_END_OF_FILE) | 
 |       error = GPE_OK; | 
 |     else { | 
 |       free(*outbuf); | 
 |       *outbuf = NULL; | 
 |       *outlen = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   return error; | 
 | } |