BBS2chProxyPoster.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. #include "BBS2chProxyPoster.h"
  2. #include <vector>
  3. #include <map>
  4. #include <sstream>
  5. #include <algorithm>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #ifdef _WIN32
  11. #include <windows.h>
  12. #endif
  13. #ifdef USE_LUA
  14. #include <lua.hpp>
  15. #endif
  16. #include "BBS2chProxyAuth.h"
  17. #include "BBS2chProxyKeyManager.h"
  18. #include "hmac.h"
  19. #include "stringEncodingConverter.h"
  20. #include "parson/parson.h"
  21. extern char *proxy_server;
  22. extern long proxy_port;
  23. extern long proxy_type;
  24. extern long timeout;
  25. extern char *user_agent;
  26. extern int force_ipv4;
  27. extern char *appKey;
  28. extern char *hmacKey;
  29. extern unsigned int api_mode;
  30. extern BBS2chProxyHttpHeaders bbscgi_headers;
  31. extern std::vector<std::string> bbscgi_postorder;
  32. extern unsigned int bbscgi_utf8;
  33. extern char *lua_script;
  34. extern int allow_chunked;
  35. extern CURLSH *curl_share;
  36. extern void log_printf(int level, const char *format ...);
  37. extern double getCurrentTime(void);
  38. static size_t header_callback_bbscgi(char *buffer, size_t size, size_t nitems, void *userdata)
  39. {
  40. BBS2chProxy5chPoster *poster = static_cast<BBS2chProxy5chPoster *>(userdata);
  41. BBS2chProxyConnection *conn = poster->connectionDelegate;
  42. if(poster->_status > 0) return size*nitems;
  43. else if(poster->_status == -1) {
  44. if(!memcmp(buffer,"\r\n",2)) {
  45. poster->_status = 0;
  46. }
  47. return size*nitems;
  48. }
  49. PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
  50. if (parsedHeader) { // Looks like a header
  51. const std::string &headerName = parsedHeader->getLowercasedName();
  52. //fprintf(stderr, "%s\n", parsedHeader->getFull().c_str());
  53. if (headerName == "connection") {
  54. poster->_responseHeaders.append("Connection: Close\r\n");
  55. return size*nitems;
  56. }
  57. else if (headerName == "transfer-encoding") {
  58. if (parsedHeader->contains("chunked")) {
  59. if (allow_chunked && !conn->isClientHttp1_0) {
  60. poster->_isResponseChunked = true;
  61. poster->_responseHeaders.append(buffer, size*nitems);
  62. }
  63. return size*nitems;
  64. }
  65. }
  66. else if (conn->force5ch && headerName == "set-cookie") {
  67. std::string value = parsedHeader->getFull(true);
  68. size_t start = value.find("domain=");
  69. if (start != std::string::npos) {
  70. start += 7;
  71. size_t end = value.find(";", start);
  72. size_t pos = value.find(".5ch.net", start);
  73. if (pos != std::string::npos && (end == std::string::npos || pos < end)) {
  74. value[start+1] = '2';
  75. poster->_responseHeaders.append(value);
  76. return size*nitems;
  77. }
  78. }
  79. }
  80. else if (headerName == "x-chx-error") {
  81. const std::string &headerValue = parsedHeader->getValue();
  82. const char *ptr = headerValue.c_str();
  83. log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", ptr);
  84. if (*ptr == 'E') {
  85. int code = atoi(ptr+1);
  86. if ((code >= 3310 && code <= 3311) || /* inconsistent with User-Agent or something? */
  87. (code >= 3320 && code <= 3324) || /* key expiration? */
  88. (code >= 3390 && code <= 3392)) /* 3390: BAN, 3391-3392: key expiration? */
  89. {
  90. BBS2chProxyConnection::keyManager.setKey("", poster->_monaKeyForRequest, poster->_userAgentForRequest, code);
  91. if (poster->_isFirstRun) {
  92. poster->_status = 2;
  93. return 0;
  94. }
  95. }
  96. }
  97. }
  98. else if (headerName == "x-monakey") {
  99. BBS2chProxyConnection::keyManager.setKey(parsedHeader->getValue(), poster->_monaKeyForRequest, poster->_userAgentForRequest, 0);
  100. }
  101. poster->_responseHeaders.append(buffer, size*nitems);
  102. return size*nitems;
  103. }
  104. else {
  105. if (!strncasecmp("HTTP/", buffer, 5)) {
  106. const char *ptr = buffer + 5;
  107. const char *end = buffer + size*nitems;
  108. while (ptr < end && *ptr != ' ') ptr++;
  109. while (ptr < end && *ptr == ' ') ptr++;
  110. if (ptr < end) {
  111. int code = atoi(ptr);
  112. if (code == 100) {
  113. poster->_status = -1;
  114. return size*nitems;
  115. }
  116. }
  117. }
  118. poster->_responseHeaders.append(buffer, size*nitems);
  119. if (!memcmp(buffer, "\r\n", 2)) {
  120. conn->socketToClient->write(poster->_responseHeaders.data(), poster->_responseHeaders.size());
  121. poster->_status = 1;
  122. }
  123. return size*nitems;
  124. }
  125. }
  126. static size_t write_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
  127. {
  128. BBS2chProxy5chPoster *poster = static_cast<BBS2chProxy5chPoster *>(userdata);
  129. BBS2chProxyConnection *conn = poster->connectionDelegate;
  130. if(poster->_isResponseChunked) {
  131. char buf[64];
  132. snprintf(buf, 64, "%lx\r\n", size*nitems);
  133. conn->socketToClient->write(buf, strlen(buf));
  134. }
  135. size_t ret = conn->socketToClient->write(buffer, size*nitems);
  136. if(poster->_isResponseChunked) conn->socketToClient->writeString("\r\n");
  137. return ret;
  138. }
  139. static size_t header_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
  140. {
  141. BBS2chProxyHttpHeaders *headers = static_cast<BBS2chProxyHttpHeaders *>(userdata);
  142. PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
  143. if (parsedHeader.get()) { // Looks like a header
  144. const std::string &headerName = parsedHeader->getLowercasedName();
  145. if (headerName == "connection") {
  146. if (headers) headers->add(parsedHeader->getName(), "close");
  147. return size*nitems;
  148. }
  149. else if (headerName == "transfer-encoding") {
  150. if (parsedHeader->contains("chunked")) {
  151. return size*nitems;
  152. }
  153. }
  154. if (headers) headers->add(parsedHeader->getName(), parsedHeader->getValue());
  155. }
  156. else if (headers) headers->setStatusLine(buffer, size*nitems);
  157. return size*nitems;
  158. }
  159. static size_t write_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
  160. {
  161. std::vector<char> *data = static_cast<std::vector<char> *>(userdata);
  162. size_t downloaded = size*nitems;
  163. if (data) data->insert(data->end(), buffer, buffer+downloaded);
  164. return downloaded;
  165. }
  166. static bool isValidAsUTF8(const char *input, size_t inputLength) {
  167. for (int i=0; i<inputLength; i++) {
  168. unsigned char c1 = input[i];
  169. if (c1 < 0x80) continue;
  170. else if (c1 >= 0xc2 && c1 <= 0xdf) {
  171. if (i >= inputLength - 1) return false;
  172. unsigned char c2 = input[++i];
  173. if (c2 < 0x80 || c2 > 0xbf) return false;
  174. unsigned int unicode = c2 & 0xf;
  175. unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 4;
  176. unicode |= ((c1 >> 2) & 0x7) << 8;
  177. if (unicode < 0x80 || unicode > 0x7ff) return false;
  178. }
  179. else if (c1 >= 0xe0 && c1 <= 0xef) {
  180. if (i >= inputLength - 2) return false;
  181. unsigned char c2 = input[++i];
  182. if (c2 < 0x80 || c2 > 0xbf) return false;
  183. unsigned char c3 = input[++i];
  184. if (c3 < 0x80 || c3 > 0xbf) return false;
  185. unsigned int unicode = c3 & 0xf;
  186. unicode |= (((c3 >> 4) & 0x3) | ((c2 & 0x3) << 2)) << 4;
  187. unicode |= ((c2 >> 2) & 0xf) << 8;
  188. unicode |= (c1 & 0xf) << 12;
  189. if (unicode < 0x800 || unicode > 0xffff) return false;
  190. else if (unicode >= 0xd800 && unicode <= 0xdfff) return false; /* for surrogate pairs */
  191. }
  192. else if (c1 >= 0xf0 && c1 <= 0xf7) {
  193. if (i >= inputLength - 3) return false;
  194. unsigned char c2 = input[++i];
  195. if (c2 < 0x80 || c2 > 0xbf) return false;
  196. unsigned char c3 = input[++i];
  197. if (c3 < 0x80 || c3 > 0xbf) return false;
  198. unsigned char c4 = input[++i];
  199. if (c4 < 0x80 || c4 > 0xbf) return false;
  200. unsigned int unicode = c4 & 0xf;
  201. unicode |= (((c4 >> 4) & 0x3) | ((c3 & 0x3) << 2)) << 4;
  202. unicode |= ((c3 >> 2) & 0xf) << 8;
  203. unicode |= (c2 & 0xf) << 12;
  204. unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 16;
  205. unicode |= ((c1 >> 2) & 0x1) << 20;
  206. if (unicode < 0x10000 || unicode > 0x10ffff) return false;
  207. }
  208. else return false;
  209. }
  210. return true;
  211. }
  212. static void appendPostSignature(BBS2chProxyFormData &body, const std::string &userAgent, const std::string &monaKey, BBS2chProxyHttpHeaders &headers, bool forTalk)
  213. {
  214. char nonce[32];
  215. std::string message;
  216. if (!forTalk) {
  217. snprintf(nonce, 32, "%.3f", getCurrentTime());
  218. message.append(body["bbs"]);
  219. message.append("<>");
  220. message.append(body["key"]);
  221. message.append("<>");
  222. message.append(body["time"]);
  223. message.append("<>");
  224. message.append(body["FROM"]);
  225. message.append("<>");
  226. message.append(body["mail"]);
  227. message.append("<>");
  228. message.append(body["MESSAGE"]);
  229. message.append("<>");
  230. message.append(body["subject"]);
  231. message.append("<>");
  232. message.append(userAgent);
  233. message.append("<>");
  234. message.append(monaKey);
  235. message.append("<>");
  236. message.append("<>");
  237. message.append(nonce);
  238. } else {
  239. message.append(body["bbs"]);
  240. message.append("<>");
  241. message.append(body["key"]);
  242. message.append("<>");
  243. message.append(body["subject"]);
  244. message.append("<>");
  245. message.append(body["MESSAGE"]);
  246. message.append("<>");
  247. message.append(body["time"]);
  248. message.append("<>");
  249. message.append(body["sid"]);
  250. message.append("<>");
  251. }
  252. unsigned char digest[32];
  253. char digestStr[65];
  254. static const char *table = "0123456789abcdef";
  255. proxy2ch_HMAC_SHA256(hmacKey, strlen(hmacKey), message.data(), message.length(), digest);
  256. for (int i=0; i<32; i++) {
  257. unsigned char c = digest[i];
  258. unsigned char upper = (c >> 4) & 0xf;
  259. unsigned char lower = c & 0xf;
  260. digestStr[i*2] = table[upper];
  261. digestStr[i*2+1] = table[lower];
  262. }
  263. digestStr[64] = 0;
  264. if (!forTalk) {
  265. headers.set("X-APIKey", appKey);
  266. log_printf(1, "Appended header \"X-APIKey: %s\"\n", appKey);
  267. headers.set("X-PostSig", digestStr);
  268. log_printf(1, "Appended header \"X-PostSig: %s\"\n", digestStr);
  269. headers.set("X-PostNonce", nonce);
  270. log_printf(1, "Appended header \"X-PostNonce: %s\"\n", nonce);
  271. headers.set("X-MonaKey", monaKey);
  272. log_printf(1, "Appended header \"X-MonaKey: %s\"\n", monaKey.c_str());
  273. } else {
  274. headers.set("X-Write-Token", digestStr);
  275. log_printf(1, "Appended header \"X-Write-Token: %s\"\n", digestStr);
  276. if (monaKey != "00000000-0000-0000-0000-000000000000") {
  277. headers.set("X-Write-Key", monaKey);
  278. log_printf(1, "Appended header \"X-Write-Key: %s\"\n", monaKey.c_str());
  279. }
  280. }
  281. }
  282. static bool convertBodyToUTF8(BBS2chProxyFormData &body)
  283. {
  284. bool shouldConvertToUTF8 = true;
  285. bool shouldCheckWholeBody = true;
  286. const std::string &submit = body.get("submit");
  287. if (body.getEncoded("submit").size() != submit.size()) {
  288. if (isValidAsUTF8(submit.data(), submit.size())) {
  289. shouldConvertToUTF8 = false;
  290. }
  291. shouldCheckWholeBody = false;
  292. }
  293. if (shouldCheckWholeBody) {
  294. shouldConvertToUTF8 = false;
  295. for (BBS2chProxyFormData::iterator it = body.begin(); it != body.end(); ++it) {
  296. if (it->second.empty()) continue;
  297. if (!isValidAsUTF8(it->second.get().data(), it->second.get().size())) {
  298. shouldConvertToUTF8 = true;
  299. break;
  300. }
  301. }
  302. }
  303. if (shouldConvertToUTF8) {
  304. for (BBS2chProxyFormData::iterator it = body.begin(); it != body.end(); ++it) {
  305. if (it->second.empty()) continue;
  306. char *converted = convertShiftJISToUTF8(it->second.get().data(), it->second.get().size());
  307. if (converted) {
  308. it->second = std::string(converted);
  309. free(converted);
  310. }
  311. }
  312. }
  313. return shouldConvertToUTF8;
  314. }
  315. #ifdef USE_LUA
  316. extern "C" {
  317. static int lua_hmacSHA256(lua_State *l)
  318. {
  319. static const char *table = "0123456789abcdef";
  320. size_t keyLength, dataLength;
  321. const char *key = luaL_checklstring(l, 1, &keyLength);
  322. const char *data = luaL_checklstring(l, 2, &dataLength);
  323. if (!key || !data) return 0;
  324. unsigned char digest[32];
  325. char digestStr[65];
  326. proxy2ch_HMAC_SHA256(key, keyLength, data, dataLength, digest);
  327. for (int i=0; i<32; i++) {
  328. unsigned char c = digest[i];
  329. unsigned char upper = (c >> 4) & 0xf;
  330. unsigned char lower = c & 0xf;
  331. digestStr[i*2] = table[upper];
  332. digestStr[i*2+1] = table[lower];
  333. }
  334. digestStr[64] = 0;
  335. lua_pushstring(l, digestStr);
  336. return 1;
  337. }
  338. static int lua_decodeURIComponent(lua_State *l)
  339. {
  340. size_t length;
  341. const char *input = luaL_checklstring(l, 1, &length);
  342. if (!input) return 0;
  343. bool decodePlus = true;
  344. if (!lua_isnoneornil(l, 2)) {
  345. decodePlus = (lua_toboolean(l, 2));
  346. }
  347. std::string output = decodeURIComponent(input, length, decodePlus);
  348. lua_pushstring(l, output.c_str());
  349. return 1;
  350. }
  351. static int lua_encodeURIComponent(lua_State *l)
  352. {
  353. size_t length;
  354. const char *input = luaL_checklstring(l, 1, &length);
  355. if (!input) return 0;
  356. bool spaceAsPlus = true;
  357. if (!lua_isnoneornil(l, 2)) {
  358. spaceAsPlus = (lua_toboolean(l, 2));
  359. }
  360. std::string output = encodeURIComponent(input, length, spaceAsPlus);
  361. lua_pushstring(l, output.c_str());
  362. return 1;
  363. }
  364. static int lua_convertShiftJISToUTF8(lua_State *l)
  365. {
  366. size_t length;
  367. const char *input = luaL_checklstring(l, 1, &length);
  368. if (!input) return 0;
  369. if (length > 0) {
  370. char *output = convertShiftJISToUTF8(input, length);
  371. if (!output) lua_pushnil(l);
  372. else {
  373. lua_pushstring(l, output);
  374. free(output);
  375. }
  376. }
  377. else lua_pushstring(l, "");
  378. return 1;
  379. }
  380. static int lua_isExpiredKey(lua_State *l)
  381. {
  382. size_t length;
  383. const char *input = luaL_checklstring(l, 1, &length);
  384. if (!input) return 0;
  385. if (BBS2chProxyConnection::keyManager.isExpired(input)) {
  386. lua_pushboolean(l, 1);
  387. }
  388. else lua_pushboolean(l, 0);
  389. return 1;
  390. }
  391. static int lua_isValidAsUTF8(lua_State *l)
  392. {
  393. size_t length;
  394. const char *input = luaL_checklstring(l, 1, &length);
  395. if (!input) return 0;
  396. lua_pushboolean(l, isValidAsUTF8(input, length));
  397. return 1;
  398. }
  399. static int lua_getMonaKey(lua_State *l)
  400. {
  401. size_t length;
  402. const char *input = luaL_checklstring(l, 1, &length);
  403. if (!input) return 0;
  404. const std::string &key = BBS2chProxyConnection::keyManager.getKey(input);
  405. lua_pushstring(l, key.c_str());
  406. return 1;
  407. }
  408. static int lua_getSID(lua_State *l)
  409. {
  410. const std::string &sid = BBS2chProxyConnection::auth.getSID();
  411. lua_pushstring(l, sid.c_str());
  412. return 1;
  413. }
  414. }
  415. #endif
  416. IBBS2chProxyPoster::IBBS2chProxyPoster(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
  417. : _requestHeaders(headers), _requestBody(body), _verbose(0), connectionDelegate(delegate)
  418. {
  419. prepareHeadersAndBody();
  420. }
  421. BBS2chProxy5chPoster::BBS2chProxy5chPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
  422. : IBBS2chProxyPoster(headers, body, delegate), _isFirstRun(true), _status(0), _isResponseChunked(false)
  423. {
  424. _url = url.absoluteString();
  425. }
  426. BBS2chProxyTalkPoster::BBS2chProxyTalkPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
  427. : IBBS2chProxyPoster(headers, body, delegate)
  428. {
  429. _url = "https://api.talk-platform.com/v1/bbs.cgi";
  430. _requestBody.remove("sid");
  431. }
  432. BBS2chProxyTalkTo5chPoster::BBS2chProxyTalkTo5chPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
  433. : BBS2chProxy5chPoster(url, headers, body, delegate)
  434. {
  435. }
  436. void IBBS2chProxyPoster::prepareHeadersAndBody()
  437. {
  438. _host = _requestHeaders.get("Host");
  439. _board = _requestBody.get("bbs");
  440. _thread = _requestBody.get("key");
  441. if (!bbscgi_postorder.empty()) {
  442. _requestBody.reorder(bbscgi_postorder);
  443. log_printf(1, "Reordered request body is: %s\n", _requestBody.toString().c_str());
  444. }
  445. _requestHeaders.remove("Host");
  446. if (user_agent) _requestHeaders.set("User-Agent", user_agent);
  447. if (!bbscgi_headers.empty()) {
  448. for (std::map<std::string, PBBS2chProxyHttpHeaderEntry>::iterator it = bbscgi_headers.getMap().begin(); it != bbscgi_headers.getMap().end(); it++) {
  449. /* we create a copy of entry here, because the original entry shouldn't be modified */
  450. PBBS2chProxyHttpHeaderEntry entry(new BBS2chProxyHttpHeaderEntry(*it->second.get()));
  451. if (!_host.empty()) {
  452. entry->replaceValue("%HOST%", _host);
  453. }
  454. if (!_board.empty()) {
  455. entry->replaceValue("%BOARD%", _board);
  456. }
  457. if (!_thread.empty()) {
  458. entry->replaceValue("%THREAD%", _thread);
  459. }
  460. _requestHeaders.set(entry);
  461. log_printf(1, "Appended custom header \"%s\"\n", entry->getFull().c_str());
  462. }
  463. }
  464. }
  465. curl_slist* IBBS2chProxyPoster::prepareCurlHandle(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, curl_slist* headersForCurl)
  466. {
  467. CURL *curl = connectionDelegate->curl;
  468. headersForCurl = headers.appendToCurlSlist(headersForCurl);
  469. if (!headers.has("Expect")) headersForCurl = curl_slist_append(headersForCurl, "Expect:");
  470. if (!headers.has("Accept")) headersForCurl = curl_slist_append(headersForCurl, "Accept:");
  471. if (curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  472. curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
  473. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  474. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  475. curl_easy_setopt(curl, CURLOPT_POST, 1L);
  476. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.toString().c_str());
  477. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  478. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  479. curl_easy_setopt(curl, CURLOPT_VERBOSE, _verbose);
  480. if (force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  481. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  482. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
  483. if (!_nic.empty()) curl_easy_setopt(curl, CURLOPT_INTERFACE, _nic.c_str());
  484. if (!_forceProxy.empty()) {
  485. curl_easy_setopt(curl, CURLOPT_PROXY, _forceProxy.c_str());
  486. }
  487. else if (proxy_server) {
  488. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  489. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  490. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  491. }
  492. return headersForCurl;
  493. }
  494. void IBBS2chProxyPoster::runLuaScript(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
  495. {
  496. #ifdef USE_LUA
  497. lua_State* l = luaL_newstate();
  498. luaL_openlibs(l);
  499. if (luaL_loadfile(l, lua_script) != LUA_OK) {
  500. log_printf(0, "Lua: Failed to open script %s:\n %s\n", lua_script, lua_tostring(l, -1));
  501. goto lua_end;
  502. }
  503. lua_newtable(l);
  504. lua_pushcfunction(l, lua_hmacSHA256);
  505. lua_setfield(l, -2, "hmacSHA256");
  506. lua_pushcfunction(l, lua_decodeURIComponent);
  507. lua_setfield(l, -2, "decodeURIComponent");
  508. lua_pushcfunction(l, lua_encodeURIComponent);
  509. lua_setfield(l, -2, "encodeURIComponent");
  510. lua_pushcfunction(l, lua_convertShiftJISToUTF8);
  511. lua_setfield(l, -2, "convertShiftJISToUTF8");
  512. lua_pushcfunction(l, lua_isExpiredKey);
  513. lua_setfield(l, -2, "isExpiredKey");
  514. lua_pushcfunction(l, lua_isValidAsUTF8);
  515. lua_setfield(l, -2, "isValidAsUTF8");
  516. lua_pushcfunction(l, lua_getMonaKey);
  517. lua_setfield(l, -2, "getMonaKey");
  518. lua_pushcfunction(l, lua_getSID);
  519. lua_setfield(l, -2, "getSID");
  520. lua_pushstring(l, BBS2chProxyConnection::keyManager.getKey().c_str());
  521. lua_setfield(l, -2, "monaKey");
  522. lua_pushinteger(l, connectionDelegate->serverPort);
  523. lua_setfield(l, -2, "port");
  524. lua_setglobal(l, "proxy2ch");
  525. BBS2chProxyHttpHeaders::getClassDefinitionForLua(l);
  526. lua_setglobal(l, "HttpHeaders");
  527. if (lua_pcall(l, 0, 0, 0) != LUA_OK) {
  528. log_printf(0, "Lua: Failed to run script %s:\n %s\n", lua_script, lua_tostring(l, -1));
  529. goto lua_end;
  530. }
  531. lua_getglobal(l, "willSendRequestToBbsCgi");
  532. if (!lua_isfunction(l, -1)) {
  533. log_printf(0, "Lua: willSendRequestToBbsCgi function does not exist in the script\n");
  534. goto lua_end;
  535. }
  536. lua_newtable(l);
  537. headers.getUserdataForLua(l);
  538. lua_setfield(l, -2, "headers");
  539. lua_pushstring(l, body.toString().c_str());
  540. lua_setfield(l, -2, "body");
  541. lua_pushstring(l, _host.c_str());
  542. lua_pushstring(l, _board.c_str());
  543. lua_pushstring(l, _thread.c_str());
  544. if (lua_pcall(l, 4, 1, 0) != LUA_OK) {
  545. log_printf(0, "Lua: Failed to call willSendRequestToBbsCgi function:\n %s\n", lua_tostring(l, -1));
  546. goto lua_end;
  547. }
  548. if (!lua_istable(l, -1)) {
  549. log_printf(0, "Lua: A return type of willSendRequestToBbsCgi function should be a table\n");
  550. goto lua_end;
  551. }
  552. lua_pushstring(l, "body");
  553. lua_rawget(l, -2);
  554. if (lua_isstring(l, -1)) {
  555. size_t length;
  556. const char *newBody = lua_tolstring(l, -1, &length);
  557. body = BBS2chProxyFormData(newBody, length);
  558. log_printf(1, "Lua: Set request body \"%s\"\n", newBody);
  559. }
  560. lua_pop(l, 1);
  561. lua_pushstring(l, "headers");
  562. lua_rawget(l, -2);
  563. if (lua_istable(l, -1)) {
  564. headers = BBS2chProxyHttpHeaders();
  565. lua_pushnil(l);
  566. while (lua_next(l, -2)) {
  567. if (lua_isstring(l, -1) && lua_isstring(l, -2)) {
  568. const char *name = lua_tostring(l, -2);
  569. const char *value = lua_tostring(l, -1);
  570. headers.add(name, value);
  571. log_printf(1, "Lua: Set request header \"%s: %s\"\n", name, value);
  572. }
  573. lua_pop(l, 1);
  574. }
  575. }
  576. else if (lua_isuserdata(l, -1)) {
  577. if (lua_getmetatable(l, -1)) {
  578. if (lua_getfield(l, -1, "_type") == LUA_TSTRING) {
  579. if (!strcmp(lua_tostring(l, -1), "HttpHeaders")) {
  580. BBS2chProxyHttpHeaders *newHeaders = *((BBS2chProxyHttpHeaders **)lua_touserdata(l, -3));
  581. if (newHeaders != &headers) {
  582. headers = *newHeaders;
  583. }
  584. for (BBS2chProxyHttpHeaders::iterator it = headers.begin(); it != headers.end(); ++it) {
  585. log_printf(1, "Lua: Set request header \"%s\"\n", it->second.c_str());
  586. }
  587. }
  588. }
  589. lua_pop(l, 2);
  590. }
  591. }
  592. lua_pop(l, 1);
  593. lua_pushstring(l, "options");
  594. lua_rawget(l, -2);
  595. if (lua_istable(l, -1)) {
  596. lua_pushstring(l, "interface");
  597. lua_rawget(l, -2);
  598. if (lua_isstring(l, -1)) {
  599. _nic = std::string(lua_tostring(l, -1));
  600. }
  601. lua_pop(l, 1);
  602. lua_pushstring(l, "verbose");
  603. lua_rawget(l, -2);
  604. if (lua_isboolean(l, -1)) {
  605. _verbose = lua_toboolean(l, -1);
  606. }
  607. lua_pop(l, 1);
  608. lua_pushstring(l, "proxy");
  609. lua_rawget(l, -2);
  610. if (lua_isstring(l, -1)) {
  611. _forceProxy = std::string(lua_tostring(l, -1));
  612. }
  613. lua_pop(l, 1);
  614. }
  615. lua_end:
  616. lua_close(l);
  617. #endif
  618. }
  619. void BBS2chProxy5chPoster::makeSignature(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
  620. {
  621. bool isPink = _host.find("bbspink.com") != std::string::npos;
  622. bool shouldSign = appKey && (((api_mode & 2) && !isPink) || (api_mode & 4));
  623. bool shouldConvertBodyToUTF8 = (bbscgi_utf8 == 1 && shouldSign) || (bbscgi_utf8 == 2);
  624. _userAgentForRequest = headers.get("User-Agent");
  625. if (headers.has("X-MonaKey")) {
  626. _monaKeyForRequest = headers.get("X-MonaKey");
  627. }
  628. if (shouldConvertBodyToUTF8 && !headers.has("X-PostSig")) {
  629. if (convertBodyToUTF8(body)) {
  630. log_printf(1, "Converted request body to UTF-8: %s\n", body.toString().c_str());
  631. }
  632. else {
  633. log_printf(1, "Request body seems already to be UTF-8, will be sent without conversion\n");
  634. }
  635. std::string contentType = headers.get("Content-Type");
  636. std::transform(contentType.begin(), contentType.end(), contentType.begin(), tolower);
  637. if (contentType.find("charset=utf-8") == std::string::npos) {
  638. headers.set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
  639. log_printf(1, "Appended header \"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\"\n");
  640. }
  641. }
  642. if (shouldSign && (!lua_script || !headers.has("X-PostSig"))) {
  643. if (!_userAgentForRequest.empty()) {
  644. _monaKeyForRequest = BBS2chProxyConnection::keyManager.getKey(_userAgentForRequest);
  645. appendPostSignature(body, _userAgentForRequest, _monaKeyForRequest, headers, false);
  646. } else {
  647. log_printf(0, "API: User-Agent muse be set explicitly to post with API.\n");
  648. }
  649. }
  650. if (!_monaKeyForRequest.empty()) {
  651. double wait = BBS2chProxyConnection::keyManager.secondsToWaitBeforePosting(_monaKeyForRequest);
  652. if (wait > 0) {
  653. log_printf(1, "Sleeping for %.1f seconds to avoid posting too fast...\n", wait);
  654. #ifdef _WIN32
  655. Sleep(wait * 1e+3);
  656. #else
  657. usleep(wait * 1e+6);
  658. #endif
  659. }
  660. }
  661. }
  662. long BBS2chProxy5chPoster::post()
  663. {
  664. CURL *curl = connectionDelegate->curl;
  665. long statusCode = 0;
  666. for (int run=0; run<2; run++) {
  667. BBS2chProxyHttpHeaders _headers = _requestHeaders;
  668. BBS2chProxyFormData _body = _requestBody;
  669. curl_slist *headersForCurl = NULL;
  670. _verbose = 0;
  671. _status = 0;
  672. _monaKeyForRequest.clear();
  673. _nic.clear();
  674. _forceProxy.clear();
  675. _isFirstRun = run == 0;
  676. _responseHeaders.clear();
  677. #ifdef USE_LUA
  678. if (lua_script) {
  679. runLuaScript(_headers, _body);
  680. }
  681. #endif
  682. makeSignature(_headers, _body);
  683. headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
  684. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_bbscgi);
  685. curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
  686. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_proxy);
  687. curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
  688. CURLcode res = curl_easy_perform(curl);
  689. if (res != CURLE_OK) {
  690. if (res == CURLE_WRITE_ERROR && _status == 2) {
  691. log_printf(1, "MonaKey should be reset. Sending the same request automatically...\n");
  692. curl_easy_reset(curl);
  693. curl_slist_free_all(headersForCurl);
  694. continue;
  695. }
  696. else {
  697. log_printf(0, "curl error: %s (%s)\n", curl_easy_strerror(res), _url.c_str());
  698. if (!_status) connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
  699. statusCode = 503;
  700. }
  701. }
  702. else {
  703. if (_isResponseChunked) {
  704. connectionDelegate->socketToClient->writeString("0\r\n\r\n");
  705. }
  706. curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
  707. }
  708. curl_easy_reset(curl);
  709. curl_slist_free_all(headersForCurl);
  710. break;
  711. }
  712. return statusCode;
  713. }
  714. void BBS2chProxyTalkPoster::makeSignature(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
  715. {
  716. bool shouldSign = appKey && (api_mode & 8);
  717. bool shouldConvertBodyToUTF8 = (bbscgi_utf8 == 1 && shouldSign) || (bbscgi_utf8 == 2);
  718. _userAgentForRequest = headers.get("User-Agent");
  719. if (headers.has("X-Write-Key")) {
  720. _monaKeyForRequest = headers.get("X-Write-Key");
  721. }
  722. if (shouldConvertBodyToUTF8 && !headers.has("X-Write-Token")) {
  723. if (convertBodyToUTF8(body)) {
  724. log_printf(1, "Converted request body to UTF-8: %s\n", body.toString().c_str());
  725. }
  726. else {
  727. log_printf(1, "Request body seems already to be UTF-8, will be sent without conversion\n");
  728. }
  729. std::string contentType = headers.get("Content-Type");
  730. std::transform(contentType.begin(), contentType.end(), contentType.begin(), tolower);
  731. if (contentType.find("charset=utf-8") == std::string::npos) {
  732. headers.set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
  733. log_printf(1, "Appended header \"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\"\n");
  734. }
  735. }
  736. if (shouldSign && (!lua_script || !headers.has("X-Write-Token"))) {
  737. if (!_userAgentForRequest.empty()) {
  738. body.append("sid", BBS2chProxyConnection::auth.getSID());
  739. body.append("appkey", appKey);
  740. body.append("anonymous", body.has("subject") ? "true" : "false");
  741. _monaKeyForRequest = BBS2chProxyConnection::keyManager.getKey(_userAgentForRequest);
  742. appendPostSignature(body, _userAgentForRequest, _monaKeyForRequest, headers, true);
  743. } else {
  744. log_printf(0, "API: User-Agent muse be set explicitly to post with API.\n");
  745. }
  746. }
  747. if (!_monaKeyForRequest.empty()) {
  748. double wait = BBS2chProxyConnection::keyManager.secondsToWaitBeforePosting(_monaKeyForRequest);
  749. if (wait > 0) {
  750. log_printf(1, "Sleeping for %.1f seconds to avoid posting too fast...\n", wait);
  751. #ifdef _WIN32
  752. Sleep(wait * 1e+3);
  753. #else
  754. usleep(wait * 1e+6);
  755. #endif
  756. }
  757. }
  758. }
  759. long BBS2chProxyTalkPoster::post()
  760. {
  761. CURL *curl = connectionDelegate->curl;
  762. long statusCode = 0;
  763. for (int run=0; run<2; run++) {
  764. BBS2chProxyHttpHeaders _headers = _requestHeaders;
  765. BBS2chProxyFormData _body = _requestBody;
  766. curl_slist *headersForCurl = NULL;
  767. std::string acceptEncoding;
  768. _verbose = 0;
  769. _nic.clear();
  770. _forceProxy.clear();
  771. #ifdef USE_LUA
  772. if (lua_script) {
  773. runLuaScript(_headers, _body);
  774. }
  775. #endif
  776. makeSignature(_headers, _body);
  777. if (_headers.has("Accept-Encoding")) {
  778. acceptEncoding = _headers.get("Accept-Encoding");
  779. _headers.remove("Accept-Encoding");
  780. }
  781. headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
  782. BBS2chProxyHttpHeaders receivedHeaders;
  783. std::vector<char> receivedBody;
  784. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
  785. curl_easy_setopt(curl, CURLOPT_HEADERDATA, &receivedHeaders);
  786. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  787. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &receivedBody);
  788. if (!acceptEncoding.empty()) curl_easy_setopt(curl, CURLOPT_ENCODING, acceptEncoding.c_str());
  789. CURLcode res = curl_easy_perform(curl);
  790. bool responseSent = false;
  791. if (res == CURLE_OK) curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
  792. if (receivedHeaders.hasNameAndValue("Content-Type", "application/json")) {
  793. receivedBody.push_back('\0');
  794. JSON_Value *json = json_parse_string(&receivedBody.front());
  795. if (json && json_type(json) == JSONObject) {
  796. JSON_Object *root = json_object(json);
  797. const char *error = json_object_dotget_string(root, "error.message");
  798. if (error) {
  799. log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", error);
  800. std::string out = "<html>\n<head>\n<title>ERROR!</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n</head>\n<body bgcolor=\"#EFEFEF\">\n<font size=\"+1\" color=\"#FF0000\"><b>ERROR: ";
  801. out += error;
  802. out += "</b></font>\n</body>\n</html>";
  803. char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
  804. connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
  805. connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
  806. if (outSJIS) {
  807. connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
  808. free(outSJIS);
  809. }
  810. statusCode = 200;
  811. responseSent = true;
  812. } else if (statusCode == 200) {
  813. int resNum = json_object_get_number(root, "commentNumber");
  814. std::string out = "<html lang=\"ja\">\n<head>\n<title>書きこみました。</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n</head>\n<body>書きこみが終わりました。<br><br>\n画面を切り替えるまでしばらくお待ち下さい。<br><br>\n</body>\n</html>";
  815. char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
  816. connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
  817. if (resNum) {
  818. std::ostringstream ss;
  819. ss << "X-Resnum: " << resNum << "\r\n";
  820. connectionDelegate->socketToClient->writeString(ss.str());
  821. }
  822. connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
  823. if (outSJIS) {
  824. connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
  825. free(outSJIS);
  826. }
  827. responseSent = true;
  828. }
  829. }
  830. if (json) json_value_free(json);
  831. }
  832. else if (receivedHeaders.has("X-Write-Key")) {
  833. BBS2chProxyConnection::keyManager.setKey(receivedHeaders.get("X-Write-Key"), _monaKeyForRequest, _userAgentForRequest, 0);
  834. const std::string &extendToken = receivedHeaders.get("X-Write-Key-Extend-Token");
  835. if (run == 0) {
  836. log_printf(1, "MonaKey has been updated. Sending the same request automatically...\n");
  837. if (!extendToken.empty()) {
  838. _requestHeaders.set("X-Write-Key-Extend-Token", extendToken);
  839. }
  840. curl_easy_reset(curl);
  841. curl_slist_free_all(headersForCurl);
  842. continue;
  843. } else if (!extendToken.empty()) {
  844. log_printf(0, "WARNING: you must send header \"X-Write-Key-Extend-Token: %s\" to complete posting.\n", extendToken.c_str());
  845. }
  846. std::string out = "<html><head><title>■ 書き込み確認 ■</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\"></head><body bgcolor=\"#EEEEEE\"><font size=\"+1\" color=\"#FF0000\"><b>書きこみ&クッキー確認</b></font><br><br><b>投稿確認<br><b style=\"color: #F00; font-size: larger;\">この書き込みで本当にいいですか?<br>\n<form method=\"POST\" action=\"../test/bbs.cgi\" accept-charset=\"Shift_JIS\"><input type=hidden name=FROM value=><input type=hidden name=mail value=><input type=hidden name=MESSAGE value=><input type=hidden name=bbs value=><input type=hidden name=time value=><input type=hidden name=key value=><input type=submit value=\"上記全てを承諾して書き込む\" name=\"submit\"></form></body></html>";
  847. char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
  848. connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
  849. connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
  850. if (outSJIS) {
  851. connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
  852. free(outSJIS);
  853. }
  854. statusCode = 200;
  855. responseSent = true;
  856. }
  857. if (!responseSent) {
  858. log_printf(5, "bbscgi response: %s\n", receivedHeaders.getStatusLine().c_str());
  859. for (BBS2chProxyHttpHeaders::iterator it = receivedHeaders.begin(); it != receivedHeaders.end(); ++it) {
  860. log_printf(5, "bbscgi response: %s\n", it->second.c_str());
  861. }
  862. receivedBody.push_back('\0');
  863. log_printf(5, "bbscgi response: %s\n", &receivedBody.front());
  864. connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
  865. statusCode = 503;
  866. }
  867. curl_easy_reset(curl);
  868. curl_slist_free_all(headersForCurl);
  869. break;
  870. }
  871. return statusCode;
  872. }
  873. long BBS2chProxyTalkTo5chPoster::post()
  874. {
  875. CURL *curl = connectionDelegate->curl;
  876. long statusCode = 0;
  877. for (int run=0; run<2; run++) {
  878. BBS2chProxyHttpHeaders _headers = _requestHeaders;
  879. BBS2chProxyFormData _body = _requestBody;
  880. curl_slist *headersForCurl = NULL;
  881. _verbose = 0;
  882. _status = 0;
  883. _monaKeyForRequest = "";
  884. _nic.clear();
  885. _forceProxy.clear();
  886. #ifdef USE_LUA
  887. if (lua_script) {
  888. runLuaScript(_headers, _body);
  889. }
  890. #endif
  891. makeSignature(_headers, _body);
  892. headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
  893. BBS2chProxyHttpHeaders receivedHeaders;
  894. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
  895. curl_easy_setopt(curl, CURLOPT_HEADERDATA, &receivedHeaders);
  896. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  897. curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
  898. CURLcode res = curl_easy_perform(curl);
  899. if (res == CURLE_OK) {
  900. curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
  901. if (receivedHeaders.has("Set-Cookie")) {
  902. std::vector<std::string>& values = receivedHeaders.getEntry("Set-Cookie")->getValueList();
  903. for (std::vector<std::string>::iterator it = values.begin(); it != values.end(); it++) {
  904. std::string &value = *it;
  905. size_t start = value.find("domain=");
  906. if (start == std::string::npos) continue;
  907. start += 7;
  908. size_t end = value.find(";", start);
  909. size_t pos = value.find(".5ch.net", start);
  910. size_t domainLen = 8;
  911. if (pos == std::string::npos || (end != std::string::npos && pos >= end)) {
  912. pos = value.find(".bbspink.com", start);
  913. domainLen = 12;
  914. }
  915. if (pos != std::string::npos && (end == std::string::npos || pos < end)) {
  916. value.replace(start, pos+domainLen-start, ".talk-platform.com");
  917. }
  918. }
  919. }
  920. if (statusCode == 200 && !receivedHeaders.has("X-Chx-Error")) {
  921. std::ostringstream ss;
  922. ss << "{\"boardCode\":\"" << "5channel_" << _board << "\",\"threadNumber\":" << _thread << ",\"commentNumber\":" << receivedHeaders.get("X-Resnum") << "}";
  923. connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
  924. if (receivedHeaders.has("Set-Cookie")) connectionDelegate->socketToClient->writeString(receivedHeaders.getFull("Set-Cookie", true));
  925. connectionDelegate->socketToClient->writeString("Content-Type: application/json\r\n\r\n");
  926. connectionDelegate->socketToClient->writeString(ss.str());
  927. } else if (statusCode == 200) {
  928. std::ostringstream ss;
  929. ss << "{\"error\":{\"message\":\"" << receivedHeaders.get("X-Chx-Error") << "\"}}";
  930. connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
  931. if (receivedHeaders.has("Set-Cookie")) connectionDelegate->socketToClient->writeString(receivedHeaders.getFull("Set-Cookie", true));
  932. connectionDelegate->socketToClient->writeString("Content-Type: application/json\r\n\r\n");
  933. connectionDelegate->socketToClient->writeString(ss.str());
  934. } else {
  935. connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
  936. statusCode = 503;
  937. }
  938. } else {
  939. connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
  940. statusCode = 503;
  941. }
  942. curl_easy_reset(curl);
  943. curl_slist_free_all(headersForCurl);
  944. break;
  945. }
  946. return statusCode;
  947. }