Home | History | Annotate | Download | only in src
      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
     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
    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