~ubuntu-branches/ubuntu/natty/xmlrpc-c/natty

« back to all changes in this revision

Viewing changes to src/xmlrpc_datetime.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2011-01-06 18:56:02 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20110106185602-09og2x3suqlzbf6s
Tags: 1.16.32-0ubuntu1
* New upstream version (stable release). LP: #659591.
  - No unresolved symbols in the shared libraries. LP: #690779.
  - Builds with --no-add-needed and --as-needed.
* Rename shared library packages.
* Add symbols files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
#include <string.h>
6
6
#include <ctype.h>
7
7
#include <assert.h>
 
8
#include <stdio.h>
 
9
#if MSVCRT
 
10
#include <windows.h>
 
11
#endif
8
12
 
9
13
#include "bool.h"
10
14
 
 
15
#include "xmlrpc-c/c_util.h"
11
16
#include "xmlrpc-c/base.h"
12
17
#include "xmlrpc-c/base_int.h"
 
18
#include "xmlrpc-c/string_int.h"
 
19
#include "xmlrpc-c/time_int.h"
13
20
 
14
21
 
15
22
/* Future work: the XMLRPC_TYPE_DATETIME xmlrpc_value should store the
24
31
   after 2038.  We need to figure out something better.
25
32
*/
26
33
 
27
 
 
28
 
#ifdef WIN32
29
 
 
30
 
static const bool win32 = TRUE;
 
34
#if HAVE_REGEX
 
35
#include "regex.h"
 
36
#endif
 
37
 
 
38
#if MSVCRT
 
39
 
31
40
static const __int64 SECS_BETWEEN_EPOCHS = 11644473600;
32
41
static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
33
42
 
34
43
 
35
 
void UnixTimeToFileTime(const time_t t, LPFILETIME pft)
36
 
{
37
 
    // Note that LONGLONG is a 64-bit value
38
 
    LONGLONG ll;
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
 
45
UnixTimeToFileTime(time_t     const t,
 
46
                   LPFILETIME const pft) {
 
47
 
 
48
    int64_t const ll =
 
49
        Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS;
 
50
 
 
51
    pft->dwLowDateTime  = (DWORD)ll;
 
52
    pft->dwHighDateTime = (DWORD)(ll >> 32);
42
53
}
43
54
 
44
 
void UnixTimeToSystemTime(const time_t t, LPSYSTEMTIME pst)
45
 
{
 
55
 
 
56
 
 
57
void
 
58
UnixTimeToSystemTime(time_t const t,
 
59
                     LPSYSTEMTIME const pst) {
46
60
    FILETIME ft;
47
61
 
48
62
    UnixTimeToFileTime(t, &ft);
49
63
    FileTimeToSystemTime(&ft, pst);
50
64
}
51
65
 
52
 
static void UnixTimeFromFileTime(xmlrpc_env *  const envP, LPFILETIME pft, time_t * const timeValueP) 
53
 
54
 
    LONGLONG ll;
55
 
 
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 */
60
 
    ll /= SECS_TO_100NS; 
61
 
 
62
 
    if ( (time_t)ll != ll )
63
 
    {
64
 
        //fail - value is too big for a time_t
 
66
 
 
67
 
 
68
static void
 
69
UnixTimeFromFileTime(xmlrpc_env *  const envP,
 
70
                     LPFILETIME    const pft,
 
71
                     time_t *      const timeValueP) { 
 
72
 
 
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; 
 
79
 
 
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;
67
 
        return;
68
 
    }
69
 
    *timeValueP = (time_t)ll;
 
83
        *timeValueP = (time_t)(-1);
 
84
    } else
 
85
        *timeValueP = (time_t)unixEpochSeconds;
70
86
}
71
87
 
72
 
static void UnixTimeFromSystemTime(xmlrpc_env *  const envP, LPSYSTEMTIME pst, time_t * const timeValueP) 
73
 
{
 
88
 
 
89
 
 
90
static void
 
91
UnixTimeFromSystemTime(xmlrpc_env * const envP,
 
92
                       LPSYSTEMTIME const pst,
 
93
                       time_t *     const timeValueP) {
74
94
    FILETIME filetime;
75
95
 
76
96
    SystemTimeToFileTime(pst, &filetime); 
77
97
    UnixTimeFromFileTime(envP, &filetime, timeValueP); 
78
98
}
79
99
 
80
 
#else
81
 
static const bool win32 = false;
82
 
#endif
 
100
#endif  /* MSVCRT */
 
101
 
 
102
 
 
103
 
 
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]+)?$";
 
