1 #include "xmlrpc_config.h"
11 #include "xmlrpc-c/base.h"
12 #include "xmlrpc-c/base_int.h"
15 /* Future work: the XMLRPC_TYPE_DATETIME xmlrpc_value should store the
16 datetime as something computation-friendly, not as a string. The
17 XML-RPC XML parser should parse the string value and reject the XML if
20 But this file should remain the authority on datetimes, so the XML
21 parser and builder should call on routines in here to do that.
23 time_t won't work because it can't represent times before 1970 or
24 after 2038. We need to figure out something better.
30 static const bool win32 = TRUE;
31 static const __int64 SECS_BETWEEN_EPOCHS = 11644473600;
32 static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
35 void UnixTimeToFileTime(const time_t t, LPFILETIME pft)
37 // Note that LONGLONG is a 64-bit value
39 ll = Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS;
40 pft->dwLowDateTime = (DWORD)ll;
41 pft->dwHighDateTime = ll >> 32;
44 void UnixTimeToSystemTime(const time_t t, LPSYSTEMTIME pst)
48 UnixTimeToFileTime(t, &ft);
49 FileTimeToSystemTime(&ft, pst);
52 static void UnixTimeFromFileTime(xmlrpc_env * const envP, LPFILETIME pft, time_t * const timeValueP)
56 ll = ((LONGLONG)pft->dwHighDateTime << 32) + pft->dwLowDateTime;
57 /* convert to the Unix epoch */
58 ll -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
59 /* now convert to seconds */
62 if ( (time_t)ll != ll )
64 //fail - value is too big for a time_t
65 xmlrpc_faultf(envP, "Does not indicate a valid date");
66 *timeValueP = (time_t)-1;
69 *timeValueP = (time_t)ll;
72 static void UnixTimeFromSystemTime(xmlrpc_env * const envP, LPSYSTEMTIME pst, time_t * const timeValueP)
76 SystemTimeToFileTime(pst, &filetime);
77 UnixTimeFromFileTime(envP, &filetime, timeValueP);
81 static const bool win32 = false;
86 validateDatetimeType(xmlrpc_env * const envP,
87 const xmlrpc_value * const valueP) {
89 if (valueP->_type != XMLRPC_TYPE_DATETIME) {
90 xmlrpc_env_set_fault_formatted(
91 envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
92 "type %s was expected.",
93 xmlrpc_typeName(valueP->_type),
94 xmlrpc_typeName(XMLRPC_TYPE_DATETIME));
101 xmlrpc_read_datetime_str(xmlrpc_env * const envP,
102 const xmlrpc_value * const valueP,
103 const char ** const stringValueP) {
105 validateDatetimeType(envP, valueP);
106 if (!envP->fault_occurred) {
107 const char * const contents =
108 XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
109 *stringValueP = strdup(contents);
110 if (*stringValueP == NULL)
111 xmlrpc_env_set_fault_formatted(
112 envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space "
113 "for datetime string");
120 xmlrpc_read_datetime_str_old(xmlrpc_env * const envP,
121 const xmlrpc_value * const valueP,
122 const char ** const stringValueP) {
124 validateDatetimeType(envP, valueP);
125 if (!envP->fault_occurred) {
126 *stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
133 parseDateNumbers(const char * const t,
134 unsigned int * const YP,
135 unsigned int * const MP,
136 unsigned int * const DP,
137 unsigned int * const hP,
138 unsigned int * const mP,
139 unsigned int * const sP) {
148 assert(strlen(t) == 17);
164 assert(t[ 8] == 'T');
170 assert(t[11] == ':');
176 assert(t[14] == ':');
192 xmlrpc_bool const haveSetenv = true;
194 xmlrpc_bool const haveSetenv = false;
196 setenv(const char * const name ATTR_UNUSED,
197 const char * const value ATTR_UNUSED,
198 int const replace ATTR_UNUSED) {
204 makeTimezoneUtc(xmlrpc_env * const envP,
205 const char ** const oldTzP) {
207 const char * const tz = getenv("TZ");
210 /* Windows implementation does not exist */
216 *oldTzP = strdup(tz);
218 xmlrpc_faultf(envP, "Unable to get memory to save TZ "
219 "environment variable.");
223 if (!envP->fault_occurred)
226 if (tz && strlen(tz) == 0) {
227 /* Everything's fine. Nothing to change or restore */
229 /* Note that putenv() is not sufficient. You can't restore
230 the original value with that, because it sets a pointer into
233 xmlrpc_faultf(envP, "Your TZ environment variable is not a "
234 "null string and your C library does not have "
235 "setenv(), so we can't change it.");
243 restoreTimezone(const char * const oldTz) {
246 setenv("TZ", oldTz, 1);
254 mkAbsTimeWin32(xmlrpc_env * const envP ATTR_UNUSED,
255 struct tm const brokenTime ATTR_UNUSED,
256 time_t * const timeValueP ATTR_UNUSED) {
258 /* Windows Implementation */
259 SYSTEMTIME stbrokenTime;
261 stbrokenTime.wHour = brokenTime.tm_hour;
262 stbrokenTime.wMinute = brokenTime.tm_min;
263 stbrokenTime.wSecond = brokenTime.tm_sec;
264 stbrokenTime.wMonth = brokenTime.tm_mon;
265 stbrokenTime.wDay = brokenTime.tm_mday;
266 stbrokenTime.wYear = brokenTime.tm_year;
267 stbrokenTime.wMilliseconds = 0;
269 /* When the date string is parsed into the tm structure, it was
270 modified to decrement the month count by one and convert the
271 4 digit year to a two digit year. We undo what the parser
272 did to make it a true SYSTEMTIME structure, then convert this
273 structure into a UNIX time_t structure
275 stbrokenTime.wYear+=1900;
276 stbrokenTime.wMonth+=1;
278 UnixTimeFromSystemTime(envP, &stbrokenTime,timeValueP);
284 mkAbsTimeUnix(xmlrpc_env * const envP ATTR_UNUSED,
285 struct tm const brokenTime ATTR_UNUSED,
286 time_t * const timeValueP ATTR_UNUSED) {
291 struct tm mktimeWork;
293 /* We use mktime() to create the time_t because it's the
294 best we have available, but mktime() takes a local time
295 argument, and we have absolute time. So we fake it out
296 by temporarily setting the timezone to UTC.
298 makeTimezoneUtc(envP, &oldTz);
300 if (!envP->fault_occurred) {
301 mktimeWork = brokenTime;
302 mktimeResult = mktime(&mktimeWork);
304 restoreTimezone(oldTz);
306 if (mktimeResult == (time_t)-1)
307 xmlrpc_faultf(envP, "Does not indicate a valid date");
309 *timeValueP = mktimeResult;
317 mkAbsTime(xmlrpc_env * const envP,
318 struct tm const brokenTime,
319 time_t * const timeValueP) {
322 mkAbsTimeWin32(envP, brokenTime, timeValueP);
324 mkAbsTimeUnix(envP, brokenTime, timeValueP);
330 validateFormat(xmlrpc_env * const envP,
331 const char * const t) {
334 xmlrpc_faultf(envP, "%u characters instead of 15.", strlen(t));
335 else if (t[8] != 'T')
336 xmlrpc_faultf(envP, "9th character is '%c', not 'T'", t[8]);
340 for (i = 0; i < 8 && !envP->fault_occurred; ++i)
342 xmlrpc_faultf(envP, "Not a digit: '%c'", t[i]);
345 xmlrpc_faultf(envP, "Not a digit: '%c'", t[9]);
347 xmlrpc_faultf(envP, "Not a digit: '%c'", t[10]);
349 xmlrpc_faultf(envP, "Not a colon: '%c'", t[11]);
351 xmlrpc_faultf(envP, "Not a digit: '%c'", t[12]);
353 xmlrpc_faultf(envP, "Not a digit: '%c'", t[13]);
355 xmlrpc_faultf(envP, "Not a colon: '%c'", t[14]);
357 xmlrpc_faultf(envP, "Not a digit: '%c'", t[15]);
359 xmlrpc_faultf(envP, "Not a digit: '%c'", t[16]);
366 parseDatetime(xmlrpc_env * const envP,
367 const char * const t,
368 time_t * const timeValueP) {
369 /*----------------------------------------------------------------------------
370 Parse a time in the format stored in an xmlrpc_value and return the
371 time that it represents.
373 t[] is the input time string. We return the result as *timeValueP.
375 Example of the format we parse: "19980717T14:08:55"
376 Note that this is not quite ISO 8601. It's a bizarre combination of
377 two ISO 8601 formats.
378 -----------------------------------------------------------------------------*/
379 validateFormat(envP, t);
381 if (!envP->fault_occurred) {
382 unsigned int Y, M, D, h, m, s;
384 parseDateNumbers(t, &Y, &M, &D, &h, &m, &s);
387 xmlrpc_faultf(envP, "Year is too early to represent as "
388 "a standard Unix time");
390 struct tm brokenTime;
392 brokenTime.tm_sec = s;
393 brokenTime.tm_min = m;
394 brokenTime.tm_hour = h;
395 brokenTime.tm_mday = D;
396 brokenTime.tm_mon = M - 1;
397 brokenTime.tm_year = Y - 1900;
399 mkAbsTime(envP, brokenTime, timeValueP);
407 xmlrpc_read_datetime_sec(xmlrpc_env * const envP,
408 const xmlrpc_value * const valueP,
409 time_t * const timeValueP) {
411 validateDatetimeType(envP, valueP);
412 if (!envP->fault_occurred)
414 XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block),
421 xmlrpc_datetime_new_str(xmlrpc_env * const envP,
422 const char * const value) {
426 xmlrpc_createXmlrpcValue(envP, &valP);
428 if (!envP->fault_occurred) {
429 valP->_type = XMLRPC_TYPE_DATETIME;
431 XMLRPC_TYPED_MEM_BLOCK_INIT(
432 char, envP, &valP->_block, strlen(value) + 1);
433 if (!envP->fault_occurred) {
434 char * const contents =
435 XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
436 strcpy(contents, value);
438 if (envP->fault_occurred)
447 xmlrpc_datetime_new_sec(xmlrpc_env * const envP,
448 time_t const value) {
452 xmlrpc_createXmlrpcValue(envP, &valP);
454 if (!envP->fault_occurred) {
455 struct tm brokenTime;
458 valP->_type = XMLRPC_TYPE_DATETIME;
460 gmtime_r(&value, &brokenTime);
462 /* Note that this format is NOT ISO 8601 -- it's a bizarre
463 hybrid of two ISO 8601 formats.
465 strftime(timeString, sizeof(timeString), "%Y%m%dT%H:%M:%S",
468 XMLRPC_TYPED_MEM_BLOCK_INIT(
469 char, envP, &valP->_block, strlen(timeString) + 1);
470 if (!envP->fault_occurred) {
471 char * const contents =
472 XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
474 strcpy(contents, timeString);
476 if (envP->fault_occurred)