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