24
31
after 2038. We need to figure out something better.
30
static const bool win32 = TRUE;
31
40
static const __int64 SECS_BETWEEN_EPOCHS = 11644473600;
32
41
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;
45
UnixTimeToFileTime(time_t const t,
46
LPFILETIME const pft) {
49
Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS;
51
pft->dwLowDateTime = (DWORD)ll;
52
pft->dwHighDateTime = (DWORD)(ll >> 32);
44
void UnixTimeToSystemTime(const time_t t, LPSYSTEMTIME pst)
58
UnixTimeToSystemTime(time_t const t,
59
LPSYSTEMTIME const pst) {
48
62
UnixTimeToFileTime(t, &ft);
49
63
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
69
UnixTimeFromFileTime(xmlrpc_env * const envP,
71
time_t * const timeValueP) {
73
int64_t const WinEpoch100Ns =
74
((int64_t)pft->dwHighDateTime << 32) + pft->dwLowDateTime;
75
int64_t const unixEpoch100Ns =
76
WinEpoch100Ns - (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
77
int64_t const unixEpochSeconds =
78
unixEpoch100Ns / SECS_TO_100NS;
80
if ((time_t)unixEpochSeconds != unixEpochSeconds) {
81
/* Value is too big for a time_t; fail. */
65
82
xmlrpc_faultf(envP, "Does not indicate a valid date");
66
*timeValueP = (time_t)-1;
69
*timeValueP = (time_t)ll;
83
*timeValueP = (time_t)(-1);
85
*timeValueP = (time_t)unixEpochSeconds;
72
static void UnixTimeFromSystemTime(xmlrpc_env * const envP, LPSYSTEMTIME pst, time_t * const timeValueP)
91
UnixTimeFromSystemTime(xmlrpc_env * const envP,
92
LPSYSTEMTIME const pst,
93
time_t * const timeValueP) {
76
96
SystemTimeToFileTime(pst, &filetime);
77
97
UnixTimeFromFileTime(envP, &filetime, timeValueP);
81
static const bool win32 = false;
104
static const char * const iso8601Regex =
105
"^([0-9]{4})([0-9]{2})([0-9]{2})T"
106
"([0-9]{2}):?([0-9]{2}):?([0-9]{2})\\.?([0-9]+)?$";
160
digitStringValue(const char * const string,
161
regmatch_t const match) {
162
/*----------------------------------------------------------------------------
163
Return the numerical value of the decimal whole number substring of
164
'string' identified by 'match'. E.g. if 'string' is 'abc34d' and
165
'match' says start at 3 and end at 5, we return 34.
166
-----------------------------------------------------------------------------*/
170
assert(match.rm_so >= 0);
171
assert(match.rm_eo >= 0);
173
for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_eo; ++i) {
175
assert(isdigit(string[i]));
176
accum += string[i] - '0';
180
#endif /* HAVE_REGEX */
187
digitStringMillionths(const char * const string,
188
regmatch_t const match) {
189
/*----------------------------------------------------------------------------
190
Return the number of millionths represented by the digits after the
191
decimal point in a decimal string, where thse digits are the substring
192
of 'string' identified by 'match'. E.g. if the substring is
193
34, we return 340,000.
194
-----------------------------------------------------------------------------*/
198
assert(match.rm_so >= 0);
199
assert(match.rm_eo >= 0);
201
for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_so+6; ++i) {
203
if (i < (unsigned)match.rm_eo) {
204
assert(isdigit(string[i]));
205
accum += string[i] - '0';
210
#endif /* HAVE_REGEX */
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) {
216
parseDateNumbersRegex(xmlrpc_env * const envP,
217
const char * const datetimeString,
218
unsigned int * const YP,
219
unsigned int * const MP,
220
unsigned int * const DP,
221
unsigned int * const hP,
222
unsigned int * const mP,
223
unsigned int * const sP,
224
unsigned int * const uP) {
231
status = regcomp(&re, iso8601Regex, REG_ICASE | REG_EXTENDED);
233
regmatch_t matches[1024];
236
status = regexec(&re, datetimeString, ARRAY_SIZE(matches), matches, 0);
239
assert(matches[0].rm_so != -1); /* Match of whole regex */
241
*YP = digitStringValue(datetimeString, matches[1]);
242
*MP = digitStringValue(datetimeString, matches[2]);
243
*DP = digitStringValue(datetimeString, matches[3]);
244
*hP = digitStringValue(datetimeString, matches[4]);
245
*mP = digitStringValue(datetimeString, matches[5]);
246
*sP = digitStringValue(datetimeString, matches[6]);
248
if (matches[7].rm_so == -1)
251
*uP = digitStringMillionths(datetimeString, matches[7]);
253
regerror(status, &re, errBuf, sizeof(errBuf));
254
xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR, errBuf);
258
regerror(status, &re, errBuf, sizeof(errBuf));
259
xmlrpc_faultf(envP, "internal regex error at %s:%d: '%s'",
260
__FILE__, __LINE__, errBuf);
264
#endif /* HAVE_REGEX */
268
static __inline__ void
269
parseDateNumbersNoRegex(xmlrpc_env * const envP,
270
const char * const datetimeString,
271
unsigned int * const YP,
272
unsigned int * const MP,
273
unsigned int * const DP,
274
unsigned int * const hP,
275
unsigned int * const mP,
276
unsigned int * const sP,
277
unsigned int * const uP) {
279
unsigned int const dtStrlen = strlen(datetimeString);
145
285
char minute[2+1];
146
286
char second[2+1];
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.");
288
if (dtStrlen < 17 || dtStrlen == 18 || dtStrlen > 24)
289
xmlrpc_faultf(envP, "could not parse date, size incompatible: '%d'",
292
year[0] = datetimeString[ 0];
293
year[1] = datetimeString[ 1];
294
year[2] = datetimeString[ 2];
295
year[3] = datetimeString[ 3];
298
month[0] = datetimeString[ 4];
299
month[1] = datetimeString[ 5];
302
day[0] = datetimeString[ 6];
303
day[1] = datetimeString[ 7];
306
assert(datetimeString[ 8] == 'T');
308
hour[0] = datetimeString[ 9];
309
hour[1] = datetimeString[10];
312
assert(datetimeString[11] == ':');
314
minute[0] = datetimeString[12];
315
minute[1] = datetimeString[13];
318
assert(datetimeString[14] == ':');
320
second[0] = datetimeString[15];
321
second[1] = datetimeString[16];
325
unsigned int const pad = 24 - dtStrlen;
328
*uP = atoi(&datetimeString[18]);
329
for (i = 0; i < pad; ++i)
223
if (!envP->fault_occurred)
226
if (tz && strlen(tz) == 0) {
227
/* Everything's fine. Nothing to change or restore */
346
validateFirst17(xmlrpc_env * const envP,
347
const char * const dt) {
348
/*----------------------------------------------------------------------------
349
Assuming 'dt' is at least 17 characters long, validate that the first
350
17 characters are a valid XML-RPC datetime, e.g.
352
-----------------------------------------------------------------------------*/
355
for (i = 0; i < 8 && !envP->fault_occurred; ++i)
357
xmlrpc_env_set_fault_formatted(
358
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[i]);
361
xmlrpc_env_set_fault_formatted(
362
envP, XMLRPC_PARSE_ERROR, "9th character is '%c', not 'T'",
365
xmlrpc_env_set_fault_formatted(
366
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[9]);
367
if (!isdigit(dt[10]))
368
xmlrpc_env_set_fault_formatted(
369
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[10]);
371
xmlrpc_env_set_fault_formatted(
372
envP, XMLRPC_PARSE_ERROR, "Not a colon: '%c'", dt[11]);
373
if (!isdigit(dt[12]))
374
xmlrpc_env_set_fault_formatted(
375
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[12]);
376
if (!isdigit(dt[13]))
377
xmlrpc_env_set_fault_formatted(
378
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[13]);
380
xmlrpc_env_set_fault_formatted(
381
envP, XMLRPC_PARSE_ERROR, "Not a colon: '%c'", dt[14]);
382
if (!isdigit(dt[15]))
383
xmlrpc_env_set_fault_formatted(
384
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[15]);
385
if (!isdigit(dt[16]))
386
xmlrpc_env_set_fault_formatted(
387
envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[16]);
393
validateFractionalSeconds(xmlrpc_env * const envP,
394
const char * const dt) {
395
/*----------------------------------------------------------------------------
396
Validate the fractional seconds part of the XML-RPC datetime string
397
'dt', if any. That's the decimal point and everything following
399
-----------------------------------------------------------------------------*/
400
if (strlen(dt) > 17) {
402
xmlrpc_env_set_fault_formatted(
403
envP, XMLRPC_PARSE_ERROR,
404
"'%c' where only a period is valid", dt[17]);
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.");
407
xmlrpc_env_set_fault_formatted(
408
envP, XMLRPC_PARSE_ERROR, "Nothing after decimal point");
411
for (i = 18; dt[i] != '\0' && !envP->fault_occurred; ++i) {
413
xmlrpc_env_set_fault_formatted(
414
envP, XMLRPC_PARSE_ERROR,
415
"Non-digit in fractional seconds: '%c'", dt[i]);
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);
424
static __inline__ void
330
425
validateFormat(xmlrpc_env * const envP,
331
const char * const t) {
426
const char * const dt) {
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]);
429
xmlrpc_env_set_fault_formatted(
430
envP, XMLRPC_PARSE_ERROR,
431
"Invalid length of %u of datetime. "
432
"Must be at least 17 characters",
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]);
435
validateFirst17(envP, dt);
437
validateFractionalSeconds(envP, dt);
366
parseDatetime(xmlrpc_env * const envP,
367
const char * const t,
368
time_t * const timeValueP) {
444
parseDateNumbers(xmlrpc_env * const envP,
445
const char * const datetimeString,
446
unsigned int * const YP,
447
unsigned int * const MP,
448
unsigned int * const DP,
449
unsigned int * const hP,
450
unsigned int * const mP,
451
unsigned int * const sP,
452
unsigned int * const uP) {
455
parseDateNumbersRegex(envP, datetimeString, YP, MP, DP, hP, mP, sP, uP);
457
/* Note: validation is not as strong without regex */
458
validateFormat(envP, datetimeString);
459
if (!envP->fault_occurred)
460
parseDateNumbersNoRegex(envP, datetimeString,
461
YP, MP, DP, hP, mP, sP, uP);
468
parseDatetime(xmlrpc_env * const envP,
469
const char * const datetimeString,
470
time_t * const timeValueP,
471
unsigned int * const usecsP) {
369
472
/*----------------------------------------------------------------------------
370
473
Parse a time in the format stored in an xmlrpc_value and return the
371
474
time that it represents.
373
t[] is the input time string. We return the result as *timeValueP.
476
datetimeString[] is the input time string. We return the result as
375
479
Example of the format we parse: "19980717T14:08:55"
376
480
Note that this is not quite ISO 8601. It's a bizarre combination of
377
481
two ISO 8601 formats.
483
The input is capable of representing datetimes that cannot be expressed
484
as a time_t. In that case, we fail, with fault code
485
XMLRPC_INTERNAL_ERROR.
487
And of course the input may not validly represent a datetime at all.
488
In that case too, we fail with fault code XMLRPC_PARSE_ERROR.
378
489
-----------------------------------------------------------------------------*/
379
validateFormat(envP, t);
490
validateFormat(envP, datetimeString);
381
492
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);
493
unsigned int Y, M, D, h, m, s, u;
495
parseDateNumbers(envP, datetimeString, &Y, &M, &D, &h, &m, &s, &u);
497
if (!envP->fault_occurred) {
499
xmlrpc_env_set_fault_formatted(envP, XMLRPC_INTERNAL_ERROR,
500
"Year is too early to represent as "
501
"a standard Unix time");
503
struct tm brokenTime;
506
brokenTime.tm_sec = s;
507
brokenTime.tm_min = m;
508
brokenTime.tm_hour = h;
509
brokenTime.tm_mday = D;
510
brokenTime.tm_mon = M - 1;
511
brokenTime.tm_year = Y - 1900;
513
xmlrpc_timegm(&brokenTime, timeValueP, &error);
516
xmlrpc_env_set_fault_formatted(
517
envP, XMLRPC_PARSE_ERROR, error);
518
xmlrpc_strfree(error);