Home | History | Annotate | Download | only in server
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, 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  * 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 
     87 static 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 
    149 static 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 
    190 static 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     free(buf64);
    213 
    214     return GPE_OUT_OF_MEMORY;
    215   }
    216 
    217   /* memcpy to support binary blobs */
    218   memcpy(*buf, buf64, src_len);
    219   *len = src_len;
    220   *(*buf + src_len) = '\0';
    221 
    222   free(buf64);
    223 
    224   return GPE_OK;
    225 }
    226 
    227 /*
    228  * getpart()
    229  *
    230  * This returns whole contents of specified XML-like section and subsection
    231  * from the given file. This is mostly used to retrieve a specific part from
    232  * a test definition file for consumption by test suite servers.
    233  *
    234  * Data is returned in a dynamically allocated buffer, a pointer to this data
    235  * and the size of the data is stored at the addresses that caller specifies.
    236  *
    237  * If the returned data is a string the returned size will be the length of
    238  * the string excluding null termination. Otherwise it will just be the size
    239  * of the returned binary data.
    240  *
    241  * Calling function is responsible to free returned buffer.
    242  *
    243  * This function may return:
    244  *   GPE_NO_BUFFER_SPACE
    245  *   GPE_OUT_OF_MEMORY
    246  *   GPE_OK
    247  */
    248 
    249 int getpart(char **outbuf, size_t *outlen,
    250             const char *main, const char *sub, FILE *stream)
    251 {
    252 # define MAX_TAG_LEN 79
    253   char couter[MAX_TAG_LEN+1]; /* current outermost section */
    254   char cmain[MAX_TAG_LEN+1];  /* current main section */
    255   char csub[MAX_TAG_LEN+1];   /* current sub section */
    256   char ptag[MAX_TAG_LEN+1];   /* potential tag */
    257   char patt[MAX_TAG_LEN+1];   /* potential attributes */
    258   char *buffer = NULL;
    259   char *ptr;
    260   char *end;
    261   union {
    262     ssize_t sig;
    263      size_t uns;
    264   } len;
    265   size_t bufsize = 0;
    266   size_t outalloc = 256;
    267   int in_wanted_part = 0;
    268   int base64 = 0;
    269   int error;
    270 
    271   enum {
    272     STATE_OUTSIDE = 0,
    273     STATE_OUTER   = 1,
    274     STATE_INMAIN  = 2,
    275     STATE_INSUB   = 3,
    276     STATE_ILLEGAL = 4
    277   } state = STATE_OUTSIDE;
    278 
    279   *outlen = 0;
    280   *outbuf = malloc(outalloc);
    281   if(!*outbuf)
    282     return GPE_OUT_OF_MEMORY;
    283   *(*outbuf) = '\0';
    284 
    285   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
    286 
    287   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
    288 
    289     ptr = buffer;
    290     EAT_SPACE(ptr);
    291 
    292     if('<' != *ptr) {
    293       if(in_wanted_part) {
    294         show(("=> %s", buffer));
    295         error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
    296         if(error)
    297           break;
    298       }
    299       continue;
    300     }
    301 
    302     ptr++;
    303 
    304     if('/' == *ptr) {
    305       /*
    306       ** closing section tag
    307       */
    308 
    309       ptr++;
    310       end = ptr;
    311       EAT_WORD(end);
    312       if((len.sig = end - ptr) > MAX_TAG_LEN) {
    313         error = GPE_NO_BUFFER_SPACE;
    314         break;
    315       }
    316       memcpy(ptag, ptr, len.uns);
    317       ptag[len.uns] = '\0';
    318 
    319       if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
    320         /* end of current sub section */
    321         state = STATE_INMAIN;
    322         csub[0] = '\0';
    323         if(in_wanted_part) {
    324           /* end of wanted part */
    325           in_wanted_part = 0;
    326 
    327           /* Do we need to base64 decode the data? */
    328           if(base64) {
    329             error = decodedata(outbuf, outlen);
    330             if(error)
    331               return error;
    332           }
    333           break;
    334         }
    335       }
    336       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
    337         /* end of current main section */
    338         state = STATE_OUTER;
    339         cmain[0] = '\0';
    340         if(in_wanted_part) {
    341           /* end of wanted part */
    342           in_wanted_part = 0;
    343 
    344           /* Do we need to base64 decode the data? */
    345           if(base64) {
    346             error = decodedata(outbuf, outlen);
    347             if(error)
    348               return error;
    349           }
    350           break;
    351         }
    352       }
    353       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
    354         /* end of outermost file section */
    355         state = STATE_OUTSIDE;
    356         couter[0] = '\0';
    357         if(in_wanted_part) {
    358           /* end of wanted part */
    359           in_wanted_part = 0;
    360           break;
    361         }
    362       }
    363 
    364     }
    365     else if(!in_wanted_part) {
    366       /*
    367       ** opening section tag
    368       */
    369 
    370       /* get potential tag */
    371       end = ptr;
    372       EAT_WORD(end);
    373       if((len.sig = end - ptr) > MAX_TAG_LEN) {
    374         error = GPE_NO_BUFFER_SPACE;
    375         break;
    376       }
    377       memcpy(ptag, ptr, len.uns);
    378       ptag[len.uns] = '\0';
    379 
    380       /* ignore comments, doctypes and xml declarations */
    381       if(('!' == ptag[0]) || ('?' == ptag[0])) {
    382         show(("* ignoring (%s)", buffer));
    383         continue;
    384       }
    385 
    386       /* get all potential attributes */
    387       ptr = end;
    388       EAT_SPACE(ptr);
    389       end = ptr;
    390       while(*end && ('>' != *end))
    391         end++;
    392       if((len.sig = end - ptr) > MAX_TAG_LEN) {
    393         error = GPE_NO_BUFFER_SPACE;
    394         break;
    395       }
    396       memcpy(patt, ptr, len.uns);
    397       patt[len.uns] = '\0';
    398 
    399       if(STATE_OUTSIDE == state) {
    400         /* outermost element (<testcase>) */
    401         strcpy(couter, ptag);
    402         state = STATE_OUTER;
    403         continue;
    404       }
    405       else if(STATE_OUTER == state) {
    406         /* start of a main section */
    407         strcpy(cmain, ptag);
    408         state = STATE_INMAIN;
    409         continue;
    410       }
    411       else if(STATE_INMAIN == state) {
    412         /* start of a sub section */
    413         strcpy(csub, ptag);
    414         state = STATE_INSUB;
    415         if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
    416           /* start of wanted part */
    417           in_wanted_part = 1;
    418           if(strstr(patt, "base64="))
    419               /* bit rough test, but "mostly" functional, */
    420               /* treat wanted part data as base64 encoded */
    421               base64 = 1;
    422         }
    423         continue;
    424       }
    425 
    426     }
    427 
    428     if(in_wanted_part) {
    429       show(("=> %s", buffer));
    430       error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
    431       if(error)
    432         break;
    433     }
    434 
    435   } /* while */
    436 
    437   free(buffer);
    438 
    439   if(error != GPE_OK) {
    440     if(error == GPE_END_OF_FILE)
    441       error = GPE_OK;
    442     else {
    443       free(*outbuf);
    444       *outbuf = NULL;
    445       *outlen = 0;
    446     }
    447   }
    448 
    449   return error;
    450 }
    451 
    452