--- /dev/null
+#include "xmlrpc_config.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "bool.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/base_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.
+*/
+
+
+#ifdef WIN32
+
+static const bool win32 = TRUE;
+static const __int64 SECS_BETWEEN_EPOCHS = 11644473600;
+static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
+
+
+void UnixTimeToFileTime(const time_t t, LPFILETIME pft)
+{
+ // Note that LONGLONG is a 64-bit value
+ LONGLONG ll;
+ ll = Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS;
+ pft->dwLowDateTime = (DWORD)ll;
+ pft->dwHighDateTime = ll >> 32;
+}
+
+void UnixTimeToSystemTime(const time_t t, LPSYSTEMTIME pst)
+{
+ FILETIME ft;
+
+ UnixTimeToFileTime(t, &ft);
+ FileTimeToSystemTime(&ft, pst);
+}
+
+static void UnixTimeFromFileTime(xmlrpc_env * const envP, LPFILETIME pft, time_t * const timeValueP)
+{
+ LONGLONG ll;
+
+ ll = ((LONGLONG)pft->dwHighDateTime << 32) + pft->dwLowDateTime;
+ /* convert to the Unix epoch */
+ ll -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
+ /* now convert to seconds */
+ ll /= SECS_TO_100NS;
+
+ if ( (time_t)ll != ll )
+ {
+ //fail - value is too big for a time_t
+ xmlrpc_faultf(envP, "Does not indicate a valid date");
+ *timeValueP = (time_t)-1;
+ return;
+ }
+ *timeValueP = (time_t)ll;
+}
+
+static void UnixTimeFromSystemTime(xmlrpc_env * const envP, LPSYSTEMTIME pst, time_t * const timeValueP)
+{
+ FILETIME filetime;
+
+ SystemTimeToFileTime(pst, &filetime);
+ UnixTimeFromFileTime(envP, &filetime, timeValueP);
+}
+
+#else
+static const bool win32 = false;
+#endif
+
+
+static void
+validateDatetimeType(xmlrpc_env * const envP,
+ const xmlrpc_value * const valueP) {
+
+ if (valueP->_type != XMLRPC_TYPE_DATETIME) {
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
+ "type %s was expected.",
+ xmlrpc_typeName(valueP->_type),
+ xmlrpc_typeName(XMLRPC_TYPE_DATETIME));
+ }
+}
+
+
+
+void
+xmlrpc_read_datetime_str(xmlrpc_env * const envP,
+ const xmlrpc_value * const valueP,
+ const char ** const stringValueP) {
+
+ 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");
+ }
+}
+
+
+
+void
+xmlrpc_read_datetime_str_old(xmlrpc_env * const envP,
+ const xmlrpc_value * const valueP,
+ const char ** const stringValueP) {
+
+ validateDatetimeType(envP, valueP);
+ if (!envP->fault_occurred) {
+ *stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+ }
+}
+
+
+
+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);
+}
+
+
+#ifdef HAVE_SETENV
+xmlrpc_bool const haveSetenv = true;
+#else
+xmlrpc_bool const haveSetenv = false;
+static void
+setenv(const char * const name ATTR_UNUSED,
+ const char * const value ATTR_UNUSED,
+ int const replace ATTR_UNUSED) {
+ assert(FALSE);
+}
+#endif
+
+static void
+makeTimezoneUtc(xmlrpc_env * const envP,
+ const char ** const oldTzP) {
+
+ const char * const tz = getenv("TZ");
+
+#ifdef WIN32
+ /* Windows implementation does not exist */
+ assert(TRUE);
+#endif
+
+ if (haveSetenv) {
+ if (tz) {
+ *oldTzP = strdup(tz);
+ if (*oldTzP == NULL)
+ xmlrpc_faultf(envP, "Unable to get memory to save TZ "
+ "environment variable.");
+ } else
+ *oldTzP = NULL;
+
+ if (!envP->fault_occurred)
+ setenv("TZ", "", 1);
+ } else {
+ if (tz && strlen(tz) == 0) {
+ /* Everything's fine. Nothing to change or restore */
+ } else {
+ /* Note that putenv() is not sufficient. You can't restore
+ the original value with that, because it sets a pointer into
+ your own storage.
+ */
+ xmlrpc_faultf(envP, "Your TZ environment variable is not a "
+ "null string and your C library does not have "
+ "setenv(), so we can't change it.");
+ }
+ }
+}
+
+
+
+static void
+restoreTimezone(const char * const oldTz) {
+
+ if (haveSetenv) {
+ setenv("TZ", oldTz, 1);
+ free((char*)oldTz);
+ }
+}
+
+
+
+static void
+mkAbsTimeWin32(xmlrpc_env * const envP ATTR_UNUSED,
+ struct tm const brokenTime ATTR_UNUSED,
+ time_t * const timeValueP ATTR_UNUSED) {
+#ifdef WIN32
+ /* Windows Implementation */
+ SYSTEMTIME stbrokenTime;
+
+ stbrokenTime.wHour = brokenTime.tm_hour;
+ stbrokenTime.wMinute = brokenTime.tm_min;
+ stbrokenTime.wSecond = brokenTime.tm_sec;
+ stbrokenTime.wMonth = brokenTime.tm_mon;
+ stbrokenTime.wDay = brokenTime.tm_mday;
+ stbrokenTime.wYear = brokenTime.tm_year;
+ stbrokenTime.wMilliseconds = 0;
+
+ /* When the date string is parsed into the tm structure, it was
+ modified to decrement the month count by one and convert the
+ 4 digit year to a two digit year. We undo what the parser
+ did to make it a true SYSTEMTIME structure, then convert this
+ structure into a UNIX time_t structure
+ */
+ stbrokenTime.wYear+=1900;
+ stbrokenTime.wMonth+=1;
+
+ UnixTimeFromSystemTime(envP, &stbrokenTime,timeValueP);
+#endif
+}
+
+
+static void
+mkAbsTimeUnix(xmlrpc_env * const envP ATTR_UNUSED,
+ struct tm const brokenTime ATTR_UNUSED,
+ time_t * const timeValueP ATTR_UNUSED) {
+
+#ifndef WIN32
+ time_t mktimeResult;
+ const char * oldTz;
+ struct tm mktimeWork;
+
+ /* We use mktime() to create the time_t because it's the
+ best we have available, but mktime() takes a local time
+ argument, and we have absolute time. So we fake it out
+ by temporarily setting the timezone to UTC.
+ */
+ makeTimezoneUtc(envP, &oldTz);
+
+ if (!envP->fault_occurred) {
+ mktimeWork = brokenTime;
+ mktimeResult = mktime(&mktimeWork);
+
+ restoreTimezone(oldTz);
+
+ if (mktimeResult == (time_t)-1)
+ xmlrpc_faultf(envP, "Does not indicate a valid date");
+ else
+ *timeValueP = mktimeResult;
+ }
+#endif
+}
+
+
+
+static void
+mkAbsTime(xmlrpc_env * const envP,
+ struct tm const brokenTime,
+ time_t * const timeValueP) {
+
+ if (win32)
+ mkAbsTimeWin32(envP, brokenTime, timeValueP);
+ else
+ mkAbsTimeUnix(envP, brokenTime, timeValueP);
+}
+
+
+
+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.
+-----------------------------------------------------------------------------*/
+ validateFormat(envP, t);
+
+ if (!envP->fault_occurred) {
+ unsigned int Y, M, D, h, m, s;
+
+ parseDateNumbers(t, &Y, &M, &D, &h, &m, &s);
+
+ if (Y < 1900)
+ xmlrpc_faultf(envP, "Year is too early to represent as "
+ "a standard Unix time");
+ else {
+ struct tm brokenTime;
+
+ 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;
+
+ mkAbsTime(envP, brokenTime, timeValueP);
+ }
+ }
+}
+
+
+
+void
+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);
+}
+
+
+
+xmlrpc_value *
+xmlrpc_datetime_new_str(xmlrpc_env * const envP,
+ const char * const value) {
+
+ xmlrpc_value * valP;
+
+ xmlrpc_createXmlrpcValue(envP, &valP);
+
+ if (!envP->fault_occurred) {
+ valP->_type = XMLRPC_TYPE_DATETIME;
+
+ 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);
+ }
+ return valP;
+}
+
+
+
+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;
+
+ gmtime_r(&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;
+}