/XmlRpcC4Win/TimXmlRpc.cpp

https://github.com/danrmiller/Open-Transactions · C++ · 1690 lines · 1418 code · 183 blank · 89 comment · 225 complexity · db4a6c90cf4276b11e4b83833196e923 MD5 · raw file

  1. /*
  2. XmlRpc C++ client for Windows
  3. -----------------------------
  4. Created by Dr Tim Cooper, tco@smartsgroup.com, March 2009.
  5. This lets you talk to web sites using XmlRpc from C++ on Windows. It differs
  6. from similar libraries as follows:
  7. - works on Windows
  8. - supports HTTPS (SSL)
  9. - uses wininet to manage HTTP/HTTPS, so it's really very minimal
  10. - much faster than XmlRpc++ which suffers from STL performance problems.
  11. This project consists of 2 files: "TimXmlRpc.h" && "TimXmlRpc.cpp".
  12. Parts of this project have been taken from Chris Morley's "XmlRpc++" project,
  13. in particular the API. Chris's contribution is acknowledged by marking his
  14. work with the token: "/*ChrisMorley/". Thanks, Chris!
  15. */
  16. #undef UNICODE
  17. #include <windows.h>
  18. #include <wininet.h>
  19. #include <fstream>
  20. #include <iterator>
  21. #include "TimXmlRpc.h"
  22. static const char VALUE_TAG[] = "<value>";
  23. static const char VALUE_ETAG[] = "</value>";
  24. static const char BOOLEAN_TAG[] = "<boolean>";
  25. static const char BOOLEAN_ETAG[] = "</boolean>";
  26. static const char DOUBLE_TAG[] = "<double>";
  27. static const char DOUBLE_ETAG[] = "</double>";
  28. static const char INT_TAG[] = "<int>";
  29. static const char INT_ETAG[] = "</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 STRING_ETAG[] = "</string>";
  34. static const char DATETIME_TAG[] = "<dateTime.iso8601>";
  35. static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
  36. static const char BASE64_TAG[] = "<base64>";
  37. static const char BASE64_ETAG[] = "</base64>";
  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. static const char STRUCT_TAG[] = "<struct>";
  43. static const char MEMBER_TAG[] = "<member>";
  44. static const char NAME_TAG[] = "<name>";
  45. static const char NAME_ETAG[] = "</name>";
  46. static const char MEMBER_ETAG[] = "</member>";
  47. static const char STRUCT_ETAG[] = "</struct>";
  48. std::string XmlRpcValue::_doubleFormat;
  49. //---------------------------------- Misc: -------------------------------
  50. static bool strieq(const char* a, const char* b)
  51. {
  52. return _stricmp(a, b) == 0;
  53. }
  54. static bool strbegins(const char* bigstr, const char* smallstr, bool casesensitive)
  55. {
  56. const char* smalls = smallstr;
  57. const char* bigs = bigstr;
  58. while (*smalls) {
  59. if (casesensitive) {
  60. if (*bigs != *smalls)
  61. return false;
  62. else bigs++, smalls++;
  63. }
  64. else {
  65. if (toupper(*bigs) != toupper(*smalls))
  66. return false;
  67. else bigs++, smalls++;
  68. }
  69. }
  70. return true;
  71. }
  72. static void assert_failed(int line, char* filename, char* msg)
  73. {
  74. printf("Assert failed| %s:%d %s\n", filename, line, msg);
  75. }
  76. #define assert(c) if (c) ; else assert_failed(__LINE__,__FILE__,#c)
  77. //---------------------------------- ValueArray: -------------------------------
  78. void XmlRpcValue::ValueArray::resize(int n)
  79. {
  80. if (n >= _allocated) {
  81. // Optimise growing of the array:
  82. int power2 = n - 1;
  83. power2 |= power2 >> 1;
  84. power2 |= power2 >> 2;
  85. power2 |= power2 >> 4;
  86. power2 |= power2 >> 8;
  87. power2 |= power2 >> 16;
  88. power2++;
  89. // Set the size:
  90. A = (XmlRpcValue*)realloc(A, power2 * sizeof(XmlRpcValue));
  91. memset(A + _allocated, 0, (power2 - _allocated) * sizeof(XmlRpcValue));
  92. _allocated = power2;
  93. }
  94. _size = n;
  95. }
  96. bool XmlRpcValue::ValueArray::operator==(ValueArray &other)
  97. {
  98. if (_size != other._size)
  99. return false;
  100. for (int i=0; i < _size; i++) {
  101. if (A[i] != other.A[i])
  102. return false;
  103. }
  104. return true;
  105. }
  106. XmlRpcValue::ValueArray::~ValueArray()
  107. {
  108. for (int i=0; i < _size; i++)
  109. A[i].invalidate();
  110. free(A);
  111. }
  112. //---------------------------------- base64.h: -------------------------------
  113. /* <Chris Morley> */
  114. static
  115. int _base64Chars[]= {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
  116. 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
  117. '0','1','2','3','4','5','6','7','8','9',
  118. '+','/' };
  119. #define _0000_0011 0x03
  120. #define _1111_1100 0xFC
  121. #define _1111_0000 0xF0
  122. #define _0011_0000 0x30
  123. #define _0011_1100 0x3C
  124. #define _0000_1111 0x0F
  125. #define _1100_0000 0xC0
  126. #define _0011_1111 0x3F
  127. #define _EQUAL_CHAR (-1)
  128. #define _UNKNOWN_CHAR (-2)
  129. #define _IOS_FAILBIT std::ios_base::failbit
  130. #define _IOS_EOFBIT std::ios_base::eofbit
  131. #define _IOS_BADBIT std::ios_base::badbit
  132. #define _IOS_GOODBIT std::ios_base::goodbit
  133. // TEMPLATE CLASS base64_put
  134. class base64
  135. {
  136. public:
  137. typedef unsigned char byte_t;
  138. typedef char char_type;
  139. typedef std::char_traits<char> traits_type;
  140. // base64 requires max line length <= 72 characters
  141. // you can fill end of line
  142. // it may be crlf, crlfsp, noline || other class like it
  143. struct crlf
  144. {
  145. template<class _OI>
  146. _OI operator()(_OI _To) const{
  147. *_To = std::char_traits<char>::to_char_type('\r'); ++_To;
  148. *_To = std::char_traits<char>::to_char_type('\n'); ++_To;
  149. return (_To);
  150. }
  151. };
  152. struct crlfsp
  153. {
  154. template<class _OI>
  155. _OI operator()(_OI _To) const{
  156. *_To = std::char_traits<char>::to_char_type('\r'); ++_To;
  157. *_To = std::char_traits<char>::to_char_type('\n'); ++_To;
  158. *_To = std::char_traits<char>::to_char_type(' '); ++_To;
  159. return (_To);
  160. }
  161. };
  162. struct noline
  163. {
  164. template<class _OI>
  165. _OI operator()(_OI _To) const{
  166. return (_To);
  167. }
  168. };
  169. struct three2four
  170. {
  171. void zero()
  172. {
  173. _data[0] = 0;
  174. _data[1] = 0;
  175. _data[2] = 0;
  176. }
  177. byte_t get_0() const
  178. {
  179. return _data[0];
  180. }
  181. byte_t get_1() const
  182. {
  183. return _data[1];
  184. }
  185. byte_t get_2() const
  186. {
  187. return _data[2];
  188. }
  189. void set_0(byte_t _ch)
  190. {
  191. _data[0] = _ch;
  192. }
  193. void set_1(byte_t _ch)
  194. {
  195. _data[1] = _ch;
  196. }
  197. void set_2(byte_t _ch)
  198. {
  199. _data[2] = _ch;
  200. }
  201. // 0000 0000 1111 1111 2222 2222
  202. // xxxx xxxx xxxx xxxx xxxx xxxx
  203. // 0000 0011 1111 2222 2233 3333
  204. int b64_0() const {return (_data[0] & _1111_1100) >> 2;}
  205. int b64_1() const {return ((_data[0] & _0000_0011) << 4) + ((_data[1] & _1111_0000)>>4);}
  206. int b64_2() const {return ((_data[1] & _0000_1111) << 2) + ((_data[2] & _1100_0000)>>6);}
  207. int b64_3() const {return (_data[2] & _0011_1111);}
  208. void b64_0(int _ch) {_data[0] = ((_ch & _0011_1111) << 2) | (_0000_0011 & _data[0]);}
  209. void b64_1(int _ch) {
  210. _data[0] = ((_ch & _0011_0000) >> 4) | (_1111_1100 & _data[0]);
  211. _data[1] = ((_ch & _0000_1111) << 4) | (_0000_1111 & _data[1]); }
  212. void b64_2(int _ch) {
  213. _data[1] = ((_ch & _0011_1100) >> 2) | (_1111_0000 & _data[1]);
  214. _data[2] = ((_ch & _0000_0011) << 6) | (_0011_1111 & _data[2]); }
  215. void b64_3(int _ch){
  216. _data[2] = (_ch & _0011_1111) | (_1100_0000 & _data[2]);}
  217. private:
  218. byte_t _data[3];
  219. };
  220. template<class _II, class _OI, class _State, class _Endline>
  221. _II put(_II _First, _II _Last, _OI _To, _State& _St, _Endline _Endl) const
  222. {
  223. three2four _3to4;
  224. int line_octets = 0;
  225. while(_First != _Last)
  226. {
  227. _3to4.zero();
  228. //
  229. _3to4.set_0(*_First);
  230. _First++;
  231. if(_First == _Last)
  232. {
  233. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
  234. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
  235. *_To = std::char_traits<char>::to_char_type('='); ++_To;
  236. *_To = std::char_traits<char>::to_char_type('='); ++_To;
  237. goto __end;
  238. }
  239. _3to4.set_1(*_First);
  240. _First++;
  241. if(_First == _Last)
  242. {
  243. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
  244. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
  245. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
  246. *_To = std::char_traits<char>::to_char_type('='); ++_To;
  247. goto __end;
  248. }
  249. _3to4.set_2(*_First);
  250. _First++;
  251. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To;
  252. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To;
  253. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To;
  254. *_To = std::char_traits<char>::to_char_type(_base64Chars[_3to4.b64_3()]); ++_To;
  255. if(line_octets == 17) // base64
  256. {
  257. //_To = _Endl(_To);
  258. *_To = '\n'; ++_To;
  259. line_octets = 0;
  260. }
  261. else
  262. ++line_octets;
  263. }
  264. __end: ;
  265. return (_First);
  266. }
  267. template<class _II, class _OI, class _State>
  268. _II get(_II _First, _II _Last, _OI _To, _State& _St) const
  269. {
  270. three2four _3to4;
  271. int _Char;
  272. while(_First != _Last)
  273. {
  274. // Take octet
  275. _3to4.zero();
  276. // -- 0 --
  277. // Search next valid char...
  278. while((_Char = _getCharType(*_First)) < 0 && _Char == _UNKNOWN_CHAR)
  279. {
  280. if(++_First == _Last)
  281. {
  282. _St |= _IOS_FAILBIT|_IOS_EOFBIT; return _First; // unexpected EOF
  283. }
  284. }
  285. if(_Char == _EQUAL_CHAR){
  286. // Error! First character in octet can't be '='
  287. _St |= _IOS_FAILBIT;
  288. return _First;
  289. }
  290. else
  291. _3to4.b64_0(_Char);
  292. // -- 1 --
  293. // Search next valid char...
  294. while(++_First != _Last)
  295. if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
  296. break;
  297. if(_First == _Last) {
  298. _St |= _IOS_FAILBIT|_IOS_EOFBIT; // unexpected EOF
  299. return _First;
  300. }
  301. if(_Char == _EQUAL_CHAR){
  302. // Error! Second character in octet can't be '='
  303. _St |= _IOS_FAILBIT;
  304. return _First;
  305. }
  306. else
  307. _3to4.b64_1(_Char);
  308. // -- 2 --
  309. // Search next valid char...
  310. while(++_First != _Last)
  311. if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
  312. break;
  313. if(_First == _Last) {
  314. // Error! Unexpected EOF. Must be '=' || base64 character
  315. _St |= _IOS_FAILBIT|_IOS_EOFBIT;
  316. return _First;
  317. }
  318. if(_Char == _EQUAL_CHAR){
  319. // OK!
  320. _3to4.b64_2(0);
  321. _3to4.b64_3(0);
  322. // chek for EOF
  323. if(++_First == _Last)
  324. {
  325. // Error! Unexpected EOF. Must be '='. Ignore it.
  326. //_St |= _IOS_BADBIT|_IOS_EOFBIT;
  327. _St |= _IOS_EOFBIT;
  328. }
  329. else
  330. if(_getCharType(*_First) != _EQUAL_CHAR)
  331. {
  332. // Error! Must be '='. Ignore it.
  333. //_St |= _IOS_BADBIT;
  334. }
  335. else
  336. ++_First; // Skip '='
  337. // write 1 byte to output
  338. *_To = (byte_t) _3to4.get_0();
  339. return _First;
  340. }
  341. else
  342. _3to4.b64_2(_Char);
  343. // -- 3 --
  344. // Search next valid char...
  345. while(++_First != _Last)
  346. if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR)
  347. break;
  348. if(_First == _Last) {
  349. // Unexpected EOF. It's error. But ignore it.
  350. //_St |= _IOS_FAILBIT|_IOS_EOFBIT;
  351. _St |= _IOS_EOFBIT;
  352. return _First;
  353. }
  354. if(_Char == _EQUAL_CHAR)
  355. {
  356. // OK!
  357. _3to4.b64_3(0);
  358. // write to output 2 bytes
  359. *_To = (byte_t) _3to4.get_0();
  360. *_To = (byte_t) _3to4.get_1();
  361. ++_First; // set position to next character
  362. return _First;
  363. }
  364. else
  365. _3to4.b64_3(_Char);
  366. // write to output 3 bytes
  367. *_To = (byte_t) _3to4.get_0();
  368. *_To = (byte_t) _3to4.get_1();
  369. *_To = (byte_t) _3to4.get_2();
  370. ++_First;
  371. } // while(_First != _Last)
  372. return (_First);
  373. }
  374. protected:
  375. int _getCharType(int _Ch) const
  376. {
  377. if(_base64Chars[62] == _Ch)
  378. return 62;
  379. if(_base64Chars[63] == _Ch)
  380. return 63;
  381. if((_base64Chars[0] <= _Ch) && (_base64Chars[25] >= _Ch))
  382. return _Ch - _base64Chars[0];
  383. if((_base64Chars[26] <= _Ch) && (_base64Chars[51] >= _Ch))
  384. return _Ch - _base64Chars[26] + 26;
  385. if((_base64Chars[52] <= _Ch) && (_base64Chars[61] >= _Ch))
  386. return _Ch - _base64Chars[52] + 52;
  387. if(_Ch == std::char_traits<char>::to_int_type('='))
  388. return _EQUAL_CHAR;
  389. return _UNKNOWN_CHAR;
  390. }
  391. };
  392. /*-----------------------------------------------------------------------*/
  393. void XmlRpcValue::invalidate()
  394. {
  395. switch (_type) {
  396. case TypeString: free(u.asString); break;
  397. case TypeDateTime: delete u.asTime; break;
  398. case TypeBase64: delete u.asBinary; break;
  399. case TypeArray: delete u.asArray; break;
  400. case TypeStruct: delete u.asStruct; break;
  401. default: break;
  402. }
  403. _type = TypeInvalid;
  404. u.asBinary = 0;
  405. }
  406. // Type checking
  407. void XmlRpcValue::assertTypeOrInvalid(Type t)
  408. {
  409. if (_type == TypeInvalid || _type == TypeNil) {
  410. _type = t;
  411. switch (_type) { // Ensure there is a valid value for the type
  412. case TypeString: u.asString = _strdup(""); break;
  413. case TypeDateTime: u.asTime = new struct tm(); break;
  414. case TypeBase64: u.asBinary = new BinaryData(); break;
  415. case TypeArray: u.asArray = new ValueArray(); break;
  416. case TypeStruct: u.asStruct = new ValueStruct(); break;
  417. default: u.asBinary = 0; break;
  418. }
  419. }
  420. else if (_type != t)
  421. throw XmlRpcException("type error");
  422. }
  423. void XmlRpcValue::assertArray(int size) const
  424. {
  425. if (_type != TypeArray)
  426. throw XmlRpcException("type error: expected an array");
  427. else if (int(u.asArray->size()) < size)
  428. throw XmlRpcException("range error: array index too large");
  429. }
  430. void XmlRpcValue::assertArray(int size)
  431. {
  432. if (_type == TypeInvalid) {
  433. _type = TypeArray;
  434. u.asArray = new ValueArray(size);
  435. }
  436. else if (_type == TypeArray) {
  437. if (int(u.asArray->size()) < size) {
  438. u.asArray->resize(size);
  439. }
  440. }
  441. else
  442. throw XmlRpcException("type error: expected an array");
  443. }
  444. void XmlRpcValue::assertStruct()
  445. {
  446. if (_type == TypeInvalid) {
  447. _type = TypeStruct;
  448. u.asStruct = new ValueStruct();
  449. } else if (_type != TypeStruct)
  450. throw XmlRpcException("type error: expected a struct");
  451. }
  452. XmlRpcValue::ValueArray::ValueArray(ValueArray &other)
  453. {
  454. A = NULL;
  455. _size = _allocated = 0;
  456. resize(other._size);
  457. for (int i=0 ; i < _size; i++) {
  458. A[i] = other.A[i];
  459. }
  460. }
  461. /* This copy constructor does a deep copy. It's equivalent to: "XmlRpcValue(XmlRpcValue&orig);".
  462. Tim> In my applications, I manage to avoid using copy constructors, && if you're interested
  463. in performance, you should also consider whether you can avoid copy constructors, e.g. by
  464. passing values by reference.
  465. Thanks to Eric Schneider for identifying a fault with a previous version of this copy constructor.
  466. */
  467. XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
  468. {
  469. if (this != &rhs) {
  470. invalidate();
  471. _type = rhs._type;
  472. switch (_type) {
  473. case TypeBoolean: u.asBool = rhs.u.asBool; break;
  474. case TypeInt: u.asInt = rhs.u.asInt; break;
  475. case TypeDouble: u.asDouble = rhs.u.asDouble; break;
  476. case TypeDateTime: u.asTime = new struct tm(*rhs.u.asTime); break;
  477. case TypeString: u.asString = _strdup(rhs.u.asString); break;
  478. case TypeBase64: u.asBinary = new BinaryData(*rhs.u.asBinary); break;
  479. case TypeArray: u.asArray = new ValueArray(*rhs.u.asArray); break;
  480. case TypeStruct: u.asStruct = new ValueStruct(*rhs.u.asStruct); break;
  481. default: u.asBinary = 0; break;
  482. }
  483. }
  484. return *this;
  485. }
  486. /* </Chris Morley> */
  487. // Map something like: "T2-D&amp;T1" to "T2-D&T1"
  488. // Returns a 'strdup()' version of the input string.
  489. static char* xmlDecode(const char* s, const char* end)
  490. {
  491. char* dest = (char*)malloc(end - s + 1);
  492. char* d = dest;
  493. while (s < end) {
  494. if (*s != '&')
  495. *d++ = *s++;
  496. else if (strbegins(s, "&amp;", true))
  497. *d++ = '&', s += 5;
  498. else if (strbegins(s, "&quot;", true))
  499. *d++ = '\"', s += 6;
  500. else if (strbegins(s, "&apos;", true)/*! standard*/ || strbegins(s, "&#039;", true))
  501. *d++ = '\'', s += 6;
  502. else if (strbegins(s, "&lt;", true))
  503. *d++ = '<', s += 4;
  504. else if (strbegins(s, "&gt;", true))
  505. *d++ = '>', s += 4;
  506. else if (strbegins(s, "&#", true)) {
  507. s += 2;
  508. *d++ = atoi(s);
  509. while (*s >= '0' && *s <= '9')
  510. s++;
  511. if (*s == ';')
  512. s++;
  513. }
  514. else
  515. *d++ = *s++; // assert(false);
  516. }
  517. *d = '\0';
  518. return dest;
  519. }
  520. // Replace raw text with xml-encoded entities.
  521. static std::string xmlEncode(const char* s)
  522. {
  523. std::ostringstream ostr;
  524. while (*s) {
  525. if (*s == '&')
  526. ostr << "&amp;";
  527. else if (*s == '<')
  528. ostr << "&lt;";
  529. else if (*s == '>')
  530. ostr << "&gt;";
  531. else if (*s == '"')
  532. ostr << "&quot;";
  533. else if (*s == '\'')
  534. ostr << "&apos;";// Would David prefer: "&#039;" ?
  535. else if (*s < ' ' || *s >= 127)
  536. ostr << "&#" << int((unsigned char)*s) << ';';
  537. else ostr << *s;
  538. s++;
  539. }
  540. return ostr.str();
  541. }
  542. /* <Chris Morley> */
  543. // Predicate for tm equality
  544. static bool tmEq(struct tm const& t1, struct tm const& t2)
  545. {
  546. return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
  547. t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday &&
  548. t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
  549. }
  550. bool XmlRpcValue::operator==(XmlRpcValue const& other) const
  551. {
  552. if (_type != other._type)
  553. return false;
  554. switch (_type) {
  555. case TypeBoolean: return ( !u.asBool && !other.u.asBool) ||
  556. ( u.asBool && other.u.asBool);
  557. case TypeInt: return u.asInt == other.u.asInt;
  558. case TypeDouble: return u.asDouble == other.u.asDouble;
  559. case TypeDateTime: return tmEq(*u.asTime, *other.u.asTime);
  560. case TypeString: return *u.asString == *other.u.asString;
  561. case TypeBase64: return *u.asBinary == *other.u.asBinary;
  562. case TypeArray: return *u.asArray == *other.u.asArray;
  563. // The map<>::operator== requires the definition of value< for kcc
  564. case TypeStruct: //return *u.asStruct == *other.u.asStruct;
  565. {
  566. if (u.asStruct->size() != other.u.asStruct->size())
  567. return false;
  568. ValueStruct::const_iterator it1=u.asStruct->begin();
  569. ValueStruct::const_iterator it2=other.u.asStruct->begin();
  570. while (it1 != u.asStruct->end()) {
  571. const XmlRpcValue& v1 = it1->second;
  572. const XmlRpcValue& v2 = it2->second;
  573. if ( ! (v1 == v2))
  574. return false;
  575. it1++;
  576. it2++;
  577. }
  578. return true;
  579. }
  580. default: break;
  581. }
  582. return true; // Both invalid values ...
  583. }
  584. // Works for strings, binary data, arrays, && structs.
  585. int XmlRpcValue::size() const
  586. {
  587. switch (_type) {
  588. case TypeString: return strlen(u.asString);
  589. case TypeBase64: return int(u.asBinary->size());
  590. case TypeArray: return int(u.asArray->size());
  591. case TypeStruct: return int(u.asStruct->size());
  592. default: break;
  593. }
  594. throw XmlRpcException("type error");
  595. }
  596. // Checks for existence of struct member
  597. bool XmlRpcValue::hasMember(const std::string& name) const
  598. {
  599. return _type == TypeStruct && u.asStruct->find(name) != u.asStruct->end();
  600. }
  601. /* </Chris Morley> */
  602. static void SkipWhiteSpace(const char* &s)
  603. {
  604. while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
  605. s++;
  606. }
  607. static char* GobbleTag(const char* &s, char dest[128])
  608. {
  609. *dest = '\0';
  610. SkipWhiteSpace(s);
  611. if (*s != '<')
  612. return dest;
  613. const char* t = strchr(s, '>');
  614. if (t == NULL)
  615. return dest;
  616. t++;
  617. int n = t - s;
  618. if (n >= 128)
  619. n = 127;
  620. memcpy(dest, s, n);
  621. dest[n] = '\0';
  622. s = t;
  623. return dest;
  624. }
  625. static void GobbleExpectedTag(const char* &s, const char* etag)
  626. {
  627. char tag[128];
  628. GobbleTag(s, tag);
  629. if (! strieq(tag, etag))
  630. throw XmlRpcException(std::string("Expecting tag: ") + etag);
  631. }
  632. // Set the value from xml. The chars at *offset into valueXml
  633. // should be the start of a <value> tag. Destroys any existing value.
  634. void XmlRpcValue::fromXml(const char* &s)
  635. {
  636. char tag[128];
  637. invalidate();
  638. // Gobble the <value> tag:
  639. GobbleTag(s, tag);
  640. if (strieq(tag, "<value>"))
  641. ; // good
  642. else if (strieq(tag, "<value/>")) {
  643. // Jeff Rasmussen claims that <value/> is valid XmlRpc. I think he's correct.
  644. _type = TypeString;
  645. u.asString = _strdup("");
  646. return;
  647. }
  648. else
  649. throw XmlRpcException(std::string("Expecting tag: <value>"));
  650. // Gobble the type tag:
  651. GobbleTag(s, tag);
  652. if (*tag == '\0') {
  653. // If no type is indicated, the type is string.
  654. stringFromXml(s);
  655. }
  656. else if (strieq(tag, BOOLEAN_TAG)) {
  657. boolFromXml(s);
  658. GobbleExpectedTag(s, BOOLEAN_ETAG);
  659. }
  660. else if (strieq(tag, I4_TAG)) {
  661. intFromXml(s);
  662. GobbleExpectedTag(s, I4_ETAG);
  663. }
  664. else if (strieq(tag, INT_TAG)) {
  665. intFromXml(s);
  666. GobbleExpectedTag(s, INT_ETAG);
  667. }
  668. else if (strieq(tag, DOUBLE_TAG)) {
  669. doubleFromXml(s);
  670. GobbleExpectedTag(s, DOUBLE_ETAG);
  671. }
  672. else if (strieq(tag, STRING_TAG)) {
  673. stringFromXml(s);
  674. GobbleExpectedTag(s, STRING_ETAG);
  675. }
  676. else if (strieq(tag, DATETIME_TAG)) {
  677. timeFromXml(s);
  678. GobbleExpectedTag(s, DATETIME_ETAG);
  679. }
  680. else if (strieq(tag, BASE64_TAG)) {
  681. binaryFromXml(s);
  682. GobbleExpectedTag(s, BASE64_ETAG);
  683. }
  684. else if (strieq(tag, ARRAY_TAG)) {
  685. arrayFromXml(s);
  686. GobbleExpectedTag(s, ARRAY_ETAG);
  687. }
  688. else if (strieq(tag, STRUCT_TAG)) {
  689. structFromXml(s);
  690. GobbleExpectedTag(s, STRUCT_ETAG);
  691. }
  692. else if (strieq(tag, "<string/>")) {
  693. _type = TypeString;
  694. u.asString = _strdup("");
  695. }
  696. else if (strieq(tag, "<nil/>")) {
  697. _type = TypeNil;
  698. }
  699. else if (strieq(tag, "<struct/>")) {
  700. _type = TypeStruct;
  701. u.asStruct = new ValueStruct;
  702. }
  703. else if (strieq(tag, VALUE_ETAG)) {
  704. // "If no type is indicated, the type is string."
  705. _type = TypeString;
  706. u.asString = _strdup("");
  707. return; // don't gobble VALUE_ETAG because we already did
  708. }
  709. else {
  710. throw XmlRpcException(std::string("Unknown type tag: ") + tag);
  711. }
  712. GobbleExpectedTag(s, VALUE_ETAG);
  713. }
  714. // Encode the Value in xml
  715. void XmlRpcValue::toXml(std::ostringstream &ostr) const
  716. {
  717. switch (_type) {
  718. case TypeBoolean: return boolToXml(ostr);
  719. case TypeInt: return intToXml(ostr);
  720. case TypeDouble: return doubleToXml(ostr);
  721. case TypeString: return stringToXml(ostr);
  722. case TypeDateTime: return timeToXml(ostr);
  723. case TypeBase64: return binaryToXml(ostr);
  724. case TypeArray: return arrayToXml(ostr);
  725. case TypeStruct: return structToXml(ostr);
  726. case TypeNil: return nilToXml(ostr);
  727. case TypeInvalid: throw XmlRpcException("Undefined XmlRpc value");
  728. default: break;
  729. }
  730. }
  731. void XmlRpcValue::boolFromXml(const char* &s)
  732. {
  733. if (*s == '0' && s[1] == '<') {
  734. _type = TypeBoolean;
  735. u.asBool = false;
  736. s++;
  737. }
  738. else if (*s == '1' && s[1] == '<') {
  739. _type = TypeBoolean;
  740. u.asBool = true;
  741. s++;
  742. }
  743. else throw XmlRpcException("bad BOOL");
  744. }
  745. void XmlRpcValue::boolToXml(std::ostringstream &ostr) const
  746. {
  747. ostr << VALUE_TAG << BOOLEAN_TAG << (u.asBool ? "1" : "0") << BOOLEAN_ETAG << VALUE_ETAG;
  748. }
  749. void XmlRpcValue::intFromXml(const char* &s)
  750. {
  751. char* valueEnd;
  752. long ivalue = strtol(s, &valueEnd, 10);
  753. if (valueEnd == s)
  754. throw XmlRpcException("Bad double");
  755. _type = TypeInt;
  756. u.asInt = int(ivalue);
  757. s = valueEnd;
  758. }
  759. void XmlRpcValue::intToXml(std::ostringstream &ostr) const
  760. {
  761. ostr << VALUE_TAG << I4_TAG << u.asInt << I4_ETAG << VALUE_ETAG;
  762. }
  763. void XmlRpcValue::doubleFromXml(const char* &s)
  764. {
  765. char* valueEnd;
  766. double dvalue = strtod(s, &valueEnd);
  767. if (valueEnd == s)
  768. throw XmlRpcException("Bad double");
  769. _type = TypeDouble;
  770. u.asDouble = dvalue;
  771. s = valueEnd;
  772. }
  773. void XmlRpcValue::doubleToXml(std::ostringstream &ostr) const
  774. {
  775. ostr << VALUE_TAG << DOUBLE_TAG << u.asDouble << DOUBLE_ETAG << VALUE_ETAG;
  776. // This will use the default ostream::precision() to display the double. To display
  777. // values with greater accuracy, call e.g. 'ostr.precision(12)' at the top level.
  778. }
  779. void XmlRpcValue::stringFromXml(const char* &s)
  780. {
  781. const char* valueEnd = strchr(s, '<');
  782. if (valueEnd == NULL)
  783. throw XmlRpcException("Bad string");
  784. _type = TypeString;
  785. u.asString = xmlDecode(s, valueEnd);
  786. s = valueEnd;
  787. }
  788. void XmlRpcValue::stringToXml(std::ostringstream &ostr) const
  789. {
  790. if (u.asString == NULL || *u.asString == '\0')
  791. ostr << VALUE_TAG << "<string/>" << VALUE_ETAG;
  792. else ostr << VALUE_TAG << xmlEncode(u.asString) << VALUE_ETAG;
  793. // The 'STRING_TAG' is optional.
  794. }
  795. void XmlRpcValue::nilToXml(std::ostringstream &ostr) const
  796. {
  797. ostr << VALUE_TAG << "<nil/>" << VALUE_ETAG;
  798. }
  799. void XmlRpcValue::timeFromXml(const char* &s)
  800. {
  801. const char* valueEnd = strchr(s, '<');
  802. if (valueEnd == NULL)
  803. throw XmlRpcException("Bad time value");
  804. struct tm t;
  805. if (sscanf_s(s, "%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)
  806. throw XmlRpcException("Bad time value");
  807. t.tm_isdst = -1;
  808. t.tm_mon -= 1;
  809. _type = TypeDateTime;
  810. u.asTime = new struct tm(t);
  811. s = valueEnd;
  812. }
  813. void XmlRpcValue::timeToXml(std::ostringstream &ostr) const
  814. {
  815. struct tm* t = u.asTime;
  816. char buf[30];
  817. _snprintf_s(buf, sizeof(buf), sizeof(buf)-1,
  818. "%4d%02d%02dT%02d:%02d:%02d",
  819. t->tm_year,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
  820. ostr << VALUE_TAG << DATETIME_TAG << buf << DATETIME_ETAG << VALUE_ETAG;
  821. }
  822. void XmlRpcValue::binaryFromXml(const char* &s)
  823. {
  824. const char* valueEnd = strchr(s, '<');
  825. if (valueEnd == NULL)
  826. throw XmlRpcException("Bad base64");
  827. _type = TypeBase64;
  828. u.asBinary = new BinaryData();
  829. // check whether base64 encodings can contain chars xml encodes...
  830. // convert from base64 to binary
  831. int iostatus = 0;
  832. base64 decoder;
  833. std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(u.asBinary));
  834. decoder.get(s, valueEnd, ins, iostatus);
  835. s = valueEnd;
  836. }
  837. void XmlRpcValue::binaryToXml(std::ostringstream &ostr) const
  838. {
  839. // convert to base64
  840. std::vector<char> base64data;
  841. int iostatus = 0;
  842. base64 encoder;
  843. std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
  844. encoder.put(u.asBinary->begin(), u.asBinary->end(), ins, iostatus, base64::crlf());
  845. // Wrap with xml
  846. ostr << VALUE_TAG << BASE64_TAG;
  847. ostr.write(&base64data[0], base64data.size());
  848. ostr << BASE64_ETAG << VALUE_ETAG;
  849. }
  850. void XmlRpcValue::arrayFromXml(const char* &s)
  851. {
  852. char tag[128];
  853. _type = TypeArray;
  854. u.asArray = new ValueArray;
  855. GobbleTag(s, tag);
  856. if (strieq(tag, "<data/>"))
  857. return;
  858. if (! strieq(tag, DATA_TAG))
  859. throw XmlRpcException("Expecting tag: <data>");
  860. do {
  861. SkipWhiteSpace(s);
  862. if (strbegins(s, DATA_ETAG, true))
  863. break;
  864. int n = u.asArray->size();
  865. u.asArray->resize(n+1);
  866. u.asArray->at(n).fromXml(s);
  867. } while (true);
  868. // Skip the trailing </data>
  869. GobbleExpectedTag(s, DATA_ETAG);
  870. }
  871. void XmlRpcValue::arrayToXml(std::ostringstream &ostr) const
  872. {
  873. ostr << VALUE_TAG << ARRAY_TAG << DATA_TAG;
  874. int limit = int(u.asArray->size());
  875. for (int i=0; i < limit; ++i)
  876. u.asArray->at(i).toXml(ostr);
  877. ostr << DATA_ETAG << ARRAY_ETAG << VALUE_ETAG;
  878. }
  879. void XmlRpcValue::structFromXml(const char* &s)
  880. {
  881. _type = TypeStruct;
  882. u.asStruct = new ValueStruct;
  883. SkipWhiteSpace(s);
  884. while (strbegins(s, MEMBER_TAG, true)) {
  885. s += strlen(MEMBER_TAG);
  886. // name
  887. GobbleExpectedTag(s, NAME_TAG);
  888. const char* nameEnd = strchr(s, '<');
  889. if (nameEnd == NULL)
  890. throw XmlRpcException("Bad 'name' tag in struct");
  891. char* name = xmlDecode(s, nameEnd);
  892. s = nameEnd;
  893. GobbleExpectedTag(s, NAME_ETAG);
  894. // value
  895. XmlRpcValue val;
  896. val.fromXml(s);
  897. if ( ! val.valid()) {
  898. invalidate();
  899. return;
  900. }
  901. const std::pair<const std::string, XmlRpcValue> p(name, val);
  902. u.asStruct->insert(p);
  903. free(name);
  904. GobbleExpectedTag(s, MEMBER_ETAG);
  905. SkipWhiteSpace(s);
  906. }
  907. }
  908. void XmlRpcValue::structToXml(std::ostringstream &ostr) const
  909. {
  910. ostr << VALUE_TAG << STRUCT_TAG;
  911. ValueStruct::const_iterator it;
  912. for (it=u.asStruct->begin(); it!=u.asStruct->end(); ++it) {
  913. ostr << MEMBER_TAG << NAME_TAG << xmlEncode(it->first.c_str()) << NAME_ETAG;
  914. it->second.toXml(ostr);
  915. ostr << MEMBER_ETAG;
  916. }
  917. ostr << STRUCT_ETAG << VALUE_ETAG << '\n';
  918. }
  919. bool XmlRpcValue::parseMethodResponse(const char* s)
  920. {
  921. char xmlVersion[128];
  922. char tag[128];
  923. GobbleTag(s, xmlVersion);
  924. if (! strbegins(xmlVersion, "<?xml version", false))
  925. throw XmlRpcException(std::string(s));
  926. GobbleTag(s, tag);
  927. if (! strieq(tag, "<methodResponse>")) {
  928. if (strstr(s, "<title>Bad request!</title>"))
  929. throw XmlRpcException(std::string("Talking HTTP to a HTTPS server?"));
  930. else if (strstr(s, "Object ! found"))
  931. throw XmlRpcException(std::string("Wrong URL (\"Object ! found\")"));
  932. else throw XmlRpcException(std::string(s));
  933. }
  934. SkipWhiteSpace(s);
  935. if (strbegins(s, "<fault>", true)) {
  936. GobbleExpectedTag(s, "<fault>");
  937. fromXml(s);
  938. GobbleExpectedTag(s, "</fault>");
  939. return false;
  940. }
  941. else {
  942. GobbleExpectedTag(s, "<params>");
  943. GobbleExpectedTag(s, "<param>");
  944. fromXml(s);
  945. // There's false real need to parse the bits at the end, is there?
  946. //GobbleExpectedTag(s, "</param>");
  947. //GobbleExpectedTag(s, "</params>");
  948. }
  949. //GobbleExpectedTag(s, "</methodResponse>");
  950. return true;
  951. }
  952. void XmlRpcValue::buildCall(const char* method, std::ostringstream &ostr) const
  953. {
  954. ostr << "<?xml version=\"1.0\"?>\r\n";
  955. ostr << "<methodCall><methodName>" << method << "</methodName>\r\n<params>";
  956. if (getType() == XmlRpcValue::TypeArray) {
  957. for (int i=0; i < size(); ++i) {
  958. ostr << "<param>";
  959. (*this)[i].toXml(ostr);
  960. ostr << "</param>";
  961. }
  962. }
  963. else if (getType() == XmlRpcValue::TypeInvalid) {
  964. }
  965. else {
  966. ostr << "<param>";
  967. toXml(ostr);
  968. ostr << "</param>\n";
  969. }
  970. ostr << "</params></methodCall>\r\n";
  971. }
  972. /*-------------------------- class XmlRpcClient: ----------------------*/
  973. class XmlRpcImplementation {
  974. XmlRpcClient::protocol_enum protocol;
  975. bool ignoreCertificateAuthority;
  976. HINTERNET hInternet;
  977. HINTERNET hConnect;
  978. std::string object;
  979. int port;
  980. void hadError(char* function);
  981. bool connect(const char* server);
  982. public:
  983. XmlRpcCallback Callback;
  984. void *context;
  985. int totalBytes;
  986. std::string errmsg;
  987. int HttpErrcode;
  988. bool isFault;
  989. XmlRpcImplementation(const char* server, int port, const char* object, XmlRpcClient::protocol_enum protocol);
  990. XmlRpcImplementation(const char* URI);
  991. bool execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result);
  992. void setCallback(XmlRpcCallback Callback, void* context)
  993. { this->Callback = Callback; this->context = context; }
  994. void setIgnoreCertificateAuthority(bool value) { ignoreCertificateAuthority = value; }
  995. ~XmlRpcImplementation();
  996. };
  997. XmlRpcClient::XmlRpcClient(const char* server, int port, const char* object, protocol_enum protocol)
  998. {
  999. secret = new XmlRpcImplementation(server, port, object, protocol);
  1000. }
  1001. XmlRpcClient::XmlRpcClient(const char* URI)
  1002. {
  1003. secret = new XmlRpcImplementation(URI);
  1004. }
  1005. bool XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
  1006. {
  1007. return secret->execute(method, params, result);
  1008. }
  1009. std::string XmlRpcClient::getError()
  1010. {
  1011. if (secret->errmsg.size() > 1254)
  1012. secret->errmsg.resize(1254);
  1013. return secret->errmsg;
  1014. }
  1015. void XmlRpcClient::setError(std::string msg)
  1016. {
  1017. secret->errmsg = msg;
  1018. }
  1019. int XmlRpcClient::getHttpErrorCode()
  1020. {
  1021. return secret->HttpErrcode;
  1022. }
  1023. void XmlRpcClient::setCallback(XmlRpcCallback Callback, void* context)
  1024. {
  1025. secret->setCallback(Callback, context);
  1026. }
  1027. void XmlRpcClient::setIgnoreCertificateAuthority(bool value)
  1028. {
  1029. secret->setIgnoreCertificateAuthority(value);
  1030. }
  1031. bool XmlRpcClient::isFault() const
  1032. {
  1033. return secret->isFault;
  1034. }
  1035. void XmlRpcClient::close()
  1036. {
  1037. delete secret;
  1038. secret = NULL;
  1039. }
  1040. XmlRpcImplementation::XmlRpcImplementation(const char* server, int _port, const char* _object,
  1041. XmlRpcClient::protocol_enum _protocol)
  1042. {
  1043. port = _port;
  1044. object = _object;
  1045. HttpErrcode = -1;
  1046. if (_protocol == XmlRpcClient::XMLRPC_AUTO)
  1047. protocol = (port == 80) ? XmlRpcClient::XMLRPC_HTTP :
  1048. (port == 443) ? XmlRpcClient::XMLRPC_HTTPS : XmlRpcClient::XMLRPC_HTTP;
  1049. else protocol = _protocol;
  1050. ignoreCertificateAuthority = false;
  1051. hConnect = NULL;
  1052. connect(server);
  1053. }
  1054. XmlRpcImplementation::XmlRpcImplementation(const char* URI)
  1055. {
  1056. port = 0;
  1057. ignoreCertificateAuthority = false;
  1058. if (strbegins(URI, "https://", false)) {
  1059. protocol = XmlRpcClient::XMLRPC_HTTPS;
  1060. URI += 8;
  1061. port = 443;
  1062. }
  1063. else if (strbegins(URI, "http://", false)) {
  1064. protocol = XmlRpcClient::XMLRPC_HTTP;
  1065. URI += 7;
  1066. port = 80;
  1067. }
  1068. else {
  1069. errmsg = "The URI must begin with \"https://\" || \"http://\".";
  1070. return;
  1071. }
  1072. const char* t = URI;
  1073. while (*t != ':' && *t != '\0' && *t != '/')
  1074. t++;
  1075. std::string server(URI, t - URI);
  1076. if (*t == ':') {
  1077. t++;
  1078. port = atoi(t);
  1079. while (*t >= '0' && *t <= '9')
  1080. t++;
  1081. }
  1082. object = t; // should start with '/'.
  1083. connect(server.c_str());
  1084. }
  1085. bool XmlRpcImplementation::connect(const char* server)
  1086. {
  1087. Callback = NULL;
  1088. context = NULL;
  1089. totalBytes = 0;
  1090. hInternet = InternetOpen("XmlRpc", 0, NULL, NULL, 0);
  1091. if (hInternet == NULL) {
  1092. hadError("InternetOpen");
  1093. return false;
  1094. }
  1095. hConnect = InternetConnect(hInternet, server, port,
  1096. NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)this);
  1097. if (hConnect == NULL) {
  1098. hadError("InternetConnect");
  1099. return false;
  1100. }
  1101. DWORD dwTimeout=999000; // 999 seconds
  1102. InternetSetOption(hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(DWORD));
  1103. InternetSetOption(hConnect, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(DWORD));
  1104. return true;
  1105. }
  1106. // Converts a GetLastError() code into a human-readable string.
  1107. void XmlRpcImplementation::hadError(char* function)
  1108. {
  1109. errmsg = function;
  1110. errmsg += " : ";
  1111. int LastError = GetLastError();
  1112. if (LastError == ERROR_INTERNET_TIMEOUT)
  1113. errmsg += "Internet timeout";
  1114. else if (LastError == ERROR_INTERNET_INVALID_CA)
  1115. errmsg += "Invalid certificate authority";
  1116. else if (LastError == ERROR_INTERNET_SECURITY_CHANNEL_ERROR)
  1117. errmsg += "Talking HTTPS to an HTTP server?";
  1118. else if (LastError == ERROR_INTERNET_CANNOT_CONNECT)
  1119. errmsg += "Failed to connect";
  1120. else if (LastError == ERROR_INTERNET_NAME_NOT_RESOLVED)
  1121. errmsg += "Name ! resolved";
  1122. else if (LastError == ERROR_INTERNET_INVALID_URL)
  1123. errmsg += "Invalid URL";
  1124. else if (LastError == ERROR_INTERNET_NAME_NOT_RESOLVED)
  1125. errmsg += "Domain name ! resolved";
  1126. else if (LastError == ERROR_INTERNET_CONNECTION_RESET)
  1127. errmsg += "Connection reset";
  1128. else if (LastError == ERROR_INTERNET_NOT_INITIALIZED)
  1129. errmsg += "Internet ! initialised";
  1130. else if (LastError == ERROR_INTERNET_CONNECTION_ABORTED)
  1131. errmsg += "Connection aborted";
  1132. else {
  1133. static char* buf;
  1134. FormatMessage(
  1135. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1136. FORMAT_MESSAGE_FROM_SYSTEM |
  1137. FORMAT_MESSAGE_IGNORE_INSERTS,
  1138. NULL,
  1139. LastError,
  1140. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  1141. (char*)&buf,
  1142. 0,
  1143. NULL
  1144. );
  1145. if (buf == NULL) {
  1146. char tmp[512];
  1147. sprintf(tmp, "Error %d", LastError);
  1148. errmsg += tmp;
  1149. }
  1150. else {
  1151. errmsg += buf;
  1152. LocalFree(buf);
  1153. }
  1154. }
  1155. }
  1156. static void CALLBACK myInternetCallback(HINTERNET hInternet,
  1157. DWORD_PTR dwContext,
  1158. DWORD dwInternetStatus,
  1159. LPVOID lpvStatusInformation,
  1160. DWORD dwStatusInformationLength)
  1161. {
  1162. XmlRpcImplementation *connection = (XmlRpcImplementation*)dwContext;
  1163. if (connection && connection->Callback) {
  1164. static char buf[512];
  1165. char* status;
  1166. switch (dwInternetStatus) {
  1167. case INTERNET_STATUS_RECEIVING_RESPONSE: if (connection->totalBytes == 0)
  1168. status = "Waiting for response";
  1169. else status = "Receiving response";
  1170. break;
  1171. case INTERNET_STATUS_RESPONSE_RECEIVED: status = "Response received"; break;
  1172. case INTERNET_STATUS_HANDLE_CLOSING: status = "Handle closing"; break;
  1173. case INTERNET_STATUS_REQUEST_SENT: status = "Data sent"; break;
  1174. case INTERNET_STATUS_SENDING_REQUEST: status = "Sending data"; break;
  1175. default: status = buf;
  1176. sprintf(buf, "Status %d", dwInternetStatus);
  1177. break;
  1178. }
  1179. connection->Callback(connection->context, status);
  1180. }
  1181. }
  1182. bool XmlRpcImplementation::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
  1183. {
  1184. errmsg = "";
  1185. if (hConnect == NULL) {
  1186. errmsg = "No connection";
  1187. return false;
  1188. }
  1189. // Build the request as an XML file:
  1190. std::ostringstream ostr;
  1191. params.buildCall(method, ostr);
  1192. // Create the HttpOpenRequest object:
  1193. if (Callback)
  1194. Callback(context, "Sending data");
  1195. const char* acceptTypes[2] = { "text/*", NULL };
  1196. int flags = INTERNET_FLAG_DONT_CACHE;
  1197. flags |= INTERNET_FLAG_KEEP_CONNECTION;
  1198. if (protocol != XmlRpcClient::XMLRPC_HTTP)
  1199. flags |= INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
  1200. HINTERNET hHttpFile = HttpOpenRequest(
  1201. hConnect,
  1202. "POST",
  1203. object.c_str(),
  1204. HTTP_VERSION,
  1205. NULL,
  1206. acceptTypes,
  1207. flags,
  1208. (DWORD_PTR)this);
  1209. if (hHttpFile == NULL) {
  1210. hadError("HttpOpenRequest");
  1211. return false;
  1212. }
  1213. InternetSetStatusCallback(hHttpFile, myInternetCallback);
  1214. if (ignoreCertificateAuthority) {
  1215. DWORD dwFlags;
  1216. DWORD dwBuffLen = sizeof(dwFlags);
  1217. InternetQueryOption(hHttpFile, INTERNET_OPTION_SECURITY_FLAGS,
  1218. (LPVOID)&dwFlags, &dwBuffLen);
  1219. dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
  1220. InternetSetOption(hHttpFile, INTERNET_OPTION_SECURITY_FLAGS,
  1221. &dwFlags, sizeof (dwFlags) );
  1222. }
  1223. // Add the 'Content-Type' && 'Content-length' headers
  1224. char header[255]; // Thanks, Anthony Chan.
  1225. sprintf(header, "Content-Type: text/xml\r\nContent-length: %d", ostr.str().size());
  1226. HttpAddRequestHeaders(hHttpFile, header, strlen(header), HTTP_ADDREQ_FLAG_ADD);
  1227. // Send the request:
  1228. if (! HttpSendRequest(hHttpFile, NULL, 0, (LPVOID)ostr.str().c_str(), ostr.str().size())) {
  1229. hadError("HttpSendRequest");
  1230. return false;
  1231. }
  1232. if (Callback)
  1233. Callback(context, "Data sent...");
  1234. // Read the response:
  1235. char* buf = NULL;
  1236. int len = 0;
  1237. do {
  1238. DWORD bytesAvailable;
  1239. if (!InternetQueryDataAvailable(hHttpFile, &bytesAvailable, 0, (DWORD_PTR)this)) {
  1240. hadError("InternetQueryDataAvailable");
  1241. break;
  1242. }
  1243. if (bytesAvailable == 0)
  1244. break; // This is the EOF condition.
  1245. buf = (char*)realloc(buf, len+bytesAvailable+1);
  1246. // Read the data from the HINTERNET handle.
  1247. DWORD bytesRead;
  1248. if (!InternetReadFile(hHttpFile,
  1249. (LPVOID)(buf + len),
  1250. bytesAvailable,
  1251. &bytesRead))
  1252. {
  1253. hadError("InternetReadFile");
  1254. break;
  1255. }
  1256. len += bytesRead;
  1257. buf[len] = '\0';
  1258. totalBytes = len;
  1259. } while (true);
  1260. // Roel Vanhout's insertion: Did we get a HTTP_STATUS_OK response? If not, why not?
  1261. // XMLRPC spec says always return 200 for a valid answer. So if it's anything other than
  1262. // 200, it's an error (i.e., no support for redirects etc.).
  1263. DWORD status_code;
  1264. DWORD buf_size, dummy;
  1265. buf_size = sizeof(DWORD);
  1266. if (!HttpQueryInfo(hHttpFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status_code, &buf_size, 0)) {
  1267. errmsg = "Could not query HTTP result status";
  1268. free(buf);
  1269. return false;
  1270. }
  1271. if (status_code != 200) {
  1272. buf_size = 0;
  1273. HttpQueryInfo(hHttpFile, HTTP_QUERY_STATUS_TEXT, &dummy, &buf_size, 0);
  1274. buf_size++; // For the '\0'
  1275. char* status_text = (char*)::malloc(buf_size);
  1276. if (!HttpQueryInfo(hHttpFile, HTTP_QUERY_STATUS_TEXT, status_text, &buf_size, 0))
  1277. errmsg = "Could not query HTTP result status";
  1278. else {
  1279. char buf[512];
  1280. sprintf(buf, "Low level (HTTP) error: %d %s", HttpErrcode, status_text);
  1281. errmsg = buf;
  1282. }
  1283. InternetCloseHandle(hHttpFile);
  1284. HttpErrcode = status_code;
  1285. free(buf);
  1286. return false;
  1287. }
  1288. // Close the HttpRequest object:
  1289. InternetCloseHandle(hHttpFile);
  1290. // Parse the response:
  1291. if (len == 0) {
  1292. errmsg = "Invalid XMLRPC response: content size is 0";
  1293. free(buf); // 'buf' will always be NULL unless for some strange reason,
  1294. // InternetReadFile() returns 'false' on the first pass.
  1295. return false;
  1296. }
  1297. try {
  1298. isFault = ! result.parseMethodResponse(buf);
  1299. if (isFault) {
  1300. XmlRpcValue possible = result["faultString"];
  1301. if (possible.getType() == XmlRpcValue::TypeString)
  1302. errmsg = std::string(possible);
  1303. else errmsg = "unspecified error";
  1304. }
  1305. }
  1306. catch (XmlRpcException err) {
  1307. errmsg = err.getMessage();
  1308. }
  1309. free(buf);
  1310. // Finished:
  1311. return errmsg == "";
  1312. }
  1313. XmlRpcImplementation::~XmlRpcImplementation()
  1314. {
  1315. if (hConnect)
  1316. InternetCloseHandle(hConnect);
  1317. if (hInternet)
  1318. InternetCloseHandle(hInternet);
  1319. }
  1320. //---------------------------- Unit testing: ----------------------
  1321. static void RoundTrip(XmlRpcValue &a)
  1322. {
  1323. std::ostringstream ostr;
  1324. a.toXml(ostr);
  1325. std::string buf = ostr.str();
  1326. XmlRpcValue b;
  1327. const char* s = buf.c_str();
  1328. b.fromXml(s);
  1329. assert(a == b);
  1330. }
  1331. void XmlRpcUnitTest()
  1332. {
  1333. try {
  1334. XmlRpcValue result;
  1335. char buf[327680];
  1336. std::ifstream input("C:\\Documents && Settings\\edval\\Desktop\\Edval data\\debug.txt");
  1337. input.read(buf, sizeof(buf));
  1338. const char* s = buf;
  1339. result.fromXml(s);
  1340. for (int i=0; i < result.size(); i++) {
  1341. XmlRpcValue el = result[i];
  1342. std::string _AcademicYear = el["ACADEMIC_YEAR"];
  1343. int AcademicYearId = el["ACADEMIC_YEAR_ID"];
  1344. }
  1345. } catch (XmlRpcException e) {
  1346. }
  1347. std::vector<XmlRpcValue> V;
  1348. V.push_back(10);
  1349. V.push_back(20);
  1350. V.push_back(30);
  1351. V.push_back(40);
  1352. V.push_back(50);
  1353. XmlRpcValue a = "Hello you";
  1354. RoundTrip(a);
  1355. XmlRpcValue harry;
  1356. harry[0] = 56;
  1357. harry[1] = 560;
  1358. harry[2] = 115;
  1359. harry[3] = 145;
  1360. harry[4] = 35;
  1361. //5
  1362. XmlRpcValue binny((void*)XmlRpcUnitTest, 30);
  1363. RoundTrip(binny);
  1364. XmlRpcValue jenny;
  1365. jenny["NAME"] = "Electric boots";
  1366. jenny["code"] = "54121";
  1367. jenny["status"] = true;
  1368. //14
  1369. a.clear();
  1370. a["CHARGE"] = 505;
  1371. a["SPIN"] = "foo";
  1372. a["COLOUR"] = false;
  1373. a["BENNY"] = harry;
  1374. a["BINNY"] = binny;
  1375. a["JENNY"] = jenny;
  1376. a["EMPTY"] = "";
  1377. RoundTrip(a);
  1378. // Copy constructors:
  1379. XmlRpcValue b;
  1380. {
  1381. XmlRpcValue copy = a;
  1382. XmlRpcValue tmp = copy;
  1383. b = tmp;
  1384. }
  1385. assert(a == b);
  1386. }