$search
00001 00002 #include "XmlRpcValue.h" 00003 #include "XmlRpcException.h" 00004 #include "XmlRpcUtil.h" 00005 #include "base64.h" 00006 00007 #ifndef MAKEDEPEND 00008 # include <iostream> 00009 # include <ostream> 00010 # include <stdlib.h> 00011 # include <stdio.h> 00012 #endif 00013 00014 #include <sstream> 00015 00016 namespace XmlRpc { 00017 00018 00019 static const char VALUE_TAG[] = "<value>"; 00020 static const char VALUE_ETAG[] = "</value>"; 00021 00022 static const char BOOLEAN_TAG[] = "<boolean>"; 00023 static const char BOOLEAN_ETAG[] = "</boolean>"; 00024 static const char DOUBLE_TAG[] = "<double>"; 00025 static const char DOUBLE_ETAG[] = "</double>"; 00026 static const char INT_TAG[] = "<int>"; 00027 static const char I4_TAG[] = "<i4>"; 00028 static const char I4_ETAG[] = "</i4>"; 00029 static const char STRING_TAG[] = "<string>"; 00030 static const char DATETIME_TAG[] = "<dateTime.iso8601>"; 00031 static const char DATETIME_ETAG[] = "</dateTime.iso8601>"; 00032 static const char BASE64_TAG[] = "<base64>"; 00033 static const char BASE64_ETAG[] = "</base64>"; 00034 00035 static const char ARRAY_TAG[] = "<array>"; 00036 static const char DATA_TAG[] = "<data>"; 00037 static const char DATA_ETAG[] = "</data>"; 00038 static const char ARRAY_ETAG[] = "</array>"; 00039 00040 static const char STRUCT_TAG[] = "<struct>"; 00041 static const char MEMBER_TAG[] = "<member>"; 00042 static const char NAME_TAG[] = "<name>"; 00043 static const char NAME_ETAG[] = "</name>"; 00044 static const char MEMBER_ETAG[] = "</member>"; 00045 static const char STRUCT_ETAG[] = "</struct>"; 00046 00047 00048 00049 // Format strings 00050 std::string XmlRpcValue::_doubleFormat("%.16g"); 00051 00052 00053 00054 // Clean up 00055 void XmlRpcValue::invalidate() 00056 { 00057 switch (_type) { 00058 case TypeString: delete _value.asString; break; 00059 case TypeDateTime: delete _value.asTime; break; 00060 case TypeBase64: delete _value.asBinary; break; 00061 case TypeArray: delete _value.asArray; break; 00062 case TypeStruct: delete _value.asStruct; break; 00063 default: break; 00064 } 00065 _type = TypeInvalid; 00066 _value.asBinary = 0; 00067 } 00068 00069 00070 // Type checking 00071 void XmlRpcValue::assertTypeOrInvalid(Type t) 00072 { 00073 if (_type == TypeInvalid) 00074 { 00075 _type = t; 00076 switch (_type) { // Ensure there is a valid value for the type 00077 case TypeString: _value.asString = new std::string(); break; 00078 case TypeDateTime: _value.asTime = new struct tm(); break; 00079 case TypeBase64: _value.asBinary = new BinaryData(); break; 00080 case TypeArray: _value.asArray = new ValueArray(); break; 00081 case TypeStruct: _value.asStruct = new ValueStruct(); break; 00082 default: _value.asBinary = 0; break; 00083 } 00084 } 00085 else if (_type != t) 00086 throw XmlRpcException("type error"); 00087 } 00088 00089 void XmlRpcValue::assertArray(int size) const 00090 { 00091 if (_type != TypeArray) 00092 throw XmlRpcException("type error: expected an array"); 00093 else if (int(_value.asArray->size()) < size) 00094 throw XmlRpcException("range error: array index too large"); 00095 } 00096 00097 00098 void XmlRpcValue::assertArray(int size) 00099 { 00100 if (_type == TypeInvalid) { 00101 _type = TypeArray; 00102 _value.asArray = new ValueArray(size); 00103 } else if (_type == TypeArray) { 00104 if (int(_value.asArray->size()) < size) 00105 _value.asArray->resize(size); 00106 } else 00107 throw XmlRpcException("type error: expected an array"); 00108 } 00109 00110 void XmlRpcValue::assertStruct() 00111 { 00112 if (_type == TypeInvalid) { 00113 _type = TypeStruct; 00114 _value.asStruct = new ValueStruct(); 00115 } else if (_type != TypeStruct) 00116 throw XmlRpcException("type error: expected a struct"); 00117 } 00118 00119 00120 // Operators 00121 XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs) 00122 { 00123 if (this != &rhs) 00124 { 00125 invalidate(); 00126 _type = rhs._type; 00127 switch (_type) { 00128 case TypeBoolean: _value.asBool = rhs._value.asBool; break; 00129 case TypeInt: _value.asInt = rhs._value.asInt; break; 00130 case TypeDouble: _value.asDouble = rhs._value.asDouble; break; 00131 case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break; 00132 case TypeString: _value.asString = new std::string(*rhs._value.asString); break; 00133 case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break; 00134 case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break; 00135 case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break; 00136 default: _value.asBinary = 0; break; 00137 } 00138 } 00139 return *this; 00140 } 00141 00142 00143 // Predicate for tm equality 00144 static bool tmEq(struct tm const& t1, struct tm const& t2) { 00145 return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min && 00146 t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday && 00147 t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year; 00148 } 00149 00150 bool XmlRpcValue::operator==(XmlRpcValue const& other) const 00151 { 00152 if (_type != other._type) 00153 return false; 00154 00155 switch (_type) { 00156 case TypeBoolean: return ( !_value.asBool && !other._value.asBool) || 00157 ( _value.asBool && other._value.asBool); 00158 case TypeInt: return _value.asInt == other._value.asInt; 00159 case TypeDouble: return _value.asDouble == other._value.asDouble; 00160 case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime); 00161 case TypeString: return *_value.asString == *other._value.asString; 00162 case TypeBase64: return *_value.asBinary == *other._value.asBinary; 00163 case TypeArray: return *_value.asArray == *other._value.asArray; 00164 00165 // The map<>::operator== requires the definition of value< for kcc 00166 case TypeStruct: //return *_value.asStruct == *other._value.asStruct; 00167 { 00168 if (_value.asStruct->size() != other._value.asStruct->size()) 00169 return false; 00170 00171 ValueStruct::const_iterator it1=_value.asStruct->begin(); 00172 ValueStruct::const_iterator it2=other._value.asStruct->begin(); 00173 while (it1 != _value.asStruct->end()) { 00174 const XmlRpcValue& v1 = it1->second; 00175 const XmlRpcValue& v2 = it2->second; 00176 if ( ! (v1 == v2)) 00177 return false; 00178 it1++; 00179 it2++; 00180 } 00181 return true; 00182 } 00183 default: break; 00184 } 00185 return true; // Both invalid values ... 00186 } 00187 00188 bool XmlRpcValue::operator!=(XmlRpcValue const& other) const 00189 { 00190 return !(*this == other); 00191 } 00192 00193 00194 // Works for strings, binary data, arrays, and structs. 00195 int XmlRpcValue::size() const 00196 { 00197 switch (_type) { 00198 case TypeString: return int(_value.asString->size()); 00199 case TypeBase64: return int(_value.asBinary->size()); 00200 case TypeArray: return int(_value.asArray->size()); 00201 case TypeStruct: return int(_value.asStruct->size()); 00202 default: break; 00203 } 00204 00205 throw XmlRpcException("type error"); 00206 } 00207 00208 // Checks for existence of struct member 00209 bool XmlRpcValue::hasMember(const std::string& name) const 00210 { 00211 return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end(); 00212 } 00213 00214 // Set the value from xml. The chars at *offset into valueXml 00215 // should be the start of a <value> tag. Destroys any existing value. 00216 bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset) 00217 { 00218 int savedOffset = *offset; 00219 00220 invalidate(); 00221 if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset)) 00222 return false; // Not a value, offset not updated 00223 00224 int afterValueOffset = *offset; 00225 std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset); 00226 bool result = false; 00227 if (typeTag == BOOLEAN_TAG) 00228 result = boolFromXml(valueXml, offset); 00229 else if (typeTag == I4_TAG || typeTag == INT_TAG) 00230 result = intFromXml(valueXml, offset); 00231 else if (typeTag == DOUBLE_TAG) 00232 result = doubleFromXml(valueXml, offset); 00233 else if (typeTag.empty() || typeTag == STRING_TAG) 00234 result = stringFromXml(valueXml, offset); 00235 else if (typeTag == DATETIME_TAG) 00236 result = timeFromXml(valueXml, offset); 00237 else if (typeTag == BASE64_TAG) 00238 result = binaryFromXml(valueXml, offset); 00239 else if (typeTag == ARRAY_TAG) 00240 result = arrayFromXml(valueXml, offset); 00241 else if (typeTag == STRUCT_TAG) 00242 result = structFromXml(valueXml, offset); 00243 // Watch for empty/blank strings with no <string>tag 00244 else if (typeTag == VALUE_ETAG) 00245 { 00246 *offset = afterValueOffset; // back up & try again 00247 result = stringFromXml(valueXml, offset); 00248 } 00249 00250 if (result) // Skip over the </value> tag 00251 XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset); 00252 else // Unrecognized tag after <value> 00253 *offset = savedOffset; 00254 00255 return result; 00256 } 00257 00258 // Encode the Value in xml 00259 std::string XmlRpcValue::toXml() const 00260 { 00261 switch (_type) { 00262 case TypeBoolean: return boolToXml(); 00263 case TypeInt: return intToXml(); 00264 case TypeDouble: return doubleToXml(); 00265 case TypeString: return stringToXml(); 00266 case TypeDateTime: return timeToXml(); 00267 case TypeBase64: return binaryToXml(); 00268 case TypeArray: return arrayToXml(); 00269 case TypeStruct: return structToXml(); 00270 default: break; 00271 } 00272 return std::string(); // Invalid value 00273 } 00274 00275 00276 // Boolean 00277 bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset) 00278 { 00279 const char* valueStart = valueXml.c_str() + *offset; 00280 char* valueEnd; 00281 long ivalue = strtol(valueStart, &valueEnd, 10); 00282 if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1)) 00283 return false; 00284 00285 _type = TypeBoolean; 00286 _value.asBool = (ivalue == 1); 00287 *offset += int(valueEnd - valueStart); 00288 return true; 00289 } 00290 00291 std::string XmlRpcValue::boolToXml() const 00292 { 00293 std::string xml = VALUE_TAG; 00294 xml += BOOLEAN_TAG; 00295 xml += (_value.asBool ? "1" : "0"); 00296 xml += BOOLEAN_ETAG; 00297 xml += VALUE_ETAG; 00298 return xml; 00299 } 00300 00301 // Int 00302 bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset) 00303 { 00304 const char* valueStart = valueXml.c_str() + *offset; 00305 char* valueEnd; 00306 long ivalue = strtol(valueStart, &valueEnd, 10); 00307 if (valueEnd == valueStart) 00308 return false; 00309 00310 _type = TypeInt; 00311 _value.asInt = int(ivalue); 00312 *offset += int(valueEnd - valueStart); 00313 return true; 00314 } 00315 00316 std::string XmlRpcValue::intToXml() const 00317 { 00318 char buf[256]; 00319 snprintf(buf, sizeof(buf)-1, "%d", _value.asInt); 00320 buf[sizeof(buf)-1] = 0; 00321 std::string xml = VALUE_TAG; 00322 xml += I4_TAG; 00323 xml += buf; 00324 xml += I4_ETAG; 00325 xml += VALUE_ETAG; 00326 return xml; 00327 } 00328 00329 // Double 00330 bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset) 00331 { 00332 const char* valueStart = valueXml.c_str() + *offset; 00333 char* valueEnd; 00334 00335 // ticket #2438 00336 // push/pop the locale here. Value 123.45 can get read by strtod 00337 // as '123', if the locale expects a comma instead of dot. 00338 // if there are locale problems, silently continue. 00339 std::string tmplocale; 00340 char* locale_cstr = setlocale(LC_NUMERIC, 0); 00341 if (locale_cstr) 00342 { 00343 tmplocale = locale_cstr; 00344 setlocale(LC_NUMERIC, "POSIX"); 00345 } 00346 00347 double dvalue = strtod(valueStart, &valueEnd); 00348 00349 if (tmplocale.size() > 0) { 00350 setlocale(LC_NUMERIC, tmplocale.c_str()); 00351 } 00352 00353 if (valueEnd == valueStart) 00354 return false; 00355 00356 _type = TypeDouble; 00357 _value.asDouble = dvalue; 00358 *offset += int(valueEnd - valueStart); 00359 return true; 00360 } 00361 00362 std::string XmlRpcValue::doubleToXml() const 00363 { 00364 // ticket #2438 00365 std::stringstream ss; 00366 ss.imbue(std::locale::classic()); // ensure we're using "C" locale for formatting floating-point (1.4 vs. 1,4, etc.) 00367 ss.precision(17); 00368 ss << _value.asDouble; 00369 00370 std::string xml = VALUE_TAG; 00371 xml += DOUBLE_TAG; 00372 xml += ss.str(); 00373 xml += DOUBLE_ETAG; 00374 xml += VALUE_ETAG; 00375 return xml; 00376 } 00377 00378 // String 00379 bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset) 00380 { 00381 size_t valueEnd = valueXml.find('<', *offset); 00382 if (valueEnd == std::string::npos) 00383 return false; // No end tag; 00384 00385 _type = TypeString; 00386 _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset))); 00387 *offset += int(_value.asString->length()); 00388 return true; 00389 } 00390 00391 std::string XmlRpcValue::stringToXml() const 00392 { 00393 std::string xml = VALUE_TAG; 00394 //xml += STRING_TAG; optional 00395 xml += XmlRpcUtil::xmlEncode(*_value.asString); 00396 //xml += STRING_ETAG; 00397 xml += VALUE_ETAG; 00398 return xml; 00399 } 00400 00401 // DateTime (stored as a struct tm) 00402 bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset) 00403 { 00404 size_t valueEnd = valueXml.find('<', *offset); 00405 if (valueEnd == std::string::npos) 00406 return false; // No end tag; 00407 00408 std::string stime = valueXml.substr(*offset, valueEnd-*offset); 00409 00410 struct tm t; 00411 #ifdef _MSC_VER 00412 if (sscanf_s(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) 00413 #else 00414 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) 00415 #endif 00416 return false; 00417 00418 t.tm_isdst = -1; 00419 _type = TypeDateTime; 00420 _value.asTime = new struct tm(t); 00421 *offset += int(stime.length()); 00422 return true; 00423 } 00424 00425 std::string XmlRpcValue::timeToXml() const 00426 { 00427 struct tm* t = _value.asTime; 00428 char buf[20]; 00429 snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 00430 t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 00431 buf[sizeof(buf)-1] = 0; 00432 00433 std::string xml = VALUE_TAG; 00434 xml += DATETIME_TAG; 00435 xml += buf; 00436 xml += DATETIME_ETAG; 00437 xml += VALUE_ETAG; 00438 return xml; 00439 } 00440 00441 00442 // Base64 00443 bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset) 00444 { 00445 size_t valueEnd = valueXml.find('<', *offset); 00446 if (valueEnd == std::string::npos) 00447 return false; // No end tag; 00448 00449 _type = TypeBase64; 00450 std::string asString = valueXml.substr(*offset, valueEnd-*offset); 00451 _value.asBinary = new BinaryData(); 00452 // check whether base64 encodings can contain chars xml encodes... 00453 00454 // convert from base64 to binary 00455 int iostatus = 0; 00456 base64<char> decoder; 00457 std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary)); 00458 decoder.get(asString.begin(), asString.end(), ins, iostatus); 00459 00460 *offset += int(asString.length()); 00461 return true; 00462 } 00463 00464 00465 std::string XmlRpcValue::binaryToXml() const 00466 { 00467 // convert to base64 00468 std::vector<char> base64data; 00469 int iostatus = 0; 00470 base64<char> encoder; 00471 std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data); 00472 encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf()); 00473 00474 // Wrap with xml 00475 std::string xml = VALUE_TAG; 00476 xml += BASE64_TAG; 00477 xml.append(base64data.begin(), base64data.end()); 00478 xml += BASE64_ETAG; 00479 xml += VALUE_ETAG; 00480 return xml; 00481 } 00482 00483 00484 // Array 00485 bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset) 00486 { 00487 if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset)) 00488 return false; 00489 00490 _type = TypeArray; 00491 _value.asArray = new ValueArray; 00492 XmlRpcValue v; 00493 while (v.fromXml(valueXml, offset)) 00494 _value.asArray->push_back(v); // copy... 00495 00496 // Skip the trailing </data> 00497 (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset); 00498 return true; 00499 } 00500 00501 00502 // In general, its preferable to generate the xml of each element of the 00503 // array as it is needed rather than glomming up one big string. 00504 std::string XmlRpcValue::arrayToXml() const 00505 { 00506 std::string xml = VALUE_TAG; 00507 xml += ARRAY_TAG; 00508 xml += DATA_TAG; 00509 00510 int s = int(_value.asArray->size()); 00511 for (int i=0; i<s; ++i) 00512 xml += _value.asArray->at(i).toXml(); 00513 00514 xml += DATA_ETAG; 00515 xml += ARRAY_ETAG; 00516 xml += VALUE_ETAG; 00517 return xml; 00518 } 00519 00520 00521 // Struct 00522 bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset) 00523 { 00524 _type = TypeStruct; 00525 _value.asStruct = new ValueStruct; 00526 00527 while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) { 00528 // name 00529 const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset); 00530 // value 00531 XmlRpcValue val(valueXml, offset); 00532 if ( ! val.valid()) { 00533 invalidate(); 00534 return false; 00535 } 00536 const std::pair<const std::string, XmlRpcValue> p(name, val); 00537 _value.asStruct->insert(p); 00538 00539 (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset); 00540 } 00541 return true; 00542 } 00543 00544 00545 // In general, its preferable to generate the xml of each element 00546 // as it is needed rather than glomming up one big string. 00547 std::string XmlRpcValue::structToXml() const 00548 { 00549 std::string xml = VALUE_TAG; 00550 xml += STRUCT_TAG; 00551 00552 ValueStruct::const_iterator it; 00553 for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) { 00554 xml += MEMBER_TAG; 00555 xml += NAME_TAG; 00556 xml += XmlRpcUtil::xmlEncode(it->first); 00557 xml += NAME_ETAG; 00558 xml += it->second.toXml(); 00559 xml += MEMBER_ETAG; 00560 } 00561 00562 xml += STRUCT_ETAG; 00563 xml += VALUE_ETAG; 00564 return xml; 00565 } 00566 00567 00568 00569 // Write the value without xml encoding it 00570 std::ostream& XmlRpcValue::write(std::ostream& os) const { 00571 switch (_type) { 00572 default: break; 00573 case TypeBoolean: os << _value.asBool; break; 00574 case TypeInt: os << _value.asInt; break; 00575 case TypeDouble: os << _value.asDouble; break; 00576 case TypeString: os << *_value.asString; break; 00577 case TypeDateTime: 00578 { 00579 struct tm* t = _value.asTime; 00580 char buf[20]; 00581 snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 00582 t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 00583 buf[sizeof(buf)-1] = 0; 00584 os << buf; 00585 break; 00586 } 00587 case TypeBase64: 00588 { 00589 int iostatus = 0; 00590 std::ostreambuf_iterator<char> out(os); 00591 base64<char> encoder; 00592 encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf()); 00593 break; 00594 } 00595 case TypeArray: 00596 { 00597 int s = int(_value.asArray->size()); 00598 os << '{'; 00599 for (int i=0; i<s; ++i) 00600 { 00601 if (i > 0) os << ','; 00602 _value.asArray->at(i).write(os); 00603 } 00604 os << '}'; 00605 break; 00606 } 00607 case TypeStruct: 00608 { 00609 os << '['; 00610 ValueStruct::const_iterator it; 00611 for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) 00612 { 00613 if (it!=_value.asStruct->begin()) os << ','; 00614 os << it->first << ':'; 00615 it->second.write(os); 00616 } 00617 os << ']'; 00618 break; 00619 } 00620 00621 } 00622 00623 return os; 00624 } 00625 00626 } // namespace XmlRpc 00627 00628 00629 // ostream 00630 std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v) 00631 { 00632 // If you want to output in xml format: 00633 //return os << v.toXml(); 00634 return v.write(os); 00635 } 00636