/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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.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. * * SPDX-License-Identifier: curl * ***************************************************************************/ #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; } /* * line_length() * * Counts the number of characters in a line including a new line. * Unlike strlen() it does not stop at nul bytes. * */ static size_t line_length(const char *buffer, int bytestocheck) { size_t length = 1; while(*buffer != '\n' && --bytestocheck) { length++; buffer++; } if(*buffer != '\n') { /* * We didn't find a new line so the last byte must be a * '\0' character inserted by fgets() which we should not * count. */ length--; } return length; } /* * 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, size_t *length, FILE *stream) { size_t offset = 0; char *newptr; if(!*buffer) { *buffer = malloc(128); if(!*buffer) return GPE_OUT_OF_MEMORY; *bufsize = 128; } for(;;) { int bytestoread = curlx_uztosi(*bufsize - offset); if(!fgets(*buffer + offset, bytestoread, stream)) return (offset != 0) ? GPE_OK : GPE_END_OF_FILE; *length = offset + line_length(*buffer + offset, bytestoread); 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 */ size_t src_len, /* source buffer length */ int src_b64) /* != 0 if source is base64 encoded */ { size_t need_alloc = 0; 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 200 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; size_t datalen; int in_wanted_part = 0; int base64 = 0; int nonewline = 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, &datalen, stream)) == GPE_OK) { ptr = buffer; EAT_SPACE(ptr); if('<' != *ptr) { if(in_wanted_part) { show(("=> %s", buffer)); error = appenddata(outbuf, outlen, &outalloc, buffer, datalen, 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; } if(nonewline) (*outlen)--; 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; } if(nonewline) (*outlen)--; 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 () */ 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; if(strstr(patt, "nonewline=")) { show(("* setting nonewline\n")); nonewline = 1; } } continue; } } if(in_wanted_part) { show(("=> %s", buffer)); error = appenddata(outbuf, outlen, &outalloc, buffer, datalen, 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; }