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
14 namespace 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
invalidate()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
assertTypeOrInvalid(Type t)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
assertArray(int size) const88 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
assertArray(int size)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
assertStruct()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
operator =(XmlRpcValue const & rhs)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
tmEq(struct tm const & t1,struct tm const & t2)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
operator ==(XmlRpcValue const & other) const149 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
operator !=(XmlRpcValue const & other) const187 bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
188 {
189 return !(*this == other);
190 }
191
192
193 // Works for strings, binary data, arrays, and structs.
size() const194 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
hasMember(const std::string & name) const208 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.
fromXml(std::string const & valueXml,int * offset)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
toXml() const260 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
nilFromXml(std::string const &,int *)278 bool XmlRpcValue::nilFromXml(std::string const& /* valueXml */, int* /* offset */)
279 {
280 _type = TypeNil;
281 _value.asBinary = 0;
282 return true;
283 }
284
nilToXml() const285 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
boolFromXml(std::string const & valueXml,int * offset)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
boolToXml() const308 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
intFromXml(std::string const & valueXml,int * offset)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
intToXml() const333 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
doubleFromXml(std::string const & valueXml,int * offset)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
doubleToXml() const361 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
stringFromXml(std::string const & valueXml,int * offset)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
stringToXml() const388 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)
timeFromXml(std::string const & valueXml,int * offset)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
timeToXml() const418 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
binaryFromXml(std::string const & valueXml,int * offset)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
binaryToXml() const458 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
arrayFromXml(std::string const & valueXml,int * offset)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.
arrayToXml() const497 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
structFromXml(std::string const & valueXml,int * offset)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.
structToXml() const540 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
write(std::ostream & os) const563 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
operator <<(std::ostream & os,XmlRpc::XmlRpcValue & v)623 std::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