FS-2746 --resolve large xmlrpc update thanks garmt

This commit is contained in:
Jeff Lenk committed 2012-10-13 11:37:25 -05:00
1 parent 37ecad9903
commit 6b6c83a718
397 files changed
+41821 -33840

No files matched your search

+441 -208
View File
@@ -1,34 +1,30 @@
#include "xmlrpc_config.h"
#define _XOPEN_SOURCE 600 /* Make sure strdup() is in <string.h> */
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#if MSVCRT
#include <windows.h>
#endif
#include "bool.h"
#include "mallocvar.h"
#include "xmlrpc-c/c_util.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/string_int.h"
#include "xmlrpc-c/time_int.h"
/* Future work: the XMLRPC_TYPE_DATETIME xmlrpc_value should store the
datetime as something computation-friendly, not as a string. The
XML-RPC XML parser should parse the string value and reject the XML if
it isn't valid.
But this file should remain the authority on datetimes, so the XML
parser and builder should call on routines in here to do that.
time_t won't work because it can't represent times before 1970 or
after 2038. We need to figure out something better.
*/
#if HAVE_REGEX
#include <regex.h>
#endif
#if MSVCRT
@@ -111,20 +107,66 @@ validateDatetimeType(xmlrpc_env * const envP,
void
xmlrpc_read_datetime(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
xmlrpc_datetime * const dtP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
*dtP = valueP->_value.dt;
}
}
void
xmlrpc_read_datetime_str(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
/*----------------------------------------------------------------------------
This exists for backward compatibility. No normal modern program would
want to see a datetime value in this format. Note that the format isn't
even ISO 8601 -- it's a bizarre hybrid of two ISO 8601 formats.
Do not extend this.
This exists because Xmlrpc-c was at one time lazy and this was the only way
to extract the value. An xmlrpc_value in those days represented a datetime
with the actual XML-RPC wire format of a datetime, and this function simply
returned a copy of it.
-----------------------------------------------------------------------------*/
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
*stringValueP = strdup(contents);
if (*stringValueP == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space "
"for datetime string");
time_t secs;
unsigned int usecs;
xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs);
if (!envP->fault_occurred) {
struct tm brokenTime;
char dtString[64];
xmlrpc_gmtime(secs, &brokenTime);
/* Note that this format is NOT ISO 8601 -- it's a bizarre
hybrid of two ISO 8601 formats.
*/
strftime(dtString, sizeof(dtString), "%Y%m%dT%H:%M:%S",
&brokenTime);
if (usecs != 0) {
char usecString[64];
assert(usecs < 1000000);
snprintf(usecString, sizeof(usecString), ".%06u", usecs);
STRSCAT(dtString, usecString);
}
*stringValueP = strdup(dtString);
if (*stringValueP == NULL)
xmlrpc_faultf(envP,
"Unable to allocate memory for datetime string");
}
}
}
@@ -134,160 +176,69 @@ void
xmlrpc_read_datetime_str_old(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
assert(valueP->_cache);
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
*stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
const char ** const readBufferP = valueP->_cache;
if (!*readBufferP)
/* Nobody's asked for the internal buffer before. Set it up. */
xmlrpc_read_datetime_str(envP, valueP, readBufferP);
*stringValueP = *readBufferP;
}
}
static void
parseDateNumbers(const char * const t,
unsigned int * const YP,
unsigned int * const MP,
unsigned int * const DP,
unsigned int * const hP,
unsigned int * const mP,
unsigned int * const sP) {
char year[4+1];
char month[2+1];
char day[2+1];
char hour[2+1];
char minute[2+1];
char second[2+1];
assert(strlen(t) == 17);
year[0] = t[ 0];
year[1] = t[ 1];
year[2] = t[ 2];
year[3] = t[ 3];
year[4] = '\0';
month[0] = t[ 4];
month[1] = t[ 5];
month[2] = '\0';
day[0] = t[ 6];
day[1] = t[ 7];
day[2] = '\0';
assert(t[ 8] == 'T');
hour[0] = t[ 9];
hour[1] = t[10];
hour[2] = '\0';
assert(t[11] == ':');
minute[0] = t[12];
minute[1] = t[13];
minute[2] = '\0';
assert(t[14] == ':');
second[0] = t[15];
second[1] = t[16];
second[2] = '\0';
*YP = atoi(year);
*MP = atoi(month);
*DP = atoi(day);
*hP = atoi(hour);
*mP = atoi(minute);
*sP = atoi(second);
}
static void
validateFormat(xmlrpc_env * const envP,
const char * const t) {
if (strlen(t) != 17)
xmlrpc_faultf(envP, "%u characters instead of 15.", strlen(t));
else if (t[8] != 'T')
xmlrpc_faultf(envP, "9th character is '%c', not 'T'", t[8]);
else {
unsigned int i;
for (i = 0; i < 8 && !envP->fault_occurred; ++i)
if (!isdigit(t[i]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[i]);
if (!isdigit(t[9]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[9]);
if (!isdigit(t[10]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[10]);
if (t[11] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", t[11]);
if (!isdigit(t[12]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[12]);
if (!isdigit(t[13]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[13]);
if (t[14] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", t[14]);
if (!isdigit(t[15]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[15]);
if (!isdigit(t[16]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[16]);
}
}
static void
parseDatetime(xmlrpc_env * const envP,
const char * const t,
time_t * const timeValueP) {
/*----------------------------------------------------------------------------
Parse a time in the format stored in an xmlrpc_value and return the
time that it represents.
t[] is the input time string. We return the result as *timeValueP.
Example of the format we parse: "19980717T14:08:55"
Note that this is not quite ISO 8601. It's a bizarre combination of
two ISO 8601 formats.
The input is capable of representing datetimes that cannot be expressed
as a time_t. In that case, we fail, with fault code
XMLRPC_INTERNAL_ERROR.
And of course the input may not validly represent a datetime at all.
In that case too, we fail with fault code XMLRPC_PARSE_ERROR.
-----------------------------------------------------------------------------*/
validateFormat(envP, t);
void
xmlrpc_read_datetime_usec(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
time_t * const secsP,
unsigned int * const usecsP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
unsigned int Y, M, D, h, m, s;
parseDateNumbers(t, &Y, &M, &D, &h, &m, &s);
if (Y < 1970)
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Year is too early to represent as "
"a standard Unix time");
if (valueP->_value.dt.Y < 1970)
xmlrpc_faultf(envP, "Year (%u) is too early to represent as "
"a standard Unix time",
valueP->_value.dt.Y);
else {
struct tm brokenTime;
const char * error;
brokenTime.tm_sec = s;
brokenTime.tm_min = m;
brokenTime.tm_hour = h;
brokenTime.tm_mday = D;
brokenTime.tm_mon = M - 1;
brokenTime.tm_year = Y - 1900;
xmlrpc_timegm(&brokenTime, timeValueP, &error);
brokenTime.tm_sec = valueP->_value.dt.s;
brokenTime.tm_min = valueP->_value.dt.m;
brokenTime.tm_hour = valueP->_value.dt.h;
brokenTime.tm_mday = valueP->_value.dt.D;
brokenTime.tm_mon = valueP->_value.dt.M - 1;
brokenTime.tm_year = valueP->_value.dt.Y - 1900;
xmlrpc_timegm(&brokenTime, secsP, &error);
if (error) {
xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR, error);
/* Ideally, this wouldn't be possible - it wouldn't be
possible to create an xmlrpc_value that doesn't actually
represent a real datetime. But today, we're lazy and
don't fully validate incoming XML-RPC <dateTime.iso8601>
elements, and we also have the legacy
xmlrpc_datetime_new_str() constructor to which the user
may feed garbage.
We should tighten that up and then simply assert here that
xmlrpc_timegm() succeeded.
*/
xmlrpc_env_set_fault_formatted(envP, XMLRPC_PARSE_ERROR,
"A datetime received in an XML-RPC message "
"or generated with legacy Xmlrpc-c facilities "
"does not validly describe a datetime. %s",
error);
xmlrpc_strfree(error);
}
} else
*usecsP = valueP->_value.dt.u;
}
}
}
@@ -299,37 +250,311 @@ xmlrpc_read_datetime_sec(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
time_t * const timeValueP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred)
parseDatetime(envP,
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block),
timeValueP);
unsigned int usecs;
xmlrpc_read_datetime_usec(envP, valueP, timeValueP, &usecs);
}
#if XMLRPC_HAVE_TIMEVAL
void
xmlrpc_read_datetime_timeval(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
struct timeval * const timeValueP) {
time_t secs;
unsigned int usecs;
xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs);
timeValueP->tv_sec = secs;
timeValueP->tv_usec = usecs;
}
#endif
#if XMLRPC_HAVE_TIMESPEC
void
xmlrpc_read_datetime_timespec(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
struct timespec * const timeValueP) {
time_t secs;
unsigned int usecs;
xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs);
timeValueP->tv_sec = secs;
timeValueP->tv_nsec = usecs * 1000;
}
#endif
xmlrpc_value *
xmlrpc_datetime_new(xmlrpc_env * const envP,
xmlrpc_datetime const dt) {
xmlrpc_value * valP;
const char ** readBufferP;
MALLOCVAR(readBufferP);
if (!readBufferP)
xmlrpc_faultf(envP, "Couldn't get memory for the cache part of the "
"XML-RPC datetime value object");
else {
*readBufferP = NULL;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_DATETIME;
valP->_value.dt = dt;
valP->_cache = readBufferP;
}
if (envP->fault_occurred)
free(readBufferP);
}
return valP;
}
static void
parseDatetimeString(const char * const datetimeString,
xmlrpc_datetime * const dtP) {
size_t const dtStrlen = strlen(datetimeString);
char year[4+1];
char month[2+1];
char day[2+1];
char hour[2+1];
char minute[2+1];
char second[2+1];
/* Because we require input to be valid: */
assert(dtStrlen >= 17 && dtStrlen != 18 && dtStrlen <= 24);
year[0] = datetimeString[ 0];
year[1] = datetimeString[ 1];
year[2] = datetimeString[ 2];
year[3] = datetimeString[ 3];
year[4] = '\0';
month[0] = datetimeString[ 4];
month[1] = datetimeString[ 5];
month[2] = '\0';
day[0] = datetimeString[ 6];
day[1] = datetimeString[ 7];
day[2] = '\0';
assert(datetimeString[ 8] == 'T');
hour[0] = datetimeString[ 9];
hour[1] = datetimeString[10];
hour[2] = '\0';
assert(datetimeString[11] == ':');
minute[0] = datetimeString[12];
minute[1] = datetimeString[13];
minute[2] = '\0';
assert(datetimeString[14] == ':');
second[0] = datetimeString[15];
second[1] = datetimeString[16];
second[2] = '\0';
if (dtStrlen > 17) {
size_t const pad = 24 - dtStrlen;
size_t i;
dtP->u = atoi(&datetimeString[18]);
for (i = 0; i < pad; ++i)
dtP->u *= 10;
} else
dtP->u = 0;
dtP->Y = atoi(year);
dtP->M = atoi(month);
dtP->D = atoi(day);
dtP->h = atoi(hour);
dtP->m = atoi(minute);
dtP->s = atoi(second);
}
static void
validateFirst17(xmlrpc_env * const envP,
const char * const dt) {
/*----------------------------------------------------------------------------
Assuming 'dt' is at least 17 characters long, validate that the first
17 characters are a valid XML-RPC datetime, e.g.
"20080628T16:35:02"
-----------------------------------------------------------------------------*/
unsigned int i;
for (i = 0; i < 8 && !envP->fault_occurred; ++i)
if (!isdigit(dt[i]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[i]);
if (dt[8] != 'T')
xmlrpc_faultf(envP, "9th character is '%c', not 'T'", dt[8]);
if (!isdigit(dt[9]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[9]);
if (!isdigit(dt[10]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[10]);
if (dt[11] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", dt[11]);
if (!isdigit(dt[12]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[12]);
if (!isdigit(dt[13]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[13]);
if (dt[14] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", dt[14]);
if (!isdigit(dt[15]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[15]);
if (!isdigit(dt[16]))
xmlrpc_faultf(envP, "Not a digit: '%c'", dt[16]);
}
static void
validateFractionalSeconds(xmlrpc_env * const envP,
const char * const dt) {
/*----------------------------------------------------------------------------
Validate the fractional seconds part of the XML-RPC datetime string
'dt', if any. That's the decimal point and everything following
it.
-----------------------------------------------------------------------------*/
if (strlen(dt) > 17) {
if (dt[17] != '.') {
xmlrpc_faultf(envP, "'%c' where only a period is valid", dt[17]);
} else {
if (dt[18] == '\0')
xmlrpc_faultf(envP, "Nothing after decimal point");
else {
unsigned int i;
for (i = 18; dt[i] != '\0' && !envP->fault_occurred; ++i) {
if (!isdigit(dt[i]))
xmlrpc_faultf(envP,
"Non-digit in fractional seconds: '%c'",
dt[i]);
}
}
}
}
}
static void
validateFormat(xmlrpc_env * const envP,
const char * const dt) {
if (strlen(dt) < 17)
xmlrpc_faultf(envP,
"Invalid length of %u of datetime string. "
"Must be at least 17 characters",
(unsigned)strlen(dt));
else {
validateFirst17(envP, dt);
if (!envP->fault_occurred)
validateFractionalSeconds(envP, dt);
}
}
/* Microsoft Visual C in debug mode produces code that complains about
returning an undefined value from xmlrpc_datetime_new_str(). It's a bogus
complaint, because this function is defined to return nothing meaningful
those cases. So we disable the check.
*/
#pragma runtime_checks("u", off)
xmlrpc_value *
xmlrpc_datetime_new_str(xmlrpc_env * const envP,
const char * const value) {
const char * const datetimeString) {
/*----------------------------------------------------------------------------
This exists only for backward compatibility. Originally, this was the
only way to create a datetime XML-RPC value, because we had a really
lazy implementation of XML-RPC serialization and parsing (basically, the
user did it!).
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
Do not extend this. The user should use more normal C representations
of datetimes.
-----------------------------------------------------------------------------*/
xmlrpc_value * retval;
validateFormat(envP, datetimeString);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_DATETIME;
xmlrpc_datetime dt;
XMLRPC_TYPED_MEM_BLOCK_INIT(
char, envP, &valP->_block, strlen(value) + 1);
if (!envP->fault_occurred) {
char * const contents =
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
strcpy(contents, value);
}
if (envP->fault_occurred)
free(valP);
parseDatetimeString(datetimeString, &dt);
/* Note that parseDatetimeString() can generate an invalid datetime
value, e.g. Hour 25 or February 30. Ideally, we would catch that
here, but due to laziness, we simply accept the possibility of
invalid xmlrpc_datetime in xmlrpc_value and whoever uses the the
xmlrpc_value has to deal with it.
*/
retval = xmlrpc_datetime_new(envP, dt);
}
return valP;
return retval;
}
#pragma runtime_checks("u", restore)
xmlrpc_value *
xmlrpc_datetime_new_usec(xmlrpc_env * const envP,
time_t const secs,
unsigned int const usecs) {
xmlrpc_value * valueP;
if (usecs >= 1000000)
xmlrpc_faultf(envP, "Number of fractional microseconds must be less "
"than one million. You specified %u", usecs);
else {
struct tm brokenTime;
xmlrpc_datetime dt;
xmlrpc_gmtime(secs, &brokenTime);
dt.s = brokenTime.tm_sec;
dt.m = brokenTime.tm_min;
dt.h = brokenTime.tm_hour;
dt.D = brokenTime.tm_mday;
dt.M = brokenTime.tm_mon + 1;
dt.Y = 1900 + brokenTime.tm_year;
dt.u = usecs;
valueP = xmlrpc_datetime_new(envP, dt);
}
return valueP;
}
@@ -338,34 +563,42 @@ xmlrpc_value *
xmlrpc_datetime_new_sec(xmlrpc_env * const envP,
time_t const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
struct tm brokenTime;
char timeString[64];
valP->_type = XMLRPC_TYPE_DATETIME;
xmlrpc_gmtime(value, &brokenTime);
/* Note that this format is NOT ISO 8601 -- it's a bizarre
hybrid of two ISO 8601 formats.
*/
strftime(timeString, sizeof(timeString), "%Y%m%dT%H:%M:%S",
&brokenTime);
XMLRPC_TYPED_MEM_BLOCK_INIT(
char, envP, &valP->_block, strlen(timeString) + 1);
if (!envP->fault_occurred) {
char * const contents =
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
strcpy(contents, timeString);
}
if (envP->fault_occurred)
free(valP);
}
return valP;
return xmlrpc_datetime_new_usec(envP, value, 0);
}
#if XMLRPC_HAVE_TIMEVAL
xmlrpc_value *
xmlrpc_datetime_new_timeval(xmlrpc_env * const envP,
struct timeval const value) {
return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_usec);
}
#endif
#if XMLRPC_HAVE_TIMESPEC
xmlrpc_value *
xmlrpc_datetime_new_timespec(xmlrpc_env * const envP,
struct timespec const value) {
return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_nsec/1000);
}
#endif
void
xmlrpc_destroyDatetime(xmlrpc_value * const datetimeP) {
const char ** const readBufferP = datetimeP->_cache;
if (*readBufferP)
xmlrpc_strfree(*readBufferP);
free(datetimeP->_cache);
}