| File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c |
| Location: | line 557, column 5 |
| Description: | Undefined or garbage value returned to caller |
| 1 | #include "xmlrpc_config.h" | |||
| 2 | ||||
| 3 | #define _XOPEN_SOURCE700 600 /* Make sure strdup() is in <string.h> */ | |||
| 4 | ||||
| 5 | #include <time.h> | |||
| 6 | #include <stdlib.h> | |||
| 7 | #include <string.h> | |||
| 8 | #include <ctype.h> | |||
| 9 | #include <assert.h> | |||
| 10 | #include <stdio.h> | |||
| 11 | #if MSVCRT0 | |||
| 12 | #include <windows.h> | |||
| 13 | #endif | |||
| 14 | ||||
| 15 | #include "bool.h" | |||
| 16 | #include "mallocvar.h" | |||
| 17 | ||||
| 18 | #include "xmlrpc-c/c_util.h" | |||
| 19 | #include "xmlrpc-c/base.h" | |||
| 20 | #include "xmlrpc-c/base_int.h" | |||
| 21 | #include "xmlrpc-c/string_int.h" | |||
| 22 | #include "xmlrpc-c/time_int.h" | |||
| 23 | ||||
| 24 | ||||
| 25 | #if HAVE_REGEX1 | |||
| 26 | #include <regex.h> | |||
| 27 | #endif | |||
| 28 | ||||
| 29 | #if MSVCRT0 | |||
| 30 | ||||
| 31 | static const __int64 SECS_BETWEEN_EPOCHS = 11644473600; | |||
| 32 | static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */ | |||
| 33 | ||||
| 34 | ||||
| 35 | void | |||
| 36 | UnixTimeToFileTime(time_t const t, | |||
| 37 | LPFILETIME const pft) { | |||
| 38 | ||||
| 39 | int64_t const ll = | |||
| 40 | Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS; | |||
| 41 | ||||
| 42 | pft->dwLowDateTime = (DWORD)ll; | |||
| 43 | pft->dwHighDateTime = (DWORD)(ll >> 32); | |||
| 44 | } | |||
| 45 | ||||
| 46 | ||||
| 47 | ||||
| 48 | void | |||
| 49 | UnixTimeToSystemTime(time_t const t, | |||
| 50 | LPSYSTEMTIME const pst) { | |||
| 51 | FILETIME ft; | |||
| 52 | ||||
| 53 | UnixTimeToFileTime(t, &ft); | |||
| 54 | FileTimeToSystemTime(&ft, pst); | |||
| 55 | } | |||
| 56 | ||||
| 57 | ||||
| 58 | ||||
| 59 | static void | |||
| 60 | UnixTimeFromFileTime(xmlrpc_env * const envP, | |||
| 61 | LPFILETIME const pft, | |||
| 62 | time_t * const timeValueP) { | |||
| 63 | ||||
| 64 | int64_t const WinEpoch100Ns = | |||
| 65 | ((int64_t)pft->dwHighDateTime << 32) + pft->dwLowDateTime; | |||
| 66 | int64_t const unixEpoch100Ns = | |||
| 67 | WinEpoch100Ns - (SECS_BETWEEN_EPOCHS * SECS_TO_100NS); | |||
| 68 | int64_t const unixEpochSeconds = | |||
| 69 | unixEpoch100Ns / SECS_TO_100NS; | |||
| 70 | ||||
| 71 | if ((time_t)unixEpochSeconds != unixEpochSeconds) { | |||
| 72 | /* Value is too big for a time_t; fail. */ | |||
| 73 | xmlrpc_faultf(envP, "Does not indicate a valid date"); | |||
| 74 | *timeValueP = (time_t)(-1); | |||
| 75 | } else | |||
| 76 | *timeValueP = (time_t)unixEpochSeconds; | |||
| 77 | } | |||
| 78 | ||||
| 79 | ||||
| 80 | ||||
| 81 | static void | |||
| 82 | UnixTimeFromSystemTime(xmlrpc_env * const envP, | |||
| 83 | LPSYSTEMTIME const pst, | |||
| 84 | time_t * const timeValueP) { | |||
| 85 | FILETIME filetime; | |||
| 86 | ||||
| 87 | SystemTimeToFileTime(pst, &filetime); | |||
| 88 | UnixTimeFromFileTime(envP, &filetime, timeValueP); | |||
| 89 | } | |||
| 90 | ||||
| 91 | #endif /* MSVCRT */ | |||
| 92 | ||||
| 93 | ||||
| 94 | ||||
| 95 | static void | |||
| 96 | validateDatetimeType(xmlrpc_env * const envP, | |||
| 97 | const xmlrpc_value * const valueP) { | |||
| 98 | ||||
| 99 | if (valueP->_type != XMLRPC_TYPE_DATETIME) { | |||
| 100 | xmlrpc_env_set_fault_formatted( | |||
| 101 | envP, XMLRPC_TYPE_ERROR(-501), "Value of type %s supplied where " | |||
| 102 | "type %s was expected.", | |||
| 103 | xmlrpc_type_name(valueP->_type), | |||
| 104 | xmlrpc_type_name(XMLRPC_TYPE_DATETIME)); | |||
| 105 | } | |||
| 106 | } | |||
| 107 | ||||
| 108 | ||||
| 109 | ||||
| 110 | void | |||
| 111 | xmlrpc_read_datetime(xmlrpc_env * const envP, | |||
| 112 | const xmlrpc_value * const valueP, | |||
| 113 | xmlrpc_datetime * const dtP) { | |||
| 114 | ||||
| 115 | validateDatetimeType(envP, valueP); | |||
| 116 | if (!envP->fault_occurred) { | |||
| 117 | *dtP = valueP->_value.dt; | |||
| 118 | } | |||
| 119 | } | |||
| 120 | ||||
| 121 | ||||
| 122 | ||||
| 123 | void | |||
| 124 | xmlrpc_read_datetime_str(xmlrpc_env * const envP, | |||
| 125 | const xmlrpc_value * const valueP, | |||
| 126 | const char ** const stringValueP) { | |||
| 127 | /*---------------------------------------------------------------------------- | |||
| 128 | This exists for backward compatibility. No normal modern program would | |||
| 129 | want to see a datetime value in this format. Note that the format isn't | |||
| 130 | even ISO 8601 -- it's a bizarre hybrid of two ISO 8601 formats. | |||
| 131 | ||||
| 132 | Do not extend this. | |||
| 133 | ||||
| 134 | This exists because Xmlrpc-c was at one time lazy and this was the only way | |||
| 135 | to extract the value. An xmlrpc_value in those days represented a datetime | |||
| 136 | with the actual XML-RPC wire format of a datetime, and this function simply | |||
| 137 | returned a copy of it. | |||
| 138 | -----------------------------------------------------------------------------*/ | |||
| 139 | validateDatetimeType(envP, valueP); | |||
| 140 | if (!envP->fault_occurred) { | |||
| 141 | time_t secs; | |||
| 142 | unsigned int usecs; | |||
| 143 | ||||
| 144 | xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs); | |||
| 145 | ||||
| 146 | if (!envP->fault_occurred) { | |||
| 147 | struct tm brokenTime; | |||
| 148 | char dtString[64]; | |||
| 149 | ||||
| 150 | xmlrpc_gmtime(secs, &brokenTime); | |||
| 151 | ||||
| 152 | /* Note that this format is NOT ISO 8601 -- it's a bizarre | |||
| 153 | hybrid of two ISO 8601 formats. | |||
| 154 | */ | |||
| 155 | strftime(dtString, sizeof(dtString), "%Y%m%dT%H:%M:%S", | |||
| 156 | &brokenTime); | |||
| 157 | ||||
| 158 | if (usecs != 0) { | |||
| 159 | char usecString[64]; | |||
| 160 | assert(usecs < 1000000)((usecs < 1000000) ? (void) (0) : __assert_fail ("usecs < 1000000" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 160, __PRETTY_FUNCTION__ )); | |||
| 161 | snprintf(usecString, sizeof(usecString), ".%06u", usecs); | |||
| 162 | STRSCAT(dtString, usecString)(__builtin_strncat ((dtString), (usecString), sizeof(dtString )-strlen(dtString)), *((dtString)+sizeof(dtString)-1) = '\0'); | |||
| 163 | } | |||
| 164 | ||||
| 165 | *stringValueP = strdup(dtString)(__extension__ (__builtin_constant_p (dtString) && (( size_t)(const void *)((dtString) + 1) - (size_t)(const void * )(dtString) == 1) ? (((const char *) (dtString))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (dtString) + 1; char *__retval = (char *) malloc (__len ); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval , dtString, __len); __retval; })) : __strdup (dtString))); | |||
| 166 | if (*stringValueP == NULL((void*)0)) | |||
| 167 | xmlrpc_faultf(envP, | |||
| 168 | "Unable to allocate memory for datetime string"); | |||
| 169 | } | |||
| 170 | } | |||
| 171 | } | |||
| 172 | ||||
| 173 | ||||
| 174 | ||||
| 175 | void | |||
| 176 | xmlrpc_read_datetime_str_old(xmlrpc_env * const envP, | |||
| 177 | const xmlrpc_value * const valueP, | |||
| 178 | const char ** const stringValueP) { | |||
| 179 | ||||
| 180 | assert(valueP->_cache)((valueP->_cache) ? (void) (0) : __assert_fail ("valueP->_cache" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 180, __PRETTY_FUNCTION__ )); | |||
| 181 | ||||
| 182 | validateDatetimeType(envP, valueP); | |||
| 183 | if (!envP->fault_occurred) { | |||
| 184 | const char ** const readBufferP = valueP->_cache; | |||
| 185 | ||||
| 186 | if (!*readBufferP) | |||
| 187 | /* Nobody's asked for the internal buffer before. Set it up. */ | |||
| 188 | xmlrpc_read_datetime_str(envP, valueP, readBufferP); | |||
| 189 | ||||
| 190 | *stringValueP = *readBufferP; | |||
| 191 | } | |||
| 192 | } | |||
| 193 | ||||
| 194 | ||||
| 195 | ||||
| 196 | void | |||
| 197 | xmlrpc_read_datetime_usec(xmlrpc_env * const envP, | |||
| 198 | const xmlrpc_value * const valueP, | |||
| 199 | time_t * const secsP, | |||
| 200 | unsigned int * const usecsP) { | |||
| 201 | ||||
| 202 | validateDatetimeType(envP, valueP); | |||
| 203 | ||||
| 204 | if (!envP->fault_occurred) { | |||
| 205 | if (valueP->_value.dt.Y < 1970) | |||
| 206 | xmlrpc_faultf(envP, "Year (%u) is too early to represent as " | |||
| 207 | "a standard Unix time", | |||
| 208 | valueP->_value.dt.Y); | |||
| 209 | else { | |||
| 210 | struct tm brokenTime; | |||
| 211 | const char * error; | |||
| 212 | ||||
| 213 | brokenTime.tm_sec = valueP->_value.dt.s; | |||
| 214 | brokenTime.tm_min = valueP->_value.dt.m; | |||
| 215 | brokenTime.tm_hour = valueP->_value.dt.h; | |||
| 216 | brokenTime.tm_mday = valueP->_value.dt.D; | |||
| 217 | brokenTime.tm_mon = valueP->_value.dt.M - 1; | |||
| 218 | brokenTime.tm_year = valueP->_value.dt.Y - 1900; | |||
| 219 | ||||
| 220 | xmlrpc_timegm(&brokenTime, secsP, &error); | |||
| 221 | ||||
| 222 | if (error) { | |||
| 223 | /* Ideally, this wouldn't be possible - it wouldn't be | |||
| 224 | possible to create an xmlrpc_value that doesn't actually | |||
| 225 | represent a real datetime. But today, we're lazy and | |||
| 226 | don't fully validate incoming XML-RPC <dateTime.iso8601> | |||
| 227 | elements, and we also have the legacy | |||
| 228 | xmlrpc_datetime_new_str() constructor to which the user | |||
| 229 | may feed garbage. | |||
| 230 | ||||
| 231 | We should tighten that up and then simply assert here that | |||
| 232 | xmlrpc_timegm() succeeded. | |||
| 233 | */ | |||
| 234 | xmlrpc_env_set_fault_formatted(envP, XMLRPC_PARSE_ERROR(-503), | |||
| 235 | "A datetime received in an XML-RPC message " | |||
| 236 | "or generated with legacy Xmlrpc-c facilities " | |||
| 237 | "does not validly describe a datetime. %s", | |||
| 238 | error); | |||
| 239 | xmlrpc_strfree(error); | |||
| 240 | } else | |||
| 241 | *usecsP = valueP->_value.dt.u; | |||
| 242 | } | |||
| 243 | } | |||
| 244 | } | |||
| 245 | ||||
| 246 | ||||
| 247 | ||||
| 248 | void | |||
| 249 | xmlrpc_read_datetime_sec(xmlrpc_env * const envP, | |||
| 250 | const xmlrpc_value * const valueP, | |||
| 251 | time_t * const timeValueP) { | |||
| 252 | ||||
| 253 | unsigned int usecs; | |||
| 254 | ||||
| 255 | xmlrpc_read_datetime_usec(envP, valueP, timeValueP, &usecs); | |||
| 256 | } | |||
| 257 | ||||
| 258 | ||||
| 259 | ||||
| 260 | #if XMLRPC_HAVE_TIMEVAL1 | |||
| 261 | ||||
| 262 | void | |||
| 263 | xmlrpc_read_datetime_timeval(xmlrpc_env * const envP, | |||
| 264 | const xmlrpc_value * const valueP, | |||
| 265 | struct timeval * const timeValueP) { | |||
| 266 | ||||
| 267 | time_t secs; | |||
| 268 | unsigned int usecs; | |||
| 269 | ||||
| 270 | xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs); | |||
| 271 | ||||
| 272 | timeValueP->tv_sec = secs; | |||
| 273 | timeValueP->tv_usec = usecs; | |||
| 274 | } | |||
| 275 | #endif | |||
| 276 | ||||
| 277 | ||||
| 278 | ||||
| 279 | #if XMLRPC_HAVE_TIMESPEC1 | |||
| 280 | ||||
| 281 | void | |||
| 282 | xmlrpc_read_datetime_timespec(xmlrpc_env * const envP, | |||
| 283 | const xmlrpc_value * const valueP, | |||
| 284 | struct timespec * const timeValueP) { | |||
| 285 | ||||
| 286 | time_t secs; | |||
| 287 | unsigned int usecs; | |||
| 288 | ||||
| 289 | xmlrpc_read_datetime_usec(envP, valueP, &secs, &usecs); | |||
| 290 | ||||
| 291 | timeValueP->tv_sec = secs; | |||
| 292 | timeValueP->tv_nsec = usecs * 1000; | |||
| 293 | } | |||
| 294 | #endif | |||
| 295 | ||||
| 296 | ||||
| 297 | ||||
| 298 | xmlrpc_value * | |||
| 299 | xmlrpc_datetime_new(xmlrpc_env * const envP, | |||
| 300 | xmlrpc_datetime const dt) { | |||
| 301 | ||||
| 302 | xmlrpc_value * valP; | |||
| 303 | ||||
| 304 | const char ** readBufferP; | |||
| 305 | ||||
| 306 | MALLOCVAR(readBufferP)readBufferP = malloc(sizeof(*readBufferP)); | |||
| 307 | ||||
| 308 | if (!readBufferP) | |||
| 309 | xmlrpc_faultf(envP, "Couldn't get memory for the cache part of the " | |||
| 310 | "XML-RPC datetime value object"); | |||
| 311 | ||||
| 312 | else { | |||
| 313 | *readBufferP = NULL((void*)0); | |||
| 314 | ||||
| 315 | xmlrpc_createXmlrpcValue(envP, &valP); | |||
| 316 | ||||
| 317 | if (!envP->fault_occurred) { | |||
| 318 | valP->_type = XMLRPC_TYPE_DATETIME; | |||
| 319 | ||||
| 320 | valP->_value.dt = dt; | |||
| 321 | ||||
| 322 | valP->_cache = readBufferP; | |||
| 323 | } | |||
| 324 | if (envP->fault_occurred) | |||
| 325 | free(readBufferP); | |||
| 326 | } | |||
| 327 | return valP; | |||
| 328 | } | |||
| 329 | ||||
| 330 | ||||
| 331 | ||||
| 332 | static void | |||
| 333 | parseDatetimeString(const char * const datetimeString, | |||
| 334 | xmlrpc_datetime * const dtP) { | |||
| 335 | ||||
| 336 | size_t const dtStrlen = strlen(datetimeString); | |||
| 337 | ||||
| 338 | char year[4+1]; | |||
| 339 | char month[2+1]; | |||
| 340 | char day[2+1]; | |||
| 341 | char hour[2+1]; | |||
| 342 | char minute[2+1]; | |||
| 343 | char second[2+1]; | |||
| 344 | ||||
| 345 | /* Because we require input to be valid: */ | |||
| 346 | assert(dtStrlen >= 17 && dtStrlen != 18 && dtStrlen <= 24)((dtStrlen >= 17 && dtStrlen != 18 && dtStrlen <= 24) ? (void) (0) : __assert_fail ("dtStrlen >= 17 && dtStrlen != 18 && dtStrlen <= 24" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 346, __PRETTY_FUNCTION__ )); | |||
| 347 | ||||
| 348 | year[0] = datetimeString[ 0]; | |||
| 349 | year[1] = datetimeString[ 1]; | |||
| 350 | year[2] = datetimeString[ 2]; | |||
| 351 | year[3] = datetimeString[ 3]; | |||
| 352 | year[4] = '\0'; | |||
| 353 | ||||
| 354 | month[0] = datetimeString[ 4]; | |||
| 355 | month[1] = datetimeString[ 5]; | |||
| 356 | month[2] = '\0'; | |||
| 357 | ||||
| 358 | day[0] = datetimeString[ 6]; | |||
| 359 | day[1] = datetimeString[ 7]; | |||
| 360 | day[2] = '\0'; | |||
| 361 | ||||
| 362 | assert(datetimeString[ 8] == 'T')((datetimeString[ 8] == 'T') ? (void) (0) : __assert_fail ("datetimeString[ 8] == 'T'" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 362, __PRETTY_FUNCTION__ )); | |||
| 363 | ||||
| 364 | hour[0] = datetimeString[ 9]; | |||
| 365 | hour[1] = datetimeString[10]; | |||
| 366 | hour[2] = '\0'; | |||
| 367 | ||||
| 368 | assert(datetimeString[11] == ':')((datetimeString[11] == ':') ? (void) (0) : __assert_fail ("datetimeString[11] == ':'" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 368, __PRETTY_FUNCTION__ )); | |||
| 369 | ||||
| 370 | minute[0] = datetimeString[12]; | |||
| 371 | minute[1] = datetimeString[13]; | |||
| 372 | minute[2] = '\0'; | |||
| 373 | ||||
| 374 | assert(datetimeString[14] == ':')((datetimeString[14] == ':') ? (void) (0) : __assert_fail ("datetimeString[14] == ':'" , "../../../../libs/xmlrpc-c/src/xmlrpc_datetime.c", 374, __PRETTY_FUNCTION__ )); | |||
| 375 | ||||
| 376 | second[0] = datetimeString[15]; | |||
| 377 | second[1] = datetimeString[16]; | |||
| 378 | second[2] = '\0'; | |||
| 379 | ||||
| 380 | if (dtStrlen > 17) { | |||
| 381 | size_t const pad = 24 - dtStrlen; | |||
| 382 | size_t i; | |||
| 383 | ||||
| 384 | dtP->u = atoi(&datetimeString[18]); | |||
| 385 | for (i = 0; i < pad; ++i) | |||
| 386 | dtP->u *= 10; | |||
| 387 | } else | |||
| 388 | dtP->u = 0; | |||
| 389 | ||||
| 390 | dtP->Y = atoi(year); | |||
| 391 | dtP->M = atoi(month); | |||
| 392 | dtP->D = atoi(day); | |||
| 393 | dtP->h = atoi(hour); | |||
| 394 | dtP->m = atoi(minute); | |||
| 395 | dtP->s = atoi(second); | |||
| 396 | } | |||
| 397 | ||||
| 398 | ||||
| 399 | ||||
| 400 | static void | |||
| 401 | validateFirst17(xmlrpc_env * const envP, | |||
| 402 | const char * const dt) { | |||
| 403 | /*---------------------------------------------------------------------------- | |||
| 404 | Assuming 'dt' is at least 17 characters long, validate that the first | |||
| 405 | 17 characters are a valid XML-RPC datetime, e.g. | |||
| 406 | "20080628T16:35:02" | |||
| 407 | -----------------------------------------------------------------------------*/ | |||
| 408 | unsigned int i; | |||
| 409 | ||||
| 410 | for (i = 0; i < 8 && !envP->fault_occurred; ++i) | |||
| 411 | if (!isdigit(dt[i])((*__ctype_b_loc ())[(int) ((dt[i]))] & (unsigned short int ) _ISdigit)) | |||
| 412 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[i]); | |||
| 413 | ||||
| 414 | if (dt[8] != 'T') | |||
| 415 | xmlrpc_faultf(envP, "9th character is '%c', not 'T'", dt[8]); | |||
| 416 | if (!isdigit(dt[9])((*__ctype_b_loc ())[(int) ((dt[9]))] & (unsigned short int ) _ISdigit)) | |||
| 417 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[9]); | |||
| 418 | if (!isdigit(dt[10])((*__ctype_b_loc ())[(int) ((dt[10]))] & (unsigned short int ) _ISdigit)) | |||
| 419 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[10]); | |||
| 420 | if (dt[11] != ':') | |||
| 421 | xmlrpc_faultf(envP, "Not a colon: '%c'", dt[11]); | |||
| 422 | if (!isdigit(dt[12])((*__ctype_b_loc ())[(int) ((dt[12]))] & (unsigned short int ) _ISdigit)) | |||
| 423 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[12]); | |||
| 424 | if (!isdigit(dt[13])((*__ctype_b_loc ())[(int) ((dt[13]))] & (unsigned short int ) _ISdigit)) | |||
| 425 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[13]); | |||
| 426 | if (dt[14] != ':') | |||
| 427 | xmlrpc_faultf(envP, "Not a colon: '%c'", dt[14]); | |||
| 428 | if (!isdigit(dt[15])((*__ctype_b_loc ())[(int) ((dt[15]))] & (unsigned short int ) _ISdigit)) | |||
| 429 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[15]); | |||
| 430 | if (!isdigit(dt[16])((*__ctype_b_loc ())[(int) ((dt[16]))] & (unsigned short int ) _ISdigit)) | |||
| 431 | xmlrpc_faultf(envP, "Not a digit: '%c'", dt[16]); | |||
| 432 | } | |||
| 433 | ||||
| 434 | ||||
| 435 | ||||
| 436 | static void | |||
| 437 | validateFractionalSeconds(xmlrpc_env * const envP, | |||
| 438 | const char * const dt) { | |||
| 439 | /*---------------------------------------------------------------------------- | |||
| 440 | Validate the fractional seconds part of the XML-RPC datetime string | |||
| 441 | 'dt', if any. That's the decimal point and everything following | |||
| 442 | it. | |||
| 443 | -----------------------------------------------------------------------------*/ | |||
| 444 | if (strlen(dt) > 17) { | |||
| 445 | if (dt[17] != '.') { | |||
| 446 | xmlrpc_faultf(envP, "'%c' where only a period is valid", dt[17]); | |||
| 447 | } else { | |||
| 448 | if (dt[18] == '\0') | |||
| 449 | xmlrpc_faultf(envP, "Nothing after decimal point"); | |||
| 450 | else { | |||
| 451 | unsigned int i; | |||
| 452 | for (i = 18; dt[i] != '\0' && !envP->fault_occurred; ++i) { | |||
| 453 | if (!isdigit(dt[i])((*__ctype_b_loc ())[(int) ((dt[i]))] & (unsigned short int ) _ISdigit)) | |||
| 454 | xmlrpc_faultf(envP, | |||
| 455 | "Non-digit in fractional seconds: '%c'", | |||
| 456 | dt[i]); | |||
| 457 | } | |||
| 458 | } | |||
| 459 | } | |||
| 460 | } | |||
| 461 | } | |||
| 462 | ||||
| 463 | ||||
| 464 | ||||
| 465 | static void | |||
| 466 | validateFormat(xmlrpc_env * const envP, | |||
| 467 | const char * const dt) { | |||
| 468 | ||||
| 469 | if (strlen(dt) < 17) | |||
| 470 | xmlrpc_faultf(envP, | |||
| 471 | "Invalid length of %u of datetime string. " | |||
| 472 | "Must be at least 17 characters", | |||
| 473 | (unsigned)strlen(dt)); | |||
| 474 | else { | |||
| 475 | validateFirst17(envP, dt); | |||
| 476 | ||||
| 477 | if (!envP->fault_occurred) | |||
| 478 | validateFractionalSeconds(envP, dt); | |||
| 479 | } | |||
| 480 | } | |||
| 481 | ||||
| 482 | ||||
| 483 | ||||
| 484 | /* Microsoft Visual C in debug mode produces code that complains about | |||
| 485 | returning an undefined value from xmlrpc_datetime_new_str(). It's a bogus | |||
| 486 | complaint, because this function is defined to return nothing meaningful | |||
| 487 | those cases. So we disable the check. | |||
| 488 | */ | |||
| 489 | #pragma runtime_checks("u", off) | |||
| 490 | ||||
| 491 | ||||
| 492 | ||||
| 493 | xmlrpc_value * | |||
| 494 | xmlrpc_datetime_new_str(xmlrpc_env * const envP, | |||
| 495 | const char * const datetimeString) { | |||
| 496 | /*---------------------------------------------------------------------------- | |||
| 497 | This exists only for backward compatibility. Originally, this was the | |||
| 498 | only way to create a datetime XML-RPC value, because we had a really | |||
| 499 | lazy implementation of XML-RPC serialization and parsing (basically, the | |||
| 500 | user did it!). | |||
| 501 | ||||
| 502 | Do not extend this. The user should use more normal C representations | |||
| 503 | of datetimes. | |||
| 504 | -----------------------------------------------------------------------------*/ | |||
| 505 | xmlrpc_value * retval; | |||
| 506 | ||||
| 507 | validateFormat(envP, datetimeString); | |||
| 508 | if (!envP->fault_occurred) { | |||
| 509 | xmlrpc_datetime dt; | |||
| 510 | ||||
| 511 | parseDatetimeString(datetimeString, &dt); | |||
| 512 | ||||
| 513 | /* Note that parseDatetimeString() can generate an invalid datetime | |||
| 514 | value, e.g. Hour 25 or February 30. Ideally, we would catch that | |||
| 515 | here, but due to laziness, we simply accept the possibility of | |||
| 516 | invalid xmlrpc_datetime in xmlrpc_value and whoever uses the the | |||
| 517 | xmlrpc_value has to deal with it. | |||
| 518 | */ | |||
| 519 | retval = xmlrpc_datetime_new(envP, dt); | |||
| 520 | } | |||
| 521 | ||||
| 522 | return retval; | |||
| 523 | } | |||
| 524 | ||||
| 525 | ||||
| 526 | ||||
| 527 | #pragma runtime_checks("u", restore) | |||
| 528 | ||||
| 529 | ||||
| 530 | ||||
| 531 | xmlrpc_value * | |||
| 532 | xmlrpc_datetime_new_usec(xmlrpc_env * const envP, | |||
| 533 | time_t const secs, | |||
| 534 | unsigned int const usecs) { | |||
| 535 | ||||
| 536 | xmlrpc_value * valueP; | |||
| 537 | ||||
| 538 | if (usecs >= 1000000) | |||
| 539 | xmlrpc_faultf(envP, "Number of fractional microseconds must be less " | |||
| 540 | "than one million. You specified %u", usecs); | |||
| 541 | else { | |||
| 542 | struct tm brokenTime; | |||
| 543 | xmlrpc_datetime dt; | |||
| 544 | ||||
| 545 | xmlrpc_gmtime(secs, &brokenTime); | |||
| 546 | ||||
| 547 | dt.s = brokenTime.tm_sec; | |||
| 548 | dt.m = brokenTime.tm_min; | |||
| 549 | dt.h = brokenTime.tm_hour; | |||
| 550 | dt.D = brokenTime.tm_mday; | |||
| 551 | dt.M = brokenTime.tm_mon + 1; | |||
| 552 | dt.Y = 1900 + brokenTime.tm_year; | |||
| 553 | dt.u = usecs; | |||
| 554 | ||||
| 555 | valueP = xmlrpc_datetime_new(envP, dt); | |||
| 556 | } | |||
| 557 | return valueP; | |||
| ||||
| 558 | } | |||
| 559 | ||||
| 560 | ||||
| 561 | ||||
| 562 | xmlrpc_value * | |||
| 563 | xmlrpc_datetime_new_sec(xmlrpc_env * const envP, | |||
| 564 | time_t const value) { | |||
| 565 | ||||
| 566 | return xmlrpc_datetime_new_usec(envP, value, 0); | |||
| 567 | } | |||
| 568 | ||||
| 569 | ||||
| 570 | ||||
| 571 | #if XMLRPC_HAVE_TIMEVAL1 | |||
| 572 | ||||
| 573 | xmlrpc_value * | |||
| 574 | xmlrpc_datetime_new_timeval(xmlrpc_env * const envP, | |||
| 575 | struct timeval const value) { | |||
| 576 | ||||
| 577 | return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_usec); | |||
| 578 | } | |||
| 579 | #endif | |||
| 580 | ||||
| 581 | ||||
| 582 | ||||
| 583 | #if XMLRPC_HAVE_TIMESPEC1 | |||
| 584 | ||||
| 585 | xmlrpc_value * | |||
| 586 | xmlrpc_datetime_new_timespec(xmlrpc_env * const envP, | |||
| 587 | struct timespec const value) { | |||
| 588 | ||||
| 589 | return xmlrpc_datetime_new_usec(envP, value.tv_sec, value.tv_nsec/1000); | |||
| ||||
| 590 | } | |||
| 591 | #endif | |||
| 592 | ||||
| 593 | ||||
| 594 | ||||
| 595 | void | |||
| 596 | xmlrpc_destroyDatetime(xmlrpc_value * const datetimeP) { | |||
| 597 | ||||
| 598 | const char ** const readBufferP = datetimeP->_cache; | |||
| 599 | ||||
| 600 | if (*readBufferP) | |||
| 601 | xmlrpc_strfree(*readBufferP); | |||
| 602 | ||||
| 603 | free(datetimeP->_cache); | |||
| 604 | } |