107
 
83
108
 
84
109
 
85
110
static void
90
115
        xmlrpc_env_set_fault_formatted(
91
116
            envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
92
117
            "type %s was expected.", 
93
 
            xmlrpc_typeName(valueP->_type), 
94
 
            xmlrpc_typeName(XMLRPC_TYPE_DATETIME));
 
118
            xmlrpc_type_name(valueP->_type), 
 
119
            xmlrpc_type_name(XMLRPC_TYPE_DATETIME));
95
120
    }
96
121
}
97
122
 
129
154
 
130
155
 
131
156
 
 
157
#if HAVE_REGEX
 
158
 
 
159
static unsigned int
 
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
-----------------------------------------------------------------------------*/
 
167
    unsigned int i;
 
168
    unsigned int accum;
 
169
 
 
170
    assert(match.rm_so >= 0);
 
171
    assert(match.rm_eo >= 0);
 
172
 
 
173
    for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_eo; ++i) {
 
174
        accum *= 10;
 
175
        assert(isdigit(string[i]));
 
176
        accum += string[i] - '0';
 
177
    }
 
178
    return accum;
 
179
}
 
180
#endif  /* HAVE_REGEX */
 
181
 
 
182
 
 
183
 
 
184
#if HAVE_REGEX
 
185
 
 
186
static unsigned int
 
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
-----------------------------------------------------------------------------*/
 
195
    unsigned int i;
 
196
    unsigned int accum;
 
197
 
 
198
    assert(match.rm_so >= 0);
 
199
    assert(match.rm_eo >= 0);
 
200
 
 
201
    for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_so+6; ++i) {
 
202
        accum *= 10;
 
203
        if (i < (unsigned)match.rm_eo) {
 
204
            assert(isdigit(string[i]));
 
205
            accum += string[i] - '0';
 
206
        }
 
207
    }
 
208
    return accum;
 
209
}
 
210
#endif /* HAVE_REGEX */
 
211
 
 
212
 
 
213
 
 
214
#if HAVE_REGEX
132
215
static void
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) {
 
225
 
 
226
 
 
227
    int status;
 
228
    char errBuf[1024];
 
229
    regex_t re;
 
230
 
 
231
    status = regcomp(&re, iso8601Regex, REG_ICASE | REG_EXTENDED);
 
232
    if (status == 0) {
 
233
        regmatch_t matches[1024];
 
234
        int status;
 
235
 
 
236
        status = regexec(&re, datetimeString, ARRAY_SIZE(matches), matches, 0);
 
237
 
 
238
        if (status == 0) {
 
239
            assert(matches[0].rm_so != -1);  /* Match of whole regex */
 
240
            
 
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]);
 
247
 
 
248
            if (matches[7].rm_so == -1)
 
249
                *uP = 0;
 
250
            else
 
251
                *uP = digitStringMillionths(datetimeString, matches[7]);
 
252
        } else {
 
253
            regerror(status, &re, errBuf, sizeof(errBuf));
 
254
            xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR, errBuf);
 
255
        }
 
256
 
 
257
    } else {
 
258
        regerror(status, &re, errBuf, sizeof(errBuf));
 
259
        xmlrpc_faultf(envP, "internal regex error at %s:%d: '%s'",
 
260
                      __FILE__, __LINE__, errBuf);
 
261
    }
 
262
    regfree(&re);
 
263
}
 
264
#endif  /* HAVE_REGEX */
 
