| File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/src/parse_value.c |
| Location: | line 459, column 23 |
| Description: | Array access (from variable 'string') results in a null pointer dereference |
| 1 | #include "xmlrpc_config.h" | |||
| 2 | ||||
| 3 | #include <stddef.h> | |||
| 4 | #include <stdlib.h> | |||
| 5 | #include <string.h> | |||
| 6 | #include <errno(*__errno_location ()).h> | |||
| 7 | #include <ctype.h> | |||
| 8 | #include <limits.h> | |||
| 9 | #include <float.h> | |||
| 10 | ||||
| 11 | #include "bool.h" | |||
| 12 | ||||
| 13 | #include "xmlrpc-c/base.h" | |||
| 14 | #include "xmlrpc-c/base_int.h" | |||
| 15 | #include "xmlrpc-c/string_int.h" | |||
| 16 | #include "xmlrpc-c/string_number.h" | |||
| 17 | #include "xmlrpc-c/util.h" | |||
| 18 | #include "xmlrpc-c/xmlparser.h" | |||
| 19 | #include "parse_datetime.h" | |||
| 20 | ||||
| 21 | #include "parse_value.h" | |||
| 22 | ||||
| 23 | ||||
| 24 | ||||
| 25 | static void | |||
| 26 | setParseFault(xmlrpc_env * const envP, | |||
| 27 | const char * const format, | |||
| 28 | ...) { | |||
| 29 | ||||
| 30 | va_list args; | |||
| 31 | va_start(args, format)__builtin_va_start(args, format); | |||
| 32 | xmlrpc_set_fault_formatted_v(envP, XMLRPC_PARSE_ERROR(-503), format, args); | |||
| 33 | va_end(args)__builtin_va_end(args); | |||
| 34 | } | |||
| 35 | ||||
| 36 | ||||
| 37 | ||||
| 38 | static void | |||
| 39 | parseArrayDataChild(xmlrpc_env * const envP, | |||
| 40 | xml_element * const childP, | |||
| 41 | unsigned int const maxRecursion, | |||
| 42 | xmlrpc_value * const arrayP) { | |||
| 43 | ||||
| 44 | const char * const elemName = xml_element_name(childP); | |||
| 45 | ||||
| 46 | if (!xmlrpc_streq(elemName, "value")) | |||
| 47 | setParseFault(envP, "<data> element has <%s> child. " | |||
| 48 | "Only <value> makes sense.", elemName); | |||
| 49 | else { | |||
| 50 | xmlrpc_value * itemP; | |||
| 51 | ||||
| 52 | xmlrpc_parseValue(envP, maxRecursion-1, childP, &itemP); | |||
| 53 | ||||
| 54 | if (!envP->fault_occurred) { | |||
| 55 | xmlrpc_array_append_item(envP, arrayP, itemP); | |||
| 56 | ||||
| 57 | xmlrpc_DECREF(itemP); | |||
| 58 | } | |||
| 59 | } | |||
| 60 | } | |||
| 61 | ||||
| 62 | ||||
| 63 | ||||
| 64 | static void | |||
| 65 | parseArray(xmlrpc_env * const envP, | |||
| 66 | unsigned int const maxRecursion, | |||
| 67 | xml_element * const arrayElemP, | |||
| 68 | xmlrpc_value ** const arrayPP) { | |||
| 69 | ||||
| 70 | xmlrpc_value * arrayP; | |||
| 71 | ||||
| 72 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 72); while (0 ); | |||
| 73 | XMLRPC_ASSERT(arrayElemP != NULL)do if (!(arrayElemP != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 73); while (0); | |||
| 74 | ||||
| 75 | arrayP = xmlrpc_array_new(envP); | |||
| 76 | if (!envP->fault_occurred) { | |||
| 77 | size_t const childCount = xml_element_children_size(arrayElemP); | |||
| 78 | ||||
| 79 | if (childCount != 1) | |||
| 80 | setParseFault(envP, | |||
| 81 | "<array> element has %u children. Only one <data> " | |||
| 82 | "makes sense.", (unsigned int)childCount); | |||
| 83 | else { | |||
| 84 | xml_element * const dataElemP = | |||
| 85 | xml_element_children(arrayElemP)[0]; | |||
| 86 | const char * const elemName = xml_element_name(dataElemP); | |||
| 87 | ||||
| 88 | if (!xmlrpc_streq(elemName, "data")) | |||
| 89 | setParseFault(envP, | |||
| 90 | "<array> element has <%s> child. Only <data> " | |||
| 91 | "makes sense.", elemName); | |||
| 92 | else { | |||
| 93 | xml_element ** const values = xml_element_children(dataElemP); | |||
| 94 | unsigned int const size = xml_element_children_size(dataElemP); | |||
| 95 | ||||
| 96 | unsigned int i; | |||
| 97 | ||||
| 98 | for (i = 0; i < size && !envP->fault_occurred; ++i) | |||
| 99 | parseArrayDataChild(envP, values[i], maxRecursion, arrayP); | |||
| 100 | } | |||
| 101 | } | |||
| 102 | if (envP->fault_occurred) | |||
| 103 | xmlrpc_DECREF(arrayP); | |||
| 104 | else | |||
| 105 | *arrayPP = arrayP; | |||
| 106 | } | |||
| 107 | } | |||
| 108 | ||||
| 109 | ||||
| 110 | ||||
| 111 | static void | |||
| 112 | parseName(xmlrpc_env * const envP, | |||
| 113 | xml_element * const nameElemP, | |||
| 114 | xmlrpc_value ** const valuePP) { | |||
| 115 | ||||
| 116 | size_t const childCount = xml_element_children_size(nameElemP); | |||
| 117 | ||||
| 118 | if (childCount > 0) | |||
| 119 | setParseFault(envP, "<name> element has %u children. " | |||
| 120 | "Should have none.", (unsigned int)childCount); | |||
| 121 | else { | |||
| 122 | const char * const cdata = xml_element_cdata(nameElemP); | |||
| 123 | size_t const cdataSize = xml_element_cdata_size(nameElemP); | |||
| 124 | ||||
| 125 | *valuePP = xmlrpc_string_new_lp(envP, cdataSize, cdata); | |||
| 126 | } | |||
| 127 | } | |||
| 128 | ||||
| 129 | ||||
| 130 | ||||
| 131 | static void | |||
| 132 | getNameChild(xmlrpc_env * const envP, | |||
| 133 | xml_element * const parentP, | |||
| 134 | xml_element * * const childPP) { | |||
| 135 | ||||
| 136 | xml_element ** const children = xml_element_children(parentP); | |||
| 137 | size_t const childCount = xml_element_children_size(parentP); | |||
| 138 | ||||
| 139 | xml_element * childP; | |||
| 140 | unsigned int i; | |||
| 141 | ||||
| 142 | for (i = 0, childP = NULL((void*)0); i < childCount && !childP; ++i) { | |||
| 143 | if (xmlrpc_streq(xml_element_name(children[i]), "name")) | |||
| 144 | childP = children[i]; | |||
| 145 | } | |||
| 146 | if (!childP) | |||
| 147 | xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR(-503), | |||
| 148 | "<member> has no <name> child"); | |||
| 149 | else | |||
| 150 | *childPP = childP; | |||
| 151 | } | |||
| 152 | ||||
| 153 | ||||
| 154 | ||||
| 155 | static void | |||
| 156 | getValueChild(xmlrpc_env * const envP, | |||
| 157 | xml_element * const parentP, | |||
| 158 | xml_element * * const childPP) { | |||
| 159 | ||||
| 160 | xml_element ** const children = xml_element_children(parentP); | |||
| 161 | size_t const childCount = xml_element_children_size(parentP); | |||
| 162 | ||||
| 163 | xml_element * childP; | |||
| 164 | unsigned int i; | |||
| 165 | ||||
| 166 | for (i = 0, childP = NULL((void*)0); i < childCount && !childP; ++i) { | |||
| 167 | if (xmlrpc_streq(xml_element_name(children[i]), "value")) | |||
| 168 | childP = children[i]; | |||
| 169 | } | |||
| 170 | if (!childP) | |||
| 171 | xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR(-503), | |||
| 172 | "<member> has no <value> child"); | |||
| 173 | else | |||
| 174 | *childPP = childP; | |||
| 175 | } | |||
| 176 | ||||
| 177 | ||||
| 178 | ||||
| 179 | static void | |||
| 180 | parseMember(xmlrpc_env * const envP, | |||
| 181 | xml_element * const memberP, | |||
| 182 | unsigned int const maxRecursion, | |||
| 183 | xmlrpc_value ** const keyPP, | |||
| 184 | xmlrpc_value ** const valuePP) { | |||
| 185 | ||||
| 186 | size_t const childCount = xml_element_children_size(memberP); | |||
| 187 | ||||
| 188 | if (childCount != 2) | |||
| 189 | setParseFault(envP, | |||
| 190 | "<member> element has %u children. Only one <name> and " | |||
| 191 | "one <value> make sense.", (unsigned int)childCount); | |||
| 192 | else { | |||
| 193 | xml_element * nameElemP = NULL((void*)0); | |||
| 194 | ||||
| 195 | getNameChild(envP, memberP, &nameElemP); | |||
| 196 | ||||
| 197 | if (!envP->fault_occurred) { | |||
| 198 | parseName(envP, nameElemP, keyPP); | |||
| 199 | ||||
| 200 | if (!envP->fault_occurred) { | |||
| 201 | xml_element * valueElemP = NULL((void*)0); | |||
| 202 | ||||
| 203 | getValueChild(envP, memberP, &valueElemP); | |||
| 204 | ||||
| 205 | if (!envP->fault_occurred) | |||
| 206 | xmlrpc_parseValue(envP, maxRecursion-1, valueElemP, | |||
| 207 | valuePP); | |||
| 208 | ||||
| 209 | if (envP->fault_occurred) | |||
| 210 | xmlrpc_DECREF(*keyPP); | |||
| 211 | } | |||
| 212 | } | |||
| 213 | } | |||
| 214 | } | |||
| 215 | ||||
| 216 | ||||
| 217 | ||||
| 218 | static void | |||
| 219 | parseStruct(xmlrpc_env * const envP, | |||
| 220 | unsigned int const maxRecursion, | |||
| 221 | xml_element * const elemP, | |||
| 222 | xmlrpc_value ** const structPP) { | |||
| 223 | /*---------------------------------------------------------------------------- | |||
| 224 | Parse the <struct> element 'elemP'. | |||
| 225 | -----------------------------------------------------------------------------*/ | |||
| 226 | xmlrpc_value * structP; | |||
| 227 | ||||
| 228 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 228); while ( 0); | |||
| 229 | XMLRPC_ASSERT(elemP != NULL)do if (!(elemP != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 229); while (0); | |||
| 230 | ||||
| 231 | structP = xmlrpc_struct_new(envP); | |||
| 232 | if (!envP->fault_occurred) { | |||
| 233 | /* Iterate over our children, extracting key/value pairs. */ | |||
| 234 | ||||
| 235 | xml_element ** const members = xml_element_children(elemP); | |||
| 236 | unsigned int const size = xml_element_children_size(elemP); | |||
| 237 | ||||
| 238 | unsigned int i; | |||
| 239 | ||||
| 240 | for (i = 0; i < size && !envP->fault_occurred; ++i) { | |||
| 241 | const char * const elemName = xml_element_name(members[i]); | |||
| 242 | ||||
| 243 | if (!xmlrpc_streq(elemName, "member")) | |||
| 244 | setParseFault(envP, "<%s> element found where only <member> " | |||
| 245 | "makes sense", elemName); | |||
| 246 | else { | |||
| 247 | xmlrpc_value * keyP = NULL((void*)0); | |||
| 248 | xmlrpc_value * valueP; | |||
| 249 | ||||
| 250 | parseMember(envP, members[i], maxRecursion, &keyP, &valueP); | |||
| 251 | ||||
| 252 | if (!envP->fault_occurred) { | |||
| 253 | xmlrpc_struct_set_value_v(envP, structP, keyP, valueP); | |||
| 254 | ||||
| 255 | xmlrpc_DECREF(keyP); | |||
| 256 | xmlrpc_DECREF(valueP); | |||
| 257 | } | |||
| 258 | } | |||
| 259 | } | |||
| 260 | if (envP->fault_occurred) | |||
| 261 | xmlrpc_DECREF(structP); | |||
| 262 | else | |||
| 263 | *structPP = structP; | |||
| 264 | } | |||
| 265 | } | |||
| 266 | ||||
| 267 | ||||
| 268 | ||||
| 269 | static void | |||
| 270 | parseInt(xmlrpc_env * const envP, | |||
| 271 | const char * const str, | |||
| 272 | xmlrpc_value ** const valuePP) { | |||
| 273 | /*---------------------------------------------------------------------------- | |||
| 274 | Parse the content of a <int> XML-RPC XML element, e.g. "34". | |||
| 275 | ||||
| 276 | 'str' is that content. | |||
| 277 | -----------------------------------------------------------------------------*/ | |||
| 278 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 278); while ( 0); | |||
| 279 | XMLRPC_ASSERT_PTR_OK(str)do if (!((str) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 279); while (0); | |||
| 280 | ||||
| 281 | if (str[0] == '\0') | |||
| 282 | setParseFault(envP, "<int> XML element content is empty"); | |||
| 283 | else if (isspace(str[0])((*__ctype_b_loc ())[(int) ((str[0]))] & (unsigned short int ) _ISspace)) | |||
| 284 | setParseFault(envP, "<int> content '%s' starts with white space", | |||
| 285 | str); | |||
| 286 | else { | |||
| 287 | long i; | |||
| 288 | char * tail; | |||
| 289 | ||||
| 290 | errno(*__errno_location ()) = 0; | |||
| 291 | i = strtol(str, &tail, 10); | |||
| 292 | ||||
| 293 | /* Look for ERANGE. */ | |||
| 294 | if (errno(*__errno_location ()) == ERANGE34) | |||
| 295 | setParseFault(envP, "<int> XML element value '%s' represents a " | |||
| 296 | "number beyond the range that " | |||
| 297 | "XML-RPC allows (%d - %d)", str, | |||
| 298 | XMLRPC_INT32_MIN(-0x7fffffff - 1), XMLRPC_INT32_MAX0x7fffffff); | |||
| 299 | else if (errno(*__errno_location ()) != 0) | |||
| 300 | setParseFault(envP, "unexpected error parsing <int> XML element " | |||
| 301 | "value '%s'. strtol() failed with errno %d (%s)", | |||
| 302 | str, errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
| 303 | else { | |||
| 304 | /* Look for out-of-range errors which didn't produce ERANGE. */ | |||
| 305 | if (i < XMLRPC_INT32_MIN(-0x7fffffff - 1)) | |||
| 306 | setParseFault(envP, | |||
| 307 | "<int> value %ld is below the range allowed " | |||
| 308 | "by XML-RPC (minimum is %d)", | |||
| 309 | i, XMLRPC_INT32_MIN(-0x7fffffff - 1)); | |||
| 310 | else if (i > XMLRPC_INT32_MAX0x7fffffff) | |||
| 311 | setParseFault(envP, | |||
| 312 | "<int> value %ld is above the range allowed " | |||
| 313 | "by XML-RPC (maximum is %d)", | |||
| 314 | i, XMLRPC_INT32_MAX0x7fffffff); | |||
| 315 | else { | |||
| 316 | if (tail[0] != '\0') | |||
| 317 | setParseFault(envP, | |||
| 318 | "<int> value '%s' contains non-numerical " | |||
| 319 | "junk: '%s'", str, tail); | |||
| 320 | else | |||
| 321 | *valuePP = xmlrpc_int_new(envP, i); | |||
| 322 | } | |||
| 323 | } | |||
| 324 | } | |||
| 325 | } | |||
| 326 | ||||
| 327 | ||||
| 328 | ||||
| 329 | static void | |||
| 330 | parseBoolean(xmlrpc_env * const envP, | |||
| 331 | const char * const str, | |||
| 332 | xmlrpc_value ** const valuePP) { | |||
| 333 | /*---------------------------------------------------------------------------- | |||
| 334 | Parse the content of a <boolean> XML-RPC XML element, e.g. "1". | |||
| 335 | ||||
| 336 | 'str' is that content. | |||
| 337 | -----------------------------------------------------------------------------*/ | |||
| 338 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 338); while ( 0); | |||
| 339 | XMLRPC_ASSERT_PTR_OK(str)do if (!((str) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 339); while (0); | |||
| 340 | ||||
| 341 | if (xmlrpc_streq(str, "0") || xmlrpc_streq(str, "1")) | |||
| 342 | *valuePP = xmlrpc_bool_new(envP, xmlrpc_streq(str, "1") ? 1 : 0); | |||
| 343 | else | |||
| 344 | setParseFault(envP, "<boolean> XML element content must be either " | |||
| 345 | "'0' or '1' according to XML-RPC. This one has '%s'", | |||
| 346 | str); | |||
| 347 | } | |||
| 348 | ||||
| 349 | ||||
| 350 | ||||
| 351 | static void | |||
| 352 | scanAndValidateDoubleString(xmlrpc_env * const envP, | |||
| 353 | const char * const string, | |||
| 354 | const char ** const mantissaP, | |||
| 355 | const char ** const mantissaEndP, | |||
| 356 | const char ** const fractionP, | |||
| 357 | const char ** const fractionEndP) { | |||
| 358 | ||||
| 359 | const char * mantissa; | |||
| 360 | const char * dp; | |||
| 361 | const char * p; | |||
| 362 | ||||
| 363 | if (string[0] == '-' || string[0] == '+') | |||
| 364 | mantissa = &string[1]; | |||
| 365 | else | |||
| 366 | mantissa = &string[0]; | |||
| 367 | ||||
| 368 | for (p = mantissa, dp = NULL((void*)0); *p; ++p) { | |||
| 369 | char const c = *p; | |||
| 370 | if (c == '.') { | |||
| 371 | if (dp) { | |||
| 372 | setParseFault(envP, "Two decimal points"); | |||
| 373 | return; | |||
| 374 | } else | |||
| 375 | dp = p; | |||
| 376 | } else if (c < '0' || c > '9') { | |||
| 377 | setParseFault(envP, "Garbage (not sign, digit, or period) " | |||
| 378 | "starting at '%s'", p); | |||
| 379 | return; | |||
| 380 | } | |||
| 381 | } | |||
| 382 | *mantissaP = mantissa; | |||
| 383 | if (dp) { | |||
| 384 | *mantissaEndP = dp; | |||
| 385 | *fractionP = dp+1; | |||
| 386 | *fractionEndP = p; | |||
| 387 | } else { | |||
| 388 | *mantissaEndP = p; | |||
| 389 | *fractionP = p; | |||
| 390 | *fractionEndP = p; | |||
| 391 | } | |||
| 392 | } | |||
| 393 | ||||
| 394 | ||||
| 395 | ||||
| 396 | static bool | |||
| 397 | isInfinite(double const value) { | |||
| 398 | ||||
| 399 | return value > DBL_MAX1.7976931348623157e+308; | |||
| 400 | } | |||
| 401 | ||||
| 402 | ||||
| 403 | ||||
| 404 | static void | |||
| 405 | parseDoubleString(xmlrpc_env * const envP, | |||
| 406 | const char * const string, | |||
| 407 | double * const valueP) { | |||
| 408 | /*---------------------------------------------------------------------------- | |||
| 409 | Turn e.g. "4.3" into 4.3 . | |||
| 410 | -----------------------------------------------------------------------------*/ | |||
| 411 | /* strtod() is no good for this because it is designed for human | |||
| 412 | interfaces; it parses according to locale. As a practical | |||
| 413 | matter that sometimes means that it does not recognize "." as a | |||
| 414 | decimal point. In XML-RPC, "." is a decimal point. | |||
| 415 | ||||
| 416 | Design note: in my experiments, using strtod() was 10 times | |||
| 417 | slower than using this function. | |||
| 418 | */ | |||
| 419 | const char * mantissa = NULL((void*)0); | |||
| 420 | const char * mantissaEnd = NULL((void*)0); | |||
| 421 | const char * fraction = NULL((void*)0); | |||
| 422 | const char * fractionEnd = NULL((void*)0); | |||
| 423 | ||||
| 424 | scanAndValidateDoubleString(envP, string, &mantissa, &mantissaEnd, | |||
| 425 | &fraction, &fractionEnd); | |||
| 426 | ||||
| 427 | if (!envP->fault_occurred) { | |||
| 428 | double accum; | |||
| 429 | ||||
| 430 | accum = 0.0; | |||
| 431 | ||||
| 432 | if (mantissa == mantissaEnd && fraction == fractionEnd) { | |||
| 433 | setParseFault(envP, "No digits"); | |||
| 434 | return; | |||
| 435 | } | |||
| 436 | { | |||
| 437 | /* Add in the whole part */ | |||
| 438 | const char * p; | |||
| 439 | ||||
| 440 | for (p = mantissa; p < mantissaEnd; ++p) { | |||
| 441 | accum *= 10; | |||
| 442 | accum += (*p - '0'); | |||
| 443 | } | |||
| 444 | } | |||
| 445 | { | |||
| 446 | /* Add in the fractional part */ | |||
| 447 | double significance; | |||
| 448 | const char * p; | |||
| 449 | for (significance = 0.1, p = fraction; | |||
| 450 | p < fractionEnd; | |||
| 451 | ++p, significance *= 0.1) { | |||
| 452 | ||||
| 453 | accum += (*p - '0') * significance; | |||
| 454 | } | |||
| 455 | } | |||
| 456 | if (isInfinite(accum)) | |||
| 457 | setParseFault(envP, "Value exceeds the size allowed by XML-RPC"); | |||
| 458 | else | |||
| 459 | *valueP = string[0] == '-' ? (- accum) : accum; | |||
| ||||
| 460 | } | |||
| 461 | } | |||
| 462 | ||||
| 463 | ||||
| 464 | ||||
| 465 | static void | |||
| 466 | parseDoubleStringStrtod(const char * const str, | |||
| 467 | bool * const failedP, | |||
| 468 | double * const valueP) { | |||
| 469 | ||||
| 470 | if (strlen(str) == 0) { | |||
| 471 | /* strtod() happily interprets empty string as 0.0. We don't think | |||
| 472 | the user will appreciate that XML-RPC extension. | |||
| 473 | */ | |||
| 474 | *failedP = true; | |||
| 475 | } else { | |||
| 476 | char * tail; | |||
| 477 | ||||
| 478 | errno(*__errno_location ()) = 0; | |||
| 479 | ||||
| 480 | *valueP = strtod(str, &tail); | |||
| 481 | ||||
| 482 | if (errno(*__errno_location ()) != 0) | |||
| 483 | *failedP = true; | |||
| 484 | else { | |||
| 485 | if (tail[0] != '\0') | |||
| 486 | *failedP = true; | |||
| 487 | else | |||
| 488 | *failedP = false; | |||
| 489 | } | |||
| 490 | } | |||
| 491 | } | |||
| 492 | ||||
| 493 | ||||
| 494 | ||||
| 495 | static void | |||
| 496 | parseDouble(xmlrpc_env * const envP, | |||
| 497 | const char * const str, | |||
| 498 | xmlrpc_value ** const valuePP) { | |||
| 499 | /*---------------------------------------------------------------------------- | |||
| 500 | Parse the content of a <double> XML-RPC XML element, e.g. "34.5". | |||
| 501 | ||||
| 502 | 'str' is that content. | |||
| 503 | -----------------------------------------------------------------------------*/ | |||
| 504 | xmlrpc_env parseEnv; | |||
| 505 | double valueDouble = 0; | |||
| 506 | ||||
| 507 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 507); while ( 0); | |||
| 508 | XMLRPC_ASSERT_PTR_OK(str)do if (!((str) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 508); while (0); | |||
| 509 | ||||
| 510 | xmlrpc_env_init(&parseEnv); | |||
| 511 | ||||
| 512 | parseDoubleString(&parseEnv, str, &valueDouble); | |||
| 513 | ||||
| 514 | if (parseEnv.fault_occurred) { | |||
| 515 | /* As an alternative, try a strtod() parsing. strtod() | |||
| 516 | accepts other forms, e.g. "3.4E6"; "3,4"; " 3.4". These | |||
| 517 | are not permitted by XML-RPC, but an almost-XML-RPC partner | |||
| 518 | might use one. In fact, for many years, Xmlrpc-c generated | |||
| 519 | such alternatives (by mistake). | |||
| 520 | */ | |||
| 521 | bool failed; | |||
| 522 | parseDoubleStringStrtod(str, &failed, &valueDouble); | |||
| 523 | if (failed) | |||
| 524 | setParseFault(envP, "<double> element value '%s' is not a valid " | |||
| 525 | "floating point number. %s", | |||
| 526 | str, parseEnv.fault_string); | |||
| 527 | } | |||
| 528 | ||||
| 529 | if (!envP->fault_occurred) | |||
| 530 | *valuePP = xmlrpc_double_new(envP, valueDouble); | |||
| 531 | ||||
| 532 | xmlrpc_env_clean(&parseEnv); | |||
| 533 | } | |||
| 534 | ||||
| 535 | ||||
| 536 | ||||
| 537 | static void | |||
| 538 | parseBase64(xmlrpc_env * const envP, | |||
| 539 | const char * const str, | |||
| 540 | size_t const strLength, | |||
| 541 | xmlrpc_value ** const valuePP) { | |||
| 542 | /*---------------------------------------------------------------------------- | |||
| 543 | Parse the content of a <base64> XML-RPC XML element, e.g. "FD32YY". | |||
| 544 | ||||
| 545 | 'str' is that content. | |||
| 546 | -----------------------------------------------------------------------------*/ | |||
| 547 | xmlrpc_mem_block * decoded; | |||
| 548 | ||||
| 549 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 549); while ( 0); | |||
| 550 | XMLRPC_ASSERT_PTR_OK(str)do if (!((str) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 550); while (0); | |||
| 551 | ||||
| 552 | decoded = xmlrpc_base64_decode(envP, str, strLength); | |||
| 553 | if (!envP->fault_occurred) { | |||
| 554 | unsigned char * const bytes = | |||
| 555 | XMLRPC_MEMBLOCK_CONTENTS(unsigned char, decoded)((unsigned char*) xmlrpc_mem_block_contents(decoded)); | |||
| 556 | size_t const byteCount = | |||
| 557 | XMLRPC_MEMBLOCK_SIZE(unsigned char, decoded)(xmlrpc_mem_block_size(decoded) / sizeof(unsigned char)); | |||
| 558 | ||||
| 559 | *valuePP = xmlrpc_base64_new(envP, byteCount, bytes); | |||
| 560 | ||||
| 561 | XMLRPC_MEMBLOCK_FREE(unsigned char, decoded)xmlrpc_mem_block_free(decoded); | |||
| 562 | } | |||
| 563 | } | |||
| 564 | ||||
| 565 | ||||
| 566 | ||||
| 567 | static void | |||
| 568 | parseI8(xmlrpc_env * const envP, | |||
| 569 | const char * const str, | |||
| 570 | xmlrpc_value ** const valuePP) { | |||
| 571 | /*---------------------------------------------------------------------------- | |||
| 572 | Parse the content of a <i8> XML-RPC XML element, e.g. "34". | |||
| 573 | ||||
| 574 | 'str' is that content. | |||
| 575 | -----------------------------------------------------------------------------*/ | |||
| 576 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 576); while ( 0); | |||
| 577 | XMLRPC_ASSERT_PTR_OK(str)do if (!((str) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 577); while (0); | |||
| 578 | ||||
| 579 | if (str[0] == '\0') | |||
| 580 | setParseFault(envP, "<i8> XML element content is empty"); | |||
| 581 | else if (isspace(str[0])((*__ctype_b_loc ())[(int) ((str[0]))] & (unsigned short int ) _ISspace)) | |||
| 582 | setParseFault(envP, | |||
| 583 | "<i8> content '%s' starts with white space", str); | |||
| 584 | else { | |||
| 585 | xmlrpc_int64 i; | |||
| 586 | xmlrpc_env env; | |||
| 587 | ||||
| 588 | xmlrpc_env_init(&env); | |||
| 589 | ||||
| 590 | xmlrpc_parse_int64(&env, str, &i); | |||
| 591 | ||||
| 592 | if (env.fault_occurred) | |||
| 593 | setParseFault(envP, "<i8> XML element value '%s' is invalid " | |||
| 594 | "because it does not represent " | |||
| 595 | "a 64 bit integer. %s", env.fault_string); | |||
| 596 | else | |||
| 597 | *valuePP = xmlrpc_i8_new(envP, i); | |||
| 598 | ||||
| 599 | xmlrpc_env_clean(&env); | |||
| 600 | } | |||
| 601 | } | |||
| 602 | ||||
| 603 | ||||
| 604 | ||||
| 605 | static void | |||
| 606 | parseSimpleValueCdata(xmlrpc_env * const envP, | |||
| 607 | const char * const elementName, | |||
| 608 | const char * const cdata, | |||
| 609 | size_t const cdataLength, | |||
| 610 | xmlrpc_value ** const valuePP) { | |||
| 611 | /*---------------------------------------------------------------------------- | |||
| 612 | Parse an XML element is supposedly a data type element such as | |||
| 613 | <string>. Its name is 'elementName', and it has no children, but | |||
| 614 | contains cdata 'cdata', which is 'dataLength' characters long. | |||
| 615 | -----------------------------------------------------------------------------*/ | |||
| 616 | /* We need to straighten out the whole character set / encoding thing | |||
| 617 | some day. What is 'cdata', and what should it be? Does it have | |||
| 618 | embedded NUL? Some of the code here assumes it doesn't. Is it | |||
| 619 | text? | |||
| 620 | ||||
| 621 | The <string> parser assumes it's UTF 8 with embedded NULs. | |||
| 622 | But the <int> parser will get terribly confused if there are any | |||
| 623 | UTF-8 multibyte sequences or NUL characters. So will most of the | |||
| 624 | others. | |||
| 625 | ||||
| 626 | The "ex:XXX" element names are what the Apache XML-RPC facility | |||
| 627 | uses: http://ws.apache.org/xmlrpc/types.html. (Technically, it | |||
| 628 | isn't "ex" but an arbitrary prefix that identifies a namespace | |||
| 629 | declared earlier in the XML document -- this is an XML thing. | |||
| 630 | But we aren't nearly sophisticated enough to use real XML | |||
| 631 | namespaces, so we exploit the fact that XML-RPC actually uses | |||
| 632 | "ex"). | |||
| 633 | ||||
| 634 | "i1" and "i2" are just from my imagination. | |||
| 635 | */ | |||
| 636 | ||||
| 637 | if (xmlrpc_streq(elementName, "int") || | |||
| 638 | xmlrpc_streq(elementName, "i4") || | |||
| 639 | xmlrpc_streq(elementName, "i1") || | |||
| 640 | xmlrpc_streq(elementName, "i2") || | |||
| 641 | xmlrpc_streq(elementName, "ex:i1") || | |||
| 642 | xmlrpc_streq(elementName, "ex:i2")) | |||
| 643 | parseInt(envP, cdata, valuePP); | |||
| 644 | else if (xmlrpc_streq(elementName, "boolean")) | |||
| 645 | parseBoolean(envP, cdata, valuePP); | |||
| 646 | else if (xmlrpc_streq(elementName, "double")) | |||
| 647 | parseDouble(envP, cdata, valuePP); | |||
| 648 | else if (xmlrpc_streq(elementName, "dateTime.iso8601")) | |||
| 649 | xmlrpc_parseDatetime(envP, cdata, valuePP); | |||
| 650 | else if (xmlrpc_streq(elementName, "string")) | |||
| 651 | *valuePP = xmlrpc_string_new_lp(envP, cdataLength, cdata); | |||
| 652 | else if (xmlrpc_streq(elementName, "base64")) | |||
| 653 | parseBase64(envP, cdata, cdataLength, valuePP); | |||
| 654 | else if (xmlrpc_streq(elementName, "nil") || | |||
| 655 | xmlrpc_streq(elementName, "ex:nil")) | |||
| 656 | *valuePP = xmlrpc_nil_new(envP); | |||
| 657 | else if (xmlrpc_streq(elementName, "i8") || | |||
| 658 | xmlrpc_streq(elementName, "ex:i8")) | |||
| 659 | parseI8(envP, cdata, valuePP); | |||
| 660 | else | |||
| 661 | setParseFault(envP, "Unknown value type -- XML element is named " | |||
| 662 | "<%s>", elementName); | |||
| 663 | } | |||
| 664 | ||||
| 665 | ||||
| 666 | ||||
| 667 | static void | |||
| 668 | parseSimpleValue(xmlrpc_env * const envP, | |||
| 669 | xml_element * const elemP, | |||
| 670 | xmlrpc_value ** const valuePP) { | |||
| 671 | ||||
| 672 | size_t childCount = xml_element_children_size(elemP); | |||
| 673 | ||||
| 674 | if (childCount > 0) | |||
| 675 | setParseFault(envP, "The child of a <value> element " | |||
| 676 | "is neither <array> nor <struct>, " | |||
| 677 | "but has %u child elements of its own.", | |||
| 678 | (unsigned int)childCount); | |||
| 679 | else { | |||
| 680 | const char * const elemName = xml_element_name(elemP); | |||
| 681 | const char * const cdata = xml_element_cdata(elemP); | |||
| 682 | size_t const cdataSize = xml_element_cdata_size(elemP); | |||
| 683 | ||||
| 684 | parseSimpleValueCdata(envP, elemName, cdata, cdataSize, valuePP); | |||
| 685 | } | |||
| 686 | } | |||
| 687 | ||||
| 688 | ||||
| 689 | ||||
| 690 | void | |||
| 691 | xmlrpc_parseValue(xmlrpc_env * const envP, | |||
| 692 | unsigned int const maxRecursion, | |||
| 693 | xml_element * const elemP, | |||
| 694 | xmlrpc_value ** const valuePP) { | |||
| 695 | /*---------------------------------------------------------------------------- | |||
| 696 | Compute the xmlrpc_value represented by the XML <value> element 'elem'. | |||
| 697 | Return that xmlrpc_value. | |||
| 698 | ||||
| 699 | We call convert_array() and convert_struct(), which may ultimately | |||
| 700 | call us recursively. Don't recurse any more than 'maxRecursion' | |||
| 701 | times. | |||
| 702 | -----------------------------------------------------------------------------*/ | |||
| 703 | XMLRPC_ASSERT_ENV_OK(envP)do if (!((envP) != ((void*)0) && (envP->fault_string == ((void*)0)) && !(envP)->fault_occurred)) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/src/parse_value.c", 703); while ( 0); | |||
| 704 | XMLRPC_ASSERT(elemP != NULL)do if (!(elemP != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/src/parse_value.c" , 704); while (0); | |||
| 705 | ||||
| 706 | /* Assume we'll need to recurse, make sure we're allowed */ | |||
| 707 | if (maxRecursion < 1) | |||
| ||||
| 708 | xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR(-503), | |||
| 709 | "Nested data structure too deep."); | |||
| 710 | else { | |||
| 711 | if (!xmlrpc_streq(xml_element_name(elemP), "value")) | |||
| 712 | setParseFault(envP, | |||
| 713 | "<%s> element where <value> expected", | |||
| 714 | xml_element_name(elemP)); | |||
| 715 | else { | |||
| 716 | size_t const childCount = xml_element_children_size(elemP); | |||
| 717 | ||||
| 718 | if (childCount == 0) { | |||
| 719 | /* We have no type element, so treat the value as a string. */ | |||
| 720 | char * const cdata = xml_element_cdata(elemP); | |||
| 721 | size_t const cdata_size = xml_element_cdata_size(elemP); | |||
| 722 | *valuePP = xmlrpc_string_new_lp(envP, cdata_size, cdata); | |||
| 723 | } else if (childCount > 1) | |||
| 724 | setParseFault(envP, "<value> has %u child elements. " | |||
| 725 | "Only zero or one make sense.", | |||
| 726 | (unsigned int)childCount); | |||
| 727 | else { | |||
| 728 | /* We should have a type tag inside our value tag. */ | |||
| 729 | xml_element * const childP = xml_element_children(elemP)[0]; | |||
| 730 | const char * const childName = xml_element_name(childP); | |||
| 731 | ||||
| 732 | if (xmlrpc_streq(childName, "struct")) | |||
| 733 | parseStruct(envP, maxRecursion, childP, valuePP); | |||
| 734 | else if (xmlrpc_streq(childName, "array")) | |||
| 735 | parseArray(envP, maxRecursion, childP, valuePP); | |||
| 736 | else | |||
| 737 | parseSimpleValue(envP, childP, valuePP); | |||
| 738 | } | |||
| 739 | } | |||
| 740 | } | |||
| 741 | } | |||
| 742 | ||||
| 743 | ||||
| 744 |