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