1 2#include "XmlRpcValue.h" 3#include "XmlRpcException.h" 4#include "XmlRpcUtil.h" 5#include "base64.h" 6 7#ifndef MAKEDEPEND 8# include <iostream> 9# include <ostream> 10# include <stdlib.h> 11# include <stdio.h> 12#endif 13 14namespace XmlRpc { 15 16 17 static const char VALUE_TAG[] = "<value>"; 18 static const char VALUE_ETAG[] = "</value>"; 19 20 static const char BOOLEAN_TAG[] = "<boolean>"; 21 static const char BOOLEAN_ETAG[] = "</boolean>"; 22 static const char DOUBLE_TAG[] = "<double>"; 23 static const char DOUBLE_ETAG[] = "</double>"; 24 static const char INT_TAG[] = "<int>"; 25 static const char I4_TAG[] = "<i4>"; 26 static const char I4_ETAG[] = "</i4>"; 27 static const char STRING_TAG[] = "<string>"; 28 static const char DATETIME_TAG[] = "<dateTime.iso8601>"; 29 static const char DATETIME_ETAG[] = "</dateTime.iso8601>"; 30 static const char BASE64_TAG[] = "<base64>"; 31 static const char BASE64_ETAG[] = "</base64>"; 32 static const char NIL_TAG[] = "<nil/>"; 33 34 static const char ARRAY_TAG[] = "<array>"; 35 static const char DATA_TAG[] = "<data>"; 36 static const char DATA_ETAG[] = "</data>"; 37 static const char ARRAY_ETAG[] = "</array>"; 38 39 static const char STRUCT_TAG[] = "<struct>"; 40 static const char MEMBER_TAG[] = "<member>"; 41 static const char NAME_TAG[] = "<name>"; 42 static const char NAME_ETAG[] = "</name>"; 43 static const char MEMBER_ETAG[] = "</member>"; 44 static const char STRUCT_ETAG[] = "</struct>"; 45 46 47 48 // Format strings 49 std::string XmlRpcValue::_doubleFormat("%f"); 50 51 52 53 // Clean up 54 void XmlRpcValue::invalidate() 55 { 56 switch (_type) { 57 case TypeString: delete _value.asString; break; 58 case TypeDateTime: delete _value.asTime; break; 59 case TypeBase64: delete _value.asBinary; break; 60 case TypeArray: delete _value.asArray; break; 61 case TypeStruct: delete _value.asStruct; break; 62 default: break; 63 } 64 _type = TypeInvalid; 65 _value.asBinary = 0; 66 } 67 68 69 // Type checking 70 void XmlRpcValue::assertTypeOrInvalid(Type t) 71 { 72 if (_type == TypeInvalid) 73 { 74 _type = t; 75 switch (_type) { // Ensure there is a valid value for the type 76 case TypeString: _value.asString = new std::string(); break; 77 case TypeDateTime: _value.asTime = new struct tm(); break; 78 case TypeBase64: _value.asBinary = new BinaryData(); break; 79 case TypeArray: _value.asArray = new ValueArray(); break; 80 case TypeStruct: _value.asStruct = new ValueStruct(); break; 81 default: _value.asBinary = 0; break; 82 } 83 } 84 else if (_type != t) 85 throw XmlRpcException("type error"); 86 } 87 88 void XmlRpcValue::assertArray(int size) const 89 { 90 if (_type != TypeArray) 91 throw XmlRpcException("type error: expected an array"); 92 else if (int(_value.asArray->size()) < size) 93 throw XmlRpcException("range error: array index too large"); 94 } 95 96 97 void XmlRpcValue::assertArray(int size) 98 { 99 if (_type == TypeInvalid) { 100 _type = TypeArray; 101 _value.asArray = new ValueArray(size); 102 } else if (_type == TypeArray) { 103 if (int(_value.asArray->size()) < size) 104 _value.asArray->resize(size); 105 } else 106 throw XmlRpcException("type error: expected an array"); 107 } 108 109 void XmlRpcValue::assertStruct() 110 { 111 if (_type == TypeInvalid) { 112 _type = TypeStruct; 113 _value.asStruct = new ValueStruct(); 114 } else if (_type != TypeStruct) 115 throw XmlRpcException("type error: expected a struct"); 116 } 117 118 119 // Operators 120 XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs) 121 { 122 if (this != &rhs) 123 { 124 invalidate(); 125 _type = rhs._type; 126 switch (_type) { 127 case TypeBoolean: _value.asBool = rhs._value.asBool; break; 128 case TypeInt: _value.asInt = rhs._value.asInt; break; 129 case TypeDouble: _value.asDouble = rhs._value.asDouble; break; 130 case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break; 131 case TypeString: _value.asString = new std::string(*rhs._value.asString); break; 132 case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break; 133 case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break; 134 case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break; 135 default: _value.asBinary = 0; break; 136 } 137 } 138 return *this; 139 } 140 141 142 // Predicate for tm equality 143 static bool tmEq(struct tm const& t1, struct tm const& t2) { 144 return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min && 145 t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday && 146 t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year; 147 } 148 149 bool XmlRpcValue::operator==(XmlRpcValue const& other) const 150 { 151 if (_type != other._type) 152 return false; 153 154 switch (_type) { 155 case TypeBoolean: return ( !_value.asBool && !other._value.asBool) || 156 ( _value.asBool && other._value.asBool); 157 case TypeInt: return _value.asInt == other._value.asInt; 158 case TypeDouble: return _value.asDouble == other._value.asDouble; 159 case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime); 160 case TypeString: return *_value.asString == *other._value.asString; 161 case TypeBase64: return *_value.asBinary == *other._value.asBinary; 162 case TypeArray: return *_value.asArray == *other._value.asArray; 163 164 // The map<>::operator== requires the definition of value< for kcc 165 case TypeStruct: //return *_value.asStruct == *other._value.asStruct; 166 { 167 if (_value.asStruct->size() != other._value.asStruct->size()) 168 return false; 169 170 ValueStruct::const_iterator it1=_value.asStruct->begin(); 171 ValueStruct::const_iterator it2=other._value.asStruct->begin(); 172 while (it1 != _value.asStruct->end()) { 173 const XmlRpcValue& v1 = it1->second; 174 const XmlRpcValue& v2 = it2->second; 175 if ( ! (v1 == v2)) 176 return false; 177 it1++; 178 it2++; 179 } 180 return true; 181 } 182 default: break; 183 } 184 return true; // Both invalid values ... 185 } 186 187 bool XmlRpcValue::operator!=(XmlRpcValue const& other) const 188 { 189 return !(*this == other); 190 } 191 192 193 // Works for strings, binary data, arrays, and structs. 194 int XmlRpcValue::size() const 195 { 196 switch (_type) { 197 case TypeString: return int(_value.asString->size()); 198 case TypeBase64: return int(_value.asBinary->size()); 199 case TypeArray: return int(_value.asArray->size()); 200 case TypeStruct: return int(_value.asStruct->size()); 201 default: break; 202 } 203 204 throw XmlRpcException("type error"); 205 } 206 207 // Checks for existence of struct member 208 bool XmlRpcValue::hasMember(const std::string& name) const 209 { 210 return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end(); 211 } 212 213 // Set the value from xml. The chars at *offset into valueXml 214 // should be the start of a <value> tag. Destroys any existing value. 215 bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset) 216 { 217 int savedOffset = *offset; 218 219 invalidate(); 220 if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset)) 221 return false; // Not a value, offset not updated 222 223 int afterValueOffset = *offset; 224 std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset); 225 bool result = false; 226 if (typeTag == NIL_TAG) 227 result = nilFromXml(valueXml, offset); 228 else if (typeTag == BOOLEAN_TAG) 229 result = boolFromXml(valueXml, offset); 230 else if (typeTag == I4_TAG || typeTag == INT_TAG) 231 result = intFromXml(valueXml, offset); 232 else if (typeTag == DOUBLE_TAG) 233 result = doubleFromXml(valueXml, offset); 234 else if (typeTag.empty() || typeTag == STRING_TAG) 235 result = stringFromXml(valueXml, offset); 236 else if (typeTag == DATETIME_TAG) 237 result = timeFromXml(valueXml, offset); 238 else if (typeTag == BASE64_TAG) 239 result = binaryFromXml(valueXml, offset); 240 else if (typeTag == ARRAY_TAG) 241 result = arrayFromXml(valueXml, offset); 242 else if (typeTag == STRUCT_TAG) 243 result = structFromXml(valueXml, offset); 244 // Watch for empty/blank strings with no <string>tag 245 else if (typeTag == VALUE_ETAG) 246 { 247 *offset = afterValueOffset; // back up & try again 248 result = stringFromXml(valueXml, offset); 249 } 250 251 if (result) // Skip over the </value> tag 252 XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset); 253 else // Unrecognized tag after <value> 254 *offset = savedOffset; 255 256 return result; 257 } 258 259 // Encode the Value in xml 260 std::string XmlRpcValue::toXml() const 261 { 262 switch (_type) { 263 case TypeNil: return nilToXml(); 264 case TypeBoolean: return boolToXml(); 265 case TypeInt: return intToXml(); 266 case TypeDouble: return doubleToXml(); 267 case TypeString: return stringToXml(); 268 case TypeDateTime: return timeToXml(); 269 case TypeBase64: return binaryToXml(); 270 case TypeArray: return arrayToXml(); 271 case TypeStruct: return structToXml(); 272 default: break; 273 } 274 return std::string(); // Invalid value 275 } 276 277 // Nil 278 bool XmlRpcValue::nilFromXml(std::string const& /* valueXml */, int* /* offset */) 279 { 280 _type = TypeNil; 281 _value.asBinary = 0; 282 return true; 283 } 284 285 std::string XmlRpcValue::nilToXml() const 286 { 287 std::string xml = VALUE_TAG; 288 xml += NIL_TAG; 289 xml += VALUE_ETAG; 290 return xml; 291 } 292 293 // Boolean 294 bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset) 295 { 296 const char* valueStart = valueXml.c_str() + *offset; 297 char* valueEnd; 298 long ivalue = strtol(valueStart, &valueEnd, 10); 299 if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1)) 300 return false; 301 302 _type = TypeBoolean; 303 _value.asBool = (ivalue == 1); 304 *offset += int(valueEnd - valueStart); 305 return true; 306 } 307 308 std::string XmlRpcValue::boolToXml() const 309 { 310 std::string xml = VALUE_TAG; 311 xml += BOOLEAN_TAG; 312 xml += (_value.asBool ? "1" : "0"); 313 xml += BOOLEAN_ETAG; 314 xml += VALUE_ETAG; 315 return xml; 316 } 317 318 // Int 319 bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset) 320 { 321 const char* valueStart = valueXml.c_str() + *offset; 322 char* valueEnd; 323 long ivalue = strtol(valueStart, &valueEnd, 10); 324 if (valueEnd == valueStart) 325 return false; 326 327 _type = TypeInt; 328 _value.asInt = int(ivalue); 329 *offset += int(valueEnd - valueStart); 330 return true; 331 } 332 333 std::string XmlRpcValue::intToXml() const 334 { 335 char buf[256]; 336 snprintf(buf, sizeof(buf)-1, "%d", _value.asInt); 337 buf[sizeof(buf)-1] = 0; 338 std::string xml = VALUE_TAG; 339 xml += I4_TAG; 340 xml += buf; 341 xml += I4_ETAG; 342 xml += VALUE_ETAG; 343 return xml; 344 } 345 346 // Double 347 bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset) 348 { 349 const char* valueStart = valueXml.c_str() + *offset; 350 char* valueEnd; 351 double dvalue = strtod(valueStart, &valueEnd); 352 if (valueEnd == valueStart) 353 return false; 354 355 _type = TypeDouble; 356 _value.asDouble = dvalue; 357 *offset += int(valueEnd - valueStart); 358 return true; 359 } 360 361 std::string XmlRpcValue::doubleToXml() const 362 { 363 char buf[256]; 364 snprintf(buf, sizeof(buf)-1, getDoubleFormat().c_str(), _value.asDouble); 365 buf[sizeof(buf)-1] = 0; 366 367 std::string xml = VALUE_TAG; 368 xml += DOUBLE_TAG; 369 xml += buf; 370 xml += DOUBLE_ETAG; 371 xml += VALUE_ETAG; 372 return xml; 373 } 374 375 // String 376 bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset) 377 { 378 size_t valueEnd = valueXml.find('<', *offset); 379 if (valueEnd == std::string::npos) 380 return false; // No end tag; 381 382 _type = TypeString; 383 _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset))); 384 *offset += int(_value.asString->length()); 385 return true; 386 } 387 388 std::string XmlRpcValue::stringToXml() const 389 { 390 std::string xml = VALUE_TAG; 391 //xml += STRING_TAG; optional 392 xml += XmlRpcUtil::xmlEncode(*_value.asString); 393 //xml += STRING_ETAG; 394 xml += VALUE_ETAG; 395 return xml; 396 } 397 398 // DateTime (stored as a struct tm) 399 bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset) 400 { 401 size_t valueEnd = valueXml.find('<', *offset); 402 if (valueEnd == std::string::npos) 403 return false; // No end tag; 404 405 std::string stime = valueXml.substr(*offset, valueEnd-*offset); 406 407 struct tm t; 408 if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6) 409 return false; 410 411 t.tm_isdst = -1; 412 _type = TypeDateTime; 413 _value.asTime = new struct tm(t); 414 *offset += int(stime.length()); 415 return true; 416 } 417 418 std::string XmlRpcValue::timeToXml() const 419 { 420 struct tm* t = _value.asTime; 421 char buf[20]; 422 snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 423 t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 424 buf[sizeof(buf)-1] = 0; 425 426 std::string xml = VALUE_TAG; 427 xml += DATETIME_TAG; 428 xml += buf; 429 xml += DATETIME_ETAG; 430 xml += VALUE_ETAG; 431 return xml; 432 } 433 434 435 // Base64 436 bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset) 437 { 438 size_t valueEnd = valueXml.find('<', *offset); 439 if (valueEnd == std::string::npos) 440 return false; // No end tag; 441 442 _type = TypeBase64; 443 std::string asString = valueXml.substr(*offset, valueEnd-*offset); 444 _value.asBinary = new BinaryData(); 445 // check whether base64 encodings can contain chars xml encodes... 446 447 // convert from base64 to binary 448 int iostatus = 0; 449 base64<char> decoder; 450 std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary)); 451 decoder.get(asString.begin(), asString.end(), ins, iostatus); 452 453 *offset += int(asString.length()); 454 return true; 455 } 456 457 458 std::string XmlRpcValue::binaryToXml() const 459 { 460 // convert to base64 461 std::vector<char> base64data; 462 int iostatus = 0; 463 base64<char> encoder; 464 std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data); 465 encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf()); 466 467 // Wrap with xml 468 std::string xml = VALUE_TAG; 469 xml += BASE64_TAG; 470 xml.append(base64data.begin(), base64data.end()); 471 xml += BASE64_ETAG; 472 xml += VALUE_ETAG; 473 return xml; 474 } 475 476 477 // Array 478 bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset) 479 { 480 if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset)) 481 return false; 482 483 _type = TypeArray; 484 _value.asArray = new ValueArray; 485 XmlRpcValue v; 486 while (v.fromXml(valueXml, offset)) 487 _value.asArray->push_back(v); // copy... 488 489 // Skip the trailing </data> 490 (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset); 491 return true; 492 } 493 494 495 // In general, its preferable to generate the xml of each element of the 496 // array as it is needed rather than glomming up one big string. 497 std::string XmlRpcValue::arrayToXml() const 498 { 499 std::string xml = VALUE_TAG; 500 xml += ARRAY_TAG; 501 xml += DATA_TAG; 502 503 int s = int(_value.asArray->size()); 504 for (int i=0; i<s; ++i) 505 xml += _value.asArray->at(i).toXml(); 506 507 xml += DATA_ETAG; 508 xml += ARRAY_ETAG; 509 xml += VALUE_ETAG; 510 return xml; 511 } 512 513 514 // Struct 515 bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset) 516 { 517 _type = TypeStruct; 518 _value.asStruct = new ValueStruct; 519 520 while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) { 521 // name 522 const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset); 523 // value 524 XmlRpcValue val(valueXml, offset); 525 if ( ! val.valid()) { 526 invalidate(); 527 return false; 528 } 529 const std::pair<const std::string, XmlRpcValue> p(name, val); 530 _value.asStruct->insert(p); 531 532 (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset); 533 } 534 return true; 535 } 536 537 538 // In general, its preferable to generate the xml of each element 539 // as it is needed rather than glomming up one big string. 540 std::string XmlRpcValue::structToXml() const 541 { 542 std::string xml = VALUE_TAG; 543 xml += STRUCT_TAG; 544 545 ValueStruct::const_iterator it; 546 for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) { 547 xml += MEMBER_TAG; 548 xml += NAME_TAG; 549 xml += XmlRpcUtil::xmlEncode(it->first); 550 xml += NAME_ETAG; 551 xml += it->second.toXml(); 552 xml += MEMBER_ETAG; 553 } 554 555 xml += STRUCT_ETAG; 556 xml += VALUE_ETAG; 557 return xml; 558 } 559 560 561 562 // Write the value without xml encoding it 563 std::ostream& XmlRpcValue::write(std::ostream& os) const { 564 switch (_type) { 565 default: break; 566 case TypeBoolean: os << _value.asBool; break; 567 case TypeInt: os << _value.asInt; break; 568 case TypeDouble: os << _value.asDouble; break; 569 case TypeString: os << *_value.asString; break; 570 case TypeDateTime: 571 { 572 struct tm* t = _value.asTime; 573 char buf[20]; 574 snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 575 t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 576 buf[sizeof(buf)-1] = 0; 577 os << buf; 578 break; 579 } 580 case TypeBase64: 581 { 582 int iostatus = 0; 583 std::ostreambuf_iterator<char> out(os); 584 base64<char> encoder; 585 encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf()); 586 break; 587 } 588 case TypeArray: 589 { 590 int s = int(_value.asArray->size()); 591 os << '{'; 592 for (int i=0; i<s; ++i) 593 { 594 if (i > 0) os << ','; 595 _value.asArray->at(i).write(os); 596 } 597 os << '}'; 598 break; 599 } 600 case TypeStruct: 601 { 602 os << '['; 603 ValueStruct::const_iterator it; 604 for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) 605 { 606 if (it!=_value.asStruct->begin()) os << ','; 607 os << it->first << ':'; 608 it->second.write(os); 609 } 610 os << ']'; 611 break; 612 } 613 614 } 615 616 return os; 617 } 618 619} // namespace XmlRpc 620 621 622// ostream 623std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v) 624{ 625 // If you want to output in xml format: 626 //return os << v.toXml(); 627 return v.write(os); 628} 629 630