Home | History | Annotate | Download | only in server
      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   size_t length;
    120   char *newptr;
    121 
    122   if(!*buffer) {
    123     *buffer = malloc(128);
    124     if(!*buffer)
    125       return GPE_OUT_OF_MEMORY;
    126     *bufsize = 128;
    127   }
    128 
    129   for(;;) {
    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