mirror of
https://github.com/curl/curl.git
synced 2024-12-27 06:59:43 +08:00
85e6975643
Reviewed-by: Emil Engler Closes #6802
483 lines
13 KiB
C
483 lines
13 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, 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.
|
|
*
|
|
***************************************************************************/
|
|
#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 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;
|
|
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;
|
|
}
|