265
 
 
266
 
 
267
 
 
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) {
 
278
 
 
279
    unsigned int const dtStrlen = strlen(datetimeString);
140
280
 
141
281
    char year[4+1];
142
282
    char month[2+1];
145
285
    char minute[2+1];
146
286
    char second[2+1];
147
287
 
148
 
    assert(strlen(t) == 17);
149
 
 
150
 
    year[0]   = t[ 0];
151
 
    year[1]   = t[ 1];
152
 
    year[2]   = t[ 2];
153
 
    year[3]   = t[ 3];
154
 
    year[4]   = '\0';
155
 
 
156
 
    month[0]  = t[ 4];
157
 
    month[1]  = t[ 5];
158
 
    month[2]  = '\0';
159
 
 
160
 
    day[0]    = t[ 6];
161
 
    day[1]    = t[ 7];
162
 
    day[2]    = '\0';
163
 
 
164
 
    assert(t[ 8] == 'T');
165
 
 
166
 
    hour[0]   = t[ 9];
167
 
    hour[1]   = t[10];
168
 
    hour[2]   = '\0';
169
 
 
170
 
    assert(t[11] == ':');
171
 
 
172
 
    minute[0] = t[12];
173
 
    minute[1] = t[13];
174
 
    minute[2] = '\0';
175
 
 
176
 
    assert(t[14] == ':');
177
 
 
178
 
    second[0] = t[15];
179
 
    second[1] = t[16];
180
 
    second[2] = '\0';
181
 
 
182
 
    *YP = atoi(year);
183
 
    *MP = atoi(month);
184
 
    *DP = atoi(day);
185
 
    *hP = atoi(hour);
186
 
    *mP = atoi(minute);
187
 
    *sP = atoi(second);
188
 
}
189
 
 
190
 
 
191
 
#ifdef HAVE_SETENV
192
 
xmlrpc_bool const haveSetenv = true;
193
 
#else
194
 
xmlrpc_bool const haveSetenv = false;
195
 
static void
196
 
setenv(const char * const name ATTR_UNUSED,
197
 
       const char * const value ATTR_UNUSED,
198
 
       int          const replace ATTR_UNUSED) {
199
 
    assert(FALSE);
200
 
}
201
 
#endif
202
 
 
203
 
static void
204
 
makeTimezoneUtc(xmlrpc_env *  const envP,
205
 
                const char ** const oldTzP) {
206
 
 
207
 
    const char * const tz = getenv("TZ");
208
 
 
209
 
#ifdef WIN32
210
 
        /* Windows implementation does not exist */
211
 
        assert(TRUE);
212
 
#endif
213
 
 
214
 
    if (haveSetenv) {
215
 
        if (tz) {
216
 
            *oldTzP = strdup(tz);
217
 
            if (*oldTzP == NULL)
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'",
 
290
                      dtStrlen);
 
291
    else {
 
292
        year[0]   = datetimeString[ 0];
 
293
        year[1]   = datetimeString[ 1];
 
294
        year[2]   = datetimeString[ 2];
 
295
        year[3]   = datetimeString[ 3];
 
296
        year[4]   = '\0';
 
297
 
 
298
        month[0]  = datetimeString[ 4];
 
299
        month[1]  = datetimeString[ 5];
 
300
        month[2]  = '\0';
 
301
 
 
302
        day[0]    = datetimeString[ 6];
 
303
        day[1]    = datetimeString[ 7];
 
304
        day[2]    = '\0';
 
305
 
 
306
        assert(datetimeString[ 8] == 'T');
 
307
 
 
308
        hour[0]   = datetimeString[ 9];
 
309
        hour[1]   = datetimeString[10];
 
310
        hour[2]   = '\0';
 
311
 
 
312
        assert(datetimeString[11] == ':');
 
313
 
 
314
        minute[0] = datetimeString[12];
 
315
        minute[1] = datetimeString[13];
 
316
        minute[2] = '\0';
 
317
 
 
318
        assert(datetimeString[14] == ':');
 
319
 
 
320
        second[0] = datetimeString[15];
 
321
        second[1] = datetimeString[16];
 
322
        second[2] = '\0';
 
323
 
 
324
        if (dtStrlen > 17) {
 
325
            unsigned int const pad = 24 - dtStrlen;
 
326
            unsigned int i;
 
327
 
 
328
            *uP = atoi(&datetimeString[18]);
 
329
            for (i = 0; i < pad; ++i)
 
330
                *uP *= 10;
220
331
        } else
221
 
            *oldTzP = NULL;
222
 
 
223
 
        if (!envP->fault_occurred)
224
 
            setenv("TZ", "", 1);
225
 
    } else {
226
 
        if (tz && strlen(tz) == 0) {
227
 
            /* Everything's fine.  Nothing to change or restore */
 
332
            *uP = 0;
 
333
 
 
334
        *YP = atoi(year);
 
335
        *MP = atoi(month);
 
336
        *DP = atoi(day);
 
337
        *hP = atoi(hour);
 
338
        *mP = atoi(minute);
 
339
        *sP = atoi(second);
 
340
    }
 
341
}
 
342
 
 
343
 
 
344
 
 
345
static void
 
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.
 
351
   "20080628T16:35:02"
 
352
-----------------------------------------------------------------------------*/
 
353
    unsigned int i;
 
354
 
 
355
    for (i = 0; i < 8 && !envP->fault_occurred; ++i)
 
356
        if (!isdigit(dt[i]))
 
357
            xmlrpc_env_set_fault_formatted(
 
358
                envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[i]);
 
359
 
 
360
    if (dt[8] != 'T')
 
361
        xmlrpc_env_set_fault_formatted(
 
362
            envP, XMLRPC_PARSE_ERROR, "9th character is '%c', not 'T'",
 
363
            dt[8]);
 
364
    if (!isdigit(dt[9]))
 
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]);
 
370
    if (dt[11] != ':')
 
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]);
 
379
    if (dt[14] != ':')
 
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]);
 
388
}
 
389
 
 
390
 
 
391
 
 
392
static void
 
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
 
398
   it.
 
399
-----------------------------------------------------------------------------*/
 
400
    if (strlen(dt) > 17) {
 
401
        if (dt[17] != '.') {
 
402
            xmlrpc_env_set_fault_formatted(
 
403
                envP, XMLRPC_PARSE_ERROR,
 
404
                "'%c' where only a period is valid", dt[17]);
228
405
        } else {
229
 
            /* Note that putenv() is not sufficient.  You can't restore
230
 
               the original value with that, because it sets a pointer into
231
 
               your own storage.
232
 
            */
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.");
 
406
            if (dt[18] == '\0')
 
407
                xmlrpc_env_set_fault_formatted(
 
408
                    envP, XMLRPC_PARSE_ERROR, "Nothing after decimal point");
 
409
            else {
 
410
                unsigned int i;
 
411
                for (i = 18; dt[i] != '\0' && !envP->fault_occurred; ++i) {
 
412
                    if (!isdigit(dt[i]))
 
413
                        xmlrpc_env_set_fault_formatted(
 
414
                            envP, XMLRPC_PARSE_ERROR,
 
415
                            "Non-digit in fractional seconds: '%c'", dt[i]);
 
416
                }
 
417
            }
236
418
        }
237
419
    }
238
420
}
239
 
    
240
 
 
241
 
 
242
 
static void
243
 
restoreTimezone(const char * const oldTz) {
244
 
 
245
 
    if (haveSetenv) {
246
 
        setenv("TZ", oldTz, 1);
247
 
        free((char*)oldTz);
248
 
    }
249
 
}
250
 
 
251
 
 
252
 
 
253
 
static void
254
 
mkAbsTimeWin32(xmlrpc_env * const envP ATTR_UNUSED,
255
 
               struct tm    const brokenTime ATTR_UNUSED,
256
 
               time_t     * const timeValueP ATTR_UNUSED) {
257
 
#ifdef WIN32
258
 
    /* Windows Implementation */
259
 
    SYSTEMTIME stbrokenTime;
260
 
 
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;
268
 
 
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
274
 
    */
275
 
    stbrokenTime.wYear+=1900;
276
 
    stbrokenTime.wMonth+=1;
277
 
 
278
 
    UnixTimeFromSystemTime(envP, &stbrokenTime,timeValueP);
279
 
#endif
280
 
}
281
 
 
282
 
 
283
 
static void
284
 
mkAbsTimeUnix(xmlrpc_env * const envP ATTR_UNUSED,
285
 
              struct tm    const brokenTime ATTR_UNUSED,
286
 
              time_t     * const timeValueP ATTR_UNUSED) {
287
 
 
288
 
#ifndef WIN32
289
 
    time_t mktimeResult;
290
 
    const char * oldTz;
291
 
    struct tm mktimeWork;
292
 
 
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.
297
 
    */
298
 
    makeTimezoneUtc(envP, &oldTz);
299
 
 
300
 
    if (!envP->fault_occurred) {
301
 
        mktimeWork = brokenTime;
302
 
        mktimeResult = mktime(&mktimeWork);
303
 
 
304
 
        restoreTimezone(oldTz);
305
 
 
306
 
        if (mktimeResult == (time_t)-1)
307
 
            xmlrpc_faultf(envP, "Does not indicate a valid date");
308
 
        else
309
 
            *timeValueP = mktimeResult;
310
 
    }
311
 
#endif
312
 
}
313
 
 
314
 
 
315
 
 
316
 
static void
317
 
mkAbsTime(xmlrpc_env * const envP,
318
 
          struct tm    const brokenTime,
319
 
          time_t     * const timeValueP) {
320
 
 
321
 
    if (win32)
322
 
        mkAbsTimeWin32(envP, brokenTime, timeValueP);
323
 
    else
324
 
        mkAbsTimeUnix(envP, brokenTime, timeValueP);
325
 
}
326
 
 
327
 
 
328
 
 
329
 
static void
 
421
 
 
422
 
 
423
 
 
424
static __inline__ void
330
425
validateFormat(xmlrpc_env * const envP,
331
 
               const char * const t) {
 
426
               const char * const dt) {
332
427
 
333
 
    if (strlen(t) != 17)
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]);
 
428
    if (strlen(dt) < 17)
 
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",
 
433
            strlen(dt));
337
434
    else {
338
 
        unsigned int i;
339
 
 
340
 
        for (i = 0; i < 8 && !envP->fault_occurred; ++i)
341
 
            if (!isdigit(t[i]))
342
 
                xmlrpc_faultf(envP, "Not a digit: '%c'", t[i]);
343
 
 
344
 
        if (!isdigit(t[9]))
345
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[9]);
346
 
        if (!isdigit(t[10]))
347
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[10]);
348
 
        if (t[11] != ':')
349
 
            xmlrpc_faultf(envP, "Not a colon: '%c'", t[11]);
350
 
        if (!isdigit(t[12]))
351
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[12]);
352
 
        if (!isdigit(t[13]))
353
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[13]);
354
 
        if (t[14] != ':')
355
 
            xmlrpc_faultf(envP, "Not a colon: '%c'", t[14]);
356
 
        if (!isdigit(t[15]))
357
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[15]);
358
 
        if (!isdigit(t[16]))
359
 
            xmlrpc_faultf(envP, "Not a digit: '%c'", t[16]);
 
435
        validateFirst17(envP, dt);
 
436
 
 
437
        validateFractionalSeconds(envP, dt);
360
438
    }
361
 
}        
362
 
 
363
 
 
364
 
 
365
 
static void
366
 
parseDatetime(xmlrpc_env * const envP,
367
 
              const char * const t,
368
 
              time_t *     const timeValueP) {
 
439
}
 
440
 
 
441
 
 
442
 
 
443
static void
 
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) {
 
453
 
 
454
#if HAVE_REGEX
 
455
    parseDateNumbersRegex(envP, datetimeString, YP, MP, DP, hP, mP, sP, uP);
 
456
#else
 
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);
 
462
#endif
 
463
}
 
464
 
 
465
 
 
466
 
 
467
static void
 
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.
372
475
 
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
 
477
   *timeValueP.
374
478
 
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.
 
482
 
 
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.
 
486
 
 
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);
380
491
 
381
492
    if (!envP->fault_occurred) {
382
 
        unsigned int Y, M, D, h, m, s;
383
 
        
384
 
        parseDateNumbers(t, &Y, &M, &D, &h, &m, &s);
385
 
        
386
 
        if (Y < 1900)
387
 
            xmlrpc_faultf(envP, "Year is too early to represent as "
388
 
                          "a standard Unix time");
389
 
        else {
390
 
            struct tm brokenTime;
391
 
            
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;
398
 
            
399
 
            mkAbsTime(envP, brokenTime, timeValueP);
 
493
        unsigned int Y, M, D, h, m, s, u;
 
494
        
 
495
        parseDateNumbers(envP, datetimeString, &Y, &M, &D, &h, &m, &s, &u);
 
496
 
 
497
        if (!envP->fault_occurred) {
 
498
            if (Y < 1970)
 
499
                xmlrpc_env_set_fault_formatted(envP, XMLRPC_INTERNAL_ERROR,
 
500
                                     "Year is too early to represent as "
 
501
                                     "a standard Unix time");
 
502
            else {
 
503
                struct tm brokenTime;
 
504
                const char * error;
 
505
                
 
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;
 
512
                
 
513
                xmlrpc_timegm(&brokenTime, timeValueP, &error);
 
514
 
 
515
                if (error) {
 
516
                    xmlrpc_env_set_fault_formatted(
 
517
                        envP, XMLRPC_PARSE_ERROR, error);
 
518
                    xmlrpc_strfree(error);
 
519
                } else
 
520
                    *usecsP = u;
 
521
            }
400
522
        }
401
523
    }
402
524
}
404
526
 
405
527
 
406
528
void
 
529
xmlrpc_read_datetime_usec(xmlrpc_env *         const envP,
 
530
                          const xmlrpc_value * const valueP,
 
531
                          time_t *             const secsP,
 
532
                          unsigned int *       const usecsP) {
 
533
    
 
534
    validateDatetimeType(envP, valueP);
 
535
 
 
536
    if (!envP->fault_occurred)
 
537
        parseDatetime(envP,
 
538
                      XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block),
 
539
                      secsP,
 
540
                      usecsP);
 
541
}
 
542
 
 
543
 
 
544
 
 
545
void
407
546
xmlrpc_read_datetime_sec(xmlrpc_env *         const envP,
408
547
                         const xmlrpc_value * const valueP,
409
548
                         time_t *             const timeValueP) {
410
549
    
411
 
    validateDatetimeType(envP, valueP);
412
 
    if (!envP->fault_occurred)
413
 
        parseDatetime(envP,
414
 
                      XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block),
415
 
                      timeValueP);
416
 
}
 
550
    unsigned int usecs;
 
551
 
 
552
    xmlrpc_read_datetime_usec(envP, valueP, timeValueP, &usecs);
 
553
}
 
554
 
 
555
 
 
556
 
 
557
#if XMLRPC_HAVE_TIMEVAL
 
558
 
 
559
void
 
560
xmlrpc_read_datetime_timeval(xmlrpc_env *         const envP,
 
561
                             const xmlrpc_value * const valueP,
 
562
                             struct timeval *     const timeValueP) {
 
563
    
 
564
    time_t secs;
 
565
    unsigned int usecs;
 
566
 
 
567
    xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs);
 
568
 
 
569
    timeValueP->tv_sec  = secs;
 
570
    timeValueP->tv_usec = usecs;
 
571
}
 
572
#endif
 
573
 
 
574
 
 
575
 
 
576
#if XMLRPC_HAVE_TIMESPEC
 
577
 
 
578
void
 
579
xmlrpc_read_datetime_timespec(xmlrpc_env *         const envP,
 
580
                              const xmlrpc_value * const valueP,
 
581
                              struct timespec *    const timeValueP) {
 
582
    
 
583
    time_t secs;
 
584
    unsigned int usecs;
 
585
 
 
586
    xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs);
 
587
 
 
588
    timeValueP->tv_sec  = secs;
 
589
    timeValueP->tv_nsec = usecs * 1000;
 
590
}
 
591
#endif
417
592
 
418
593
 
419
594
 
443
618
 
444
619
 
445
620
 
446
 
xmlrpc_value *
447
 
xmlrpc_datetime_new_sec(xmlrpc_env * const envP, 
448
 
                        time_t       const value) {
 
621
xmlrpc_value*
 
622
xmlrpc_datetime_new_usec(xmlrpc_env * const envP,
 
623
                         time_t       const secs,
 
624
                         unsigned int const usecs) {
449
625
 
450
626
    xmlrpc_value * valP;
451
 
    
 
627
 
452
628
    xmlrpc_createXmlrpcValue(envP, &valP);
453
629
 
454
630
    if (!envP->fault_occurred) {
455
631
        struct tm brokenTime;
456
632
        char timeString[64];
457
 
        
458
 
        valP->_type = XMLRPC_TYPE_DATETIME;
459
 
 
460
 
        gmtime_r(&value, &brokenTime);
461
 
        
 
633
 
 
634
        xmlrpc_gmtime(secs, &brokenTime);
 
635
 
462
636
        /* Note that this format is NOT ISO 8601 -- it's a bizarre
463
637
           hybrid of two ISO 8601 formats.
464
638
        */
465
639
        strftime(timeString, sizeof(timeString), "%Y%m%dT%H:%M:%S", 
466
640
                 &brokenTime);
 
641
 
 
642
        if (usecs != 0) {
 
643
            char usecString[64];
 
644
            assert(usecs < 1000000);
 
645
            snprintf(usecString, sizeof(usecString), ".%06u", usecs);
 
646
            STRSCAT(timeString, usecString);
 
647
        }
 
648
 
 
649
        valP->_type = XMLRPC_TYPE_DATETIME;
467
650
        
468
651
        XMLRPC_TYPED_MEM_BLOCK_INIT(
469
652
            char, envP, &valP->_block, strlen(timeString) + 1);
 
653
 
470
654
        if (!envP->fault_occurred) {
471
655
            char * const contents =
472
656
                XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
473
 
            
 
657
        
474
658
            strcpy(contents, timeString);
475
659
        }
476
 
        if (envP->fault_occurred)
 
660
        if (envP->fault_occurred) {
477
661
            free(valP);
 
662
            valP = NULL;
 
663
        }
478
664
    }
479
665
    return valP;
480
666
}
 
667
 
 
668
 
 
669
 
 
670
xmlrpc_value *
 
671
xmlrpc_datetime_new_sec(xmlrpc_env * const envP, 
 
672
                        time_t       const value) {
 
673
 
 
674
    return xmlrpc_datetime_new_usec(envP, value, 0);
 
675
}
 
676
 
 
677
 
 
678
 
 
679
#if XMLRPC_HAVE_TIMEVAL
 
680
 
 
681
xmlrpc_value *
 
682
xmlrpc_datetime_new_timeval(xmlrpc_env *   const envP, 
 
683
                            struct timeval const value) {
 
684
 
 
685
    return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_usec);
 
686
}
 
687
#endif
 
688
 
 
689
 
 
690
 
 
691
#if XMLRPC_HAVE_TIMESPEC
 
692
 
 
693
xmlrpc_value *
 
694
xmlrpc_datetime_new_timespec(xmlrpc_env *    const envP, 
 
695
                             struct timespec const value) {
 
696
 
 
697
    return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_nsec/1000);
 
698
}
 
699
#endif