quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }