utils.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. #pragma once
  2. static const char threadTimestampFmt[] = "%Y/%m/%d %H:%M:%S %Z";
  3. static const char httpTimestampFmt[] = "%a, %d %b %Y %H:%M:%S GMT";
  4. static void *
  5. memmem_priv(const void *l, size_t l_len, const void *s, size_t s_len)
  6. {
  7. register char *cur, *last;
  8. const char *cl = (const char *)l;
  9. const char *cs = (const char *)s;
  10. /* we need something to compare */
  11. if (l_len == 0 || s_len == 0)
  12. return NULL;
  13. /* "s" must be smaller or equal to "l" */
  14. if (l_len < s_len)
  15. return NULL;
  16. /* special case where s_len == 1 */
  17. if (s_len == 1)
  18. return (void *)memchr(l, (int)*cs, l_len);
  19. /* the last position where its possible to find "s" in "l" */
  20. last = (char *)cl + l_len - s_len;
  21. for (cur = (char *)cl; cur <= last; cur++)
  22. if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
  23. return cur;
  24. return NULL;
  25. }
  26. static int decryptMail(unsigned char *decrypted, char *encrypted)
  27. {
  28. char current[5]="0x";
  29. unsigned char *ptr = decrypted;
  30. current[2] = encrypted[0];
  31. current[3] = encrypted[1];
  32. unsigned int r = strtol(current,NULL,16);
  33. int len = strlen(encrypted);
  34. int n = 2;
  35. for(;n<len;n+=2) {
  36. current[2] = encrypted[n];
  37. current[3] = encrypted[n+1];
  38. unsigned int i = strtol(current,NULL,16);
  39. *ptr++ = i^r;
  40. }
  41. *ptr = 0;
  42. //fprintf(stderr,"%s->%s\n",encrypted,decrypted);
  43. return ptr - decrypted;
  44. }
  45. static size_t header_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
  46. {
  47. BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
  48. if(conn->status > 0) return size*nitems;
  49. else if(conn->status == -1) {
  50. if(!memcmp(buffer,"\r\n",2)) {
  51. conn->status = 0;
  52. }
  53. return size*nitems;
  54. }
  55. PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
  56. if (parsedHeader.get()) { // Looks like a header
  57. const std::string &headerName = parsedHeader->getLowercasedName();
  58. //fprintf(stderr, "%s\n", parsedHeader->getFull().c_str());
  59. if (headerName == "connection") {
  60. conn->socketToClient->writeString("Connection: Close\r\n");
  61. return size*nitems;
  62. }
  63. else if (headerName == "transfer-encoding") {
  64. if (parsedHeader->contains("chunked")) {
  65. if (allow_chunked && !conn->isClientHttp1_0) {
  66. conn->isResponseChunked = true;
  67. size_t ret = conn->socketToClient->write(buffer, size*nitems);
  68. return ret;
  69. }
  70. return size*nitems;
  71. }
  72. }
  73. else if (conn->force5ch && headerName == "set-cookie") {
  74. const char *ptr = (const char *)memmem_priv(buffer, size*nitems, "domain=", 7);
  75. if (ptr) {
  76. const char *end = ptr;
  77. while(*end != ';' && *end != '\r') end++;
  78. ptr = (const char *)memmem_priv(ptr, end-ptr, "5ch.net", 7);
  79. if (ptr) {
  80. conn->socketToClient->write(buffer, ptr-buffer);
  81. conn->socketToClient->write("2", 1);
  82. conn->socketToClient->write(ptr+1, size*nitems-(ptr+1-buffer));
  83. return size*nitems;
  84. }
  85. }
  86. }
  87. else if (conn->bbscgi) {
  88. if (headerName == "x-chx-error") {
  89. const std::string &headerValue = parsedHeader->getValue();
  90. const char *ptr = headerValue.c_str();
  91. log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", ptr);
  92. if (*ptr == 'E') {
  93. int code = atoi(ptr+1);
  94. if ((code >= 3310 && code <= 3311) || /* inconsistent with User-Agent or something? */
  95. (code >= 3320 && code <= 3324) || /* key expiration? */
  96. (code >= 3390 && code <= 3392)) /* 3390: BAN, 3391-3392: key expiration? */
  97. {
  98. conn->keyManager.setKey("", conn->monaKeyForRequest, conn->userAgentForRequest, code);
  99. }
  100. }
  101. }
  102. else if (headerName == "x-monakey") {
  103. conn->keyManager.setKey(parsedHeader->getValue(), conn->monaKeyForRequest, conn->userAgentForRequest, 0);
  104. }
  105. }
  106. return conn->socketToClient->write(buffer, size*nitems);
  107. }
  108. else {
  109. if (!strncasecmp("HTTP/", buffer, 5)) {
  110. const char *ptr = buffer + 5;
  111. const char *end = buffer + size*nitems;
  112. while (ptr < end && *ptr != ' ') ptr++;
  113. while (ptr < end && *ptr == ' ') ptr++;
  114. if (ptr < end) {
  115. int code = atoi(ptr);
  116. if (code == 100) {
  117. conn->status = -1;
  118. return size*nitems;
  119. }
  120. }
  121. }
  122. size_t ret = conn->socketToClient->write(buffer, size*nitems);
  123. if (!memcmp(buffer, "\r\n", 2)) {
  124. conn->status = 1;
  125. }
  126. return ret;
  127. }
  128. }
  129. static size_t header_callback_bbscgi(char *buffer, size_t size, size_t nitems, void *userdata)
  130. {
  131. BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
  132. if(conn->status > 0) return size*nitems;
  133. else if(conn->status == -1) {
  134. if(!memcmp(buffer,"\r\n",2)) {
  135. conn->status = 0;
  136. }
  137. return size*nitems;
  138. }
  139. PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
  140. if (parsedHeader.get()) { // Looks like a header
  141. const std::string &headerName = parsedHeader->getLowercasedName();
  142. //fprintf(stderr, "%s\n", parsedHeader->getFull().c_str());
  143. if (headerName == "connection") {
  144. conn->responseHeaders.append("Connection: Close\r\n");
  145. return size*nitems;
  146. }
  147. else if (headerName == "transfer-encoding") {
  148. if (parsedHeader->contains("chunked")) {
  149. if (allow_chunked && !conn->isClientHttp1_0) {
  150. conn->isResponseChunked = true;
  151. conn->responseHeaders.append(buffer, size*nitems);
  152. }
  153. return size*nitems;
  154. }
  155. }
  156. else if (conn->force5ch && headerName == "set-cookie") {
  157. const char *ptr = (const char *)memmem_priv(buffer, size*nitems, "domain=", 7);
  158. if (ptr) {
  159. const char *end = ptr;
  160. while (*end != ';' && *end != '\r') end++;
  161. ptr = (const char *)memmem_priv(ptr, end-ptr, "5ch.net", 7);
  162. if (ptr) {
  163. conn->responseHeaders.append(buffer, ptr-buffer);
  164. conn->responseHeaders.append(1, '2');
  165. conn->responseHeaders.append(ptr+1, size*nitems-(ptr+1-buffer));
  166. return size*nitems;
  167. }
  168. }
  169. }
  170. else if (headerName == "x-chx-error") {
  171. const std::string &headerValue = parsedHeader->getValue();
  172. const char *ptr = headerValue.c_str();
  173. log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", ptr);
  174. if (*ptr == 'E') {
  175. int code = atoi(ptr+1);
  176. if ((code >= 3310 && code <= 3311) || /* inconsistent with User-Agent or something? */
  177. (code >= 3320 && code <= 3324) || /* key expiration? */
  178. (code >= 3390 && code <= 3392)) /* 3390: BAN, 3391-3392: key expiration? */
  179. {
  180. conn->keyManager.setKey("", conn->monaKeyForRequest, conn->userAgentForRequest, code);
  181. conn->status = 2;
  182. return 0;
  183. }
  184. }
  185. }
  186. else if (headerName == "x-monakey") {
  187. conn->keyManager.setKey(parsedHeader->getValue(), conn->monaKeyForRequest, conn->userAgentForRequest, 0);
  188. }
  189. conn->responseHeaders.append(buffer, size*nitems);
  190. return size*nitems;
  191. }
  192. else {
  193. if (!strncasecmp("HTTP/", buffer, 5)) {
  194. const char *ptr = buffer + 5;
  195. const char *end = buffer + size*nitems;
  196. while (ptr < end && *ptr != ' ') ptr++;
  197. while (ptr < end && *ptr == ' ') ptr++;
  198. if (ptr < end) {
  199. int code = atoi(ptr);
  200. if (code == 100) {
  201. conn->status = -1;
  202. return size*nitems;
  203. }
  204. }
  205. }
  206. conn->responseHeaders.append(buffer, size*nitems);
  207. if (!memcmp(buffer, "\r\n", 2)) {
  208. conn->socketToClient->write(conn->responseHeaders.data(), conn->responseHeaders.size());
  209. conn->status = 1;
  210. }
  211. return size*nitems;
  212. }
  213. }
  214. static size_t write_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
  215. {
  216. BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
  217. if(conn->isResponseChunked) {
  218. char buf[64];
  219. snprintf(buf, 64, "%lx\r\n", size*nitems);
  220. conn->socketToClient->write(buf, strlen(buf));
  221. }
  222. size_t ret = conn->socketToClient->write(buffer, size*nitems);
  223. if(conn->isResponseChunked) conn->socketToClient->writeString("\r\n");
  224. return ret;
  225. }
  226. static size_t header_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
  227. {
  228. DataStorage *data = reinterpret_cast<DataStorage *>(userdata);
  229. PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
  230. if (parsedHeader.get()) { // Looks like a header
  231. const std::string &headerName = parsedHeader->getLowercasedName();
  232. if (headerName == "connection") {
  233. data->appendBytes("Connection: Close\r\n", 19);
  234. return size*nitems;
  235. }
  236. else if (headerName == "transfer-encoding") {
  237. if (parsedHeader->contains("chunked")) {
  238. return size*nitems;
  239. }
  240. }
  241. }
  242. data->appendBytes(buffer, size*nitems);
  243. return size*nitems;
  244. }
  245. static size_t write_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
  246. {
  247. DataStorage *data = reinterpret_cast<DataStorage *>(userdata);
  248. size_t downloaded = size*nitems;
  249. data->appendBytes(buffer, downloaded);
  250. return downloaded;
  251. }
  252. static size_t read_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
  253. {
  254. BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
  255. if (size*nitems < 1) return 0;
  256. if (conn->isClientChunked) {
  257. size_t chunkSize;
  258. char tmp[64];
  259. if (!conn->content_length) {
  260. if (!conn->socketToClient->readLine(tmp, 64)) return 0;
  261. chunkSize = strtol(tmp, NULL, 16);
  262. if (chunkSize == 0) {
  263. return 0;
  264. }
  265. }
  266. else {
  267. chunkSize = conn->content_length;
  268. conn->content_length = 0;
  269. }
  270. if (chunkSize <= size*nitems) {
  271. size_t ret = conn->socketToClient->read(buffer, chunkSize);
  272. if (ret <= 0) return 0;
  273. conn->socketToClient->readLine(tmp, 64);
  274. return ret;
  275. }
  276. else {
  277. size_t ret = conn->socketToClient->read(buffer, size*nitems);
  278. if (ret <= 0) return 0;
  279. conn->content_length = chunkSize - ret;
  280. return ret;
  281. }
  282. }
  283. else if (conn->content_length) {
  284. size_t bytesToRead = conn->content_length;
  285. if (size*nitems < conn->content_length) bytesToRead = size*nitems;
  286. size_t ret = conn->socketToClient->read(buffer, bytesToRead);
  287. conn->content_length -= ret;
  288. return ret;
  289. }
  290. return 0;
  291. }
  292. static void sendBasicHeaders(int respCode, const char *respMsg, IBBS2chProxySocket *socket)
  293. {
  294. char date[256];
  295. time_t now = time(0);
  296. strftime(date, 256, httpTimestampFmt, gmtime(&now));
  297. std::ostringstream ss;
  298. ss << "HTTP/1.1 " << respCode << " " << respMsg << "\r\n";
  299. if(0 >= socket->writeString(ss.str())) return;
  300. if(0 >= socket->writeString("Connection: Close\r\n")) return;
  301. if(0 >= socket->writeString("Server: 2ch Proxy\r\n")) return;
  302. if(0 >= socket->writeString(std::string("Date: ") + date + "\r\n")) return;
  303. }
  304. static void sendResponse(int respCode, const char *respMsg, IBBS2chProxySocket *socket)
  305. {
  306. sendBasicHeaders(respCode, respMsg, socket);
  307. if(0 >= socket->writeString("Content-Type: text/plain; charset=UTF-8\r\n")) return;
  308. if(0 >= socket->writeString("\r\n")) return;
  309. if(respCode >= 400) {
  310. if(0 >= socket->writeString("   ∧_∧   / ̄ ̄ ̄ ̄ ̄\n")) return;
  311. if(0 >= socket->writeString(std::string("  ( ´∀`)< ") + respMsg + "\n")) return;
  312. if(0 >= socket->writeString("  (    ) \_____\n")) return;
  313. if(0 >= socket->writeString("   │ │ │\n")) return;
  314. if(0 >= socket->writeString("  (__)_)\n")) return;
  315. }
  316. }
  317. double getCurrentTime(void)
  318. {
  319. #ifdef _WIN32
  320. FILETIME ft;
  321. GetSystemTimeAsFileTime(&ft);
  322. unsigned long long now = ft.dwHighDateTime;
  323. now <<= 32;
  324. now |= ft.dwLowDateTime;
  325. return (double)(now * 1e-7 - 11644473600.0);
  326. #else
  327. struct timeval tv;
  328. gettimeofday(&tv, NULL);
  329. return (double)(tv.tv_sec + tv.tv_usec * 1e-6);
  330. #endif
  331. }
  332. static std::string decodeURIComponent(const char *input, size_t inputLength, bool decodePlus)
  333. {
  334. std::string output;
  335. for (int i=0;i<inputLength;i++) {
  336. if (input[i] == '%') {
  337. if (i < inputLength - 2) {
  338. char from[3];
  339. char *end;
  340. from[0] = input[i+1];
  341. from[1] = input[i+2];
  342. from[2] = 0;
  343. unsigned long n = strtoul(from, &end, 16);
  344. if (n < 256 && end == from+2) {
  345. output.append(1, n);
  346. i += 2;
  347. continue;
  348. }
  349. }
  350. }
  351. else if (decodePlus && input[i] == '+') {
  352. output.append(" ");
  353. continue;
  354. }
  355. output.append(1, input[i]);
  356. }
  357. return output;
  358. }
  359. static std::string encodeURIComponent(const char *input, size_t inputLength, bool spaceAsPlus)
  360. {
  361. std::string output;
  362. for (int i=0;i<inputLength;i++) {
  363. unsigned char c = (unsigned char)input[i];
  364. if ((c >= '0' && c <= '9') ||
  365. (c >= 'A' && c <= 'Z') ||
  366. (c >= 'a' && c <= 'z') ||
  367. (c == '*') || (c == '-') || (c == '.') || (c == '_')) {
  368. output.append(1, c);
  369. }
  370. else if (c == ' ' && spaceAsPlus) {
  371. output.append("+");
  372. }
  373. else {
  374. char percentEncoded[4];
  375. snprintf(percentEncoded, 4, "%%%02X", c);
  376. output.append(percentEncoded);
  377. }
  378. }
  379. return output;
  380. }
  381. static bool isValidAsUTF8(const char *input, size_t inputLength) {
  382. for (int i=0; i<inputLength; i++) {
  383. unsigned char c1 = input[i];
  384. if (c1 < 0x80) continue;
  385. else if (c1 >= 0xc2 && c1 <= 0xdf) {
  386. if (i >= inputLength - 1) return false;
  387. unsigned char c2 = input[++i];
  388. if (c2 < 0x80 || c2 > 0xbf) return false;
  389. unsigned int unicode = c2 & 0xf;
  390. unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 4;
  391. unicode |= ((c1 >> 2) & 0x7) << 8;
  392. if (unicode < 0x80 || unicode > 0x7ff) return false;
  393. }
  394. else if (c1 >= 0xe0 && c1 <= 0xef) {
  395. if (i >= inputLength - 2) return false;
  396. unsigned char c2 = input[++i];
  397. if (c2 < 0x80 || c2 > 0xbf) return false;
  398. unsigned char c3 = input[++i];
  399. if (c3 < 0x80 || c3 > 0xbf) return false;
  400. unsigned int unicode = c3 & 0xf;
  401. unicode |= (((c3 >> 4) & 0x3) | ((c2 & 0x3) << 2)) << 4;
  402. unicode |= ((c2 >> 2) & 0xf) << 8;
  403. unicode |= (c1 & 0xf) << 12;
  404. if (unicode < 0x800 || unicode > 0xffff) return false;
  405. else if (unicode >= 0xd800 && unicode <= 0xdfff) return false; /* for surrogate pairs */
  406. }
  407. else if (c1 >= 0xf0 && c1 <= 0xf7) {
  408. if (i >= inputLength - 3) return false;
  409. unsigned char c2 = input[++i];
  410. if (c2 < 0x80 || c2 > 0xbf) return false;
  411. unsigned char c3 = input[++i];
  412. if (c3 < 0x80 || c3 > 0xbf) return false;
  413. unsigned char c4 = input[++i];
  414. if (c4 < 0x80 || c4 > 0xbf) return false;
  415. unsigned int unicode = c4 & 0xf;
  416. unicode |= (((c4 >> 4) & 0x3) | ((c3 & 0x3) << 2)) << 4;
  417. unicode |= ((c3 >> 2) & 0xf) << 8;
  418. unicode |= (c2 & 0xf) << 12;
  419. unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 16;
  420. unicode |= ((c1 >> 2) & 0x1) << 20;
  421. if (unicode < 0x10000 || unicode > 0x10ffff) return false;
  422. }
  423. else return false;
  424. }
  425. return true;
  426. }
  427. static void appendPostSignature(const char *body, const std::string &userAgent, const std::string &monaKey, BBS2chProxyHttpHeaders *headers)
  428. {
  429. std::map<std::string, std::string> fields;
  430. const char *ptr = body;
  431. while (1) {
  432. const char *tmp = ptr;
  433. while (*tmp != '=' && *tmp != 0) tmp++;
  434. if (*tmp == 0) break;
  435. std::string key(ptr, tmp-ptr);
  436. tmp++;
  437. ptr = tmp;
  438. while (*tmp != '&' && *tmp != 0) tmp++;
  439. std::string deocdedValue = decodeURIComponent(ptr, tmp-ptr, true);
  440. fields.insert(std::make_pair(key, deocdedValue));
  441. if (*tmp == 0) break;
  442. ptr = tmp + 1;
  443. }
  444. char nonce[32];
  445. snprintf(nonce, 32, "%.3f", getCurrentTime());
  446. std::string message;
  447. message.append(fields["bbs"]);
  448. message.append("<>");
  449. message.append(fields["key"]);
  450. message.append("<>");
  451. message.append(fields["time"]);
  452. message.append("<>");
  453. message.append(fields["FROM"]);
  454. message.append("<>");
  455. message.append(fields["mail"]);
  456. message.append("<>");
  457. message.append(fields["MESSAGE"]);
  458. message.append("<>");
  459. message.append(fields["subject"]);
  460. message.append("<>");
  461. message.append(userAgent);
  462. message.append("<>");
  463. message.append(monaKey);
  464. message.append("<>");
  465. message.append("<>");
  466. message.append(nonce);
  467. unsigned char digest[32];
  468. char digestStr[65];
  469. static const char *table = "0123456789abcdef";
  470. proxy2ch_HMAC_SHA256(hmacKey, strlen(hmacKey), message.data(), message.length(), digest);
  471. for (int i=0; i<32; i++) {
  472. unsigned char c = digest[i];
  473. unsigned char upper = (c >> 4) & 0xf;
  474. unsigned char lower = c & 0xf;
  475. digestStr[i*2] = table[upper];
  476. digestStr[i*2+1] = table[lower];
  477. }
  478. digestStr[64] = 0;
  479. headers->set("X-APIKey", appKey);
  480. log_printf(1, "Appended header \"X-APIKey: %s\"\n", appKey);
  481. headers->set("X-PostSig", digestStr);
  482. log_printf(1, "Appended header \"X-PostSig: %s\"\n", digestStr);
  483. headers->set("X-PostNonce", nonce);
  484. log_printf(1, "Appended header \"X-PostNonce: %s\"\n", nonce);
  485. headers->set("X-MonaKey", monaKey);
  486. log_printf(1, "Appended header \"X-MonaKey: %s\"\n", monaKey.c_str());
  487. }
  488. static std::string convertBodyToUTF8(const char *body)
  489. {
  490. std::string newBody;
  491. bool shouldConvertToUTF8 = true;
  492. bool shouldCheckWholeBody = true;
  493. const char *ptr = strstr(body, "submit=");
  494. if (ptr && (ptr == body || *(ptr-1) == '&')) {
  495. ptr += 7;
  496. const char *start = ptr;
  497. while (*ptr != '&' && *ptr != 0) ptr++;
  498. std::string decoded = decodeURIComponent(start, ptr-start, true);
  499. if (decoded.length() != (ptr-start)) {
  500. if (isValidAsUTF8(decoded.data(), decoded.length())) {
  501. shouldConvertToUTF8 = false;
  502. }
  503. shouldCheckWholeBody = false;
  504. }
  505. }
  506. if (shouldCheckWholeBody) {
  507. std::string decoded = decodeURIComponent(body, strlen(body), true);
  508. if (isValidAsUTF8(decoded.data(), decoded.length())) {
  509. shouldConvertToUTF8 = false;
  510. }
  511. }
  512. if (shouldConvertToUTF8) {
  513. ptr = body;
  514. while (1) {
  515. const char *tmp = ptr;
  516. while (*tmp != '=' && *tmp != 0) tmp++;
  517. if (*tmp == 0) break;
  518. std::string key(ptr, tmp-ptr);
  519. tmp++;
  520. ptr = tmp;
  521. while (*tmp != '&' && *tmp != 0) tmp++;
  522. std::string deocdedValue = decodeURIComponent(ptr, tmp-ptr, true);
  523. if (!deocdedValue.empty()) {
  524. char *converted = convertShiftJISToUTF8(deocdedValue.data(), deocdedValue.length());
  525. if (converted) {
  526. deocdedValue = std::string(converted);
  527. free(converted);
  528. }
  529. }
  530. if (!newBody.empty()) newBody.append("&");
  531. newBody.append(key);
  532. newBody.append("=");
  533. newBody.append(encodeURIComponent(deocdedValue.data(), deocdedValue.length(), true));
  534. if (*tmp == 0) break;
  535. ptr = tmp + 1;
  536. }
  537. }
  538. return newBody;
  539. }
  540. static size_t readChunkedBodyIntoBuffer(char **bufferOut, IBBS2chProxySocket *socket)
  541. {
  542. size_t contentLength = 0;
  543. size_t bufferLength = 128;
  544. size_t chunkSize = 0;
  545. char tmp[64];
  546. char *buffer = (char *)malloc(bufferLength);
  547. while (1) {
  548. if (!chunkSize) {
  549. if (!socket->readLine(tmp, 64)) break;
  550. chunkSize = strtol(tmp, NULL, 16);
  551. if (chunkSize == 0) {
  552. break;
  553. }
  554. if (chunkSize + contentLength >= bufferLength) {
  555. bufferLength = chunkSize + contentLength + 1;
  556. buffer = (char *)realloc(buffer, bufferLength);
  557. }
  558. }
  559. size_t ret = socket->read(buffer+contentLength, chunkSize);
  560. if (ret > 0) contentLength += ret;
  561. if (ret < chunkSize) break;
  562. if (!socket->readLine(tmp, 64)) break;
  563. chunkSize = 0;
  564. }
  565. buffer[contentLength] = 0;
  566. if (bufferOut) *bufferOut = buffer;
  567. else free(buffer);
  568. return contentLength;
  569. }
  570. #ifdef _WIN32
  571. const char * strp_weekdays[] =
  572. { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
  573. const char * strp_monthnames[] =
  574. { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
  575. bool strp_atoi(const char * & s, int & result, int low, int high, int offset)
  576. {
  577. bool worked = false;
  578. char * end;
  579. unsigned long num = strtoul(s, & end, 10);
  580. if (num >= (unsigned long)low && num <= (unsigned long)high)
  581. {
  582. result = (int)(num + offset);
  583. s = end;
  584. worked = true;
  585. }
  586. return worked;
  587. }
  588. char * strptime(const char *s, const char *format, struct tm *tm)
  589. {
  590. bool working = true;
  591. while (working && *format && *s)
  592. {
  593. switch (*format)
  594. {
  595. case '%':
  596. {
  597. ++format;
  598. switch (*format)
  599. {
  600. case 'a':
  601. case 'A': // weekday name
  602. tm->tm_wday = -1;
  603. working = false;
  604. for (size_t i = 0; i < 7; ++ i)
  605. {
  606. size_t len = strlen(strp_weekdays[i]);
  607. if (!strnicmp(strp_weekdays[i], s, len))
  608. {
  609. tm->tm_wday = i;
  610. s += len;
  611. working = true;
  612. break;
  613. }
  614. else if (!strnicmp(strp_weekdays[i], s, 3))
  615. {
  616. tm->tm_wday = i;
  617. s += 3;
  618. working = true;
  619. break;
  620. }
  621. }
  622. break;
  623. case 'b':
  624. case 'B':
  625. case 'h': // month name
  626. tm->tm_mon = -1;
  627. working = false;
  628. for (size_t i = 0; i < 12; ++ i)
  629. {
  630. size_t len = strlen(strp_monthnames[i]);
  631. if (!strnicmp(strp_monthnames[i], s, len))
  632. {
  633. tm->tm_mon = i;
  634. s += len;
  635. working = true;
  636. break;
  637. }
  638. else if (!strnicmp(strp_monthnames[i], s, 3))
  639. {
  640. tm->tm_mon = i;
  641. s += 3;
  642. working = true;
  643. break;
  644. }
  645. }
  646. break;
  647. case 'd':
  648. case 'e': // day of month number
  649. working = strp_atoi(s, tm->tm_mday, 1, 31, 0);
  650. break;
  651. case 'D': // %m/%d/%y
  652. {
  653. const char * s_save = s;
  654. working = strp_atoi(s, tm->tm_mon, 1, 12, -1);
  655. if (working && *s == '/')
  656. {
  657. ++ s;
  658. working = strp_atoi(s, tm->tm_mday, 1, 31, 0);
  659. if (working && *s == '/')
  660. {
  661. ++ s;
  662. working = strp_atoi(s, tm->tm_year, 0, 99, 0);
  663. if (working && tm->tm_year < 69)
  664. tm->tm_year += 100;
  665. }
  666. }
  667. if (!working)
  668. s = s_save;
  669. }
  670. break;
  671. case 'H': // hour
  672. working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
  673. break;
  674. case 'I': // hour 12-hour clock
  675. working = strp_atoi(s, tm->tm_hour, 1, 12, 0);
  676. break;
  677. case 'j': // day number of year
  678. working = strp_atoi(s, tm->tm_yday, 1, 366, -1);
  679. break;
  680. case 'm': // month number
  681. working = strp_atoi(s, tm->tm_mon, 1, 12, -1);
  682. break;
  683. case 'M': // minute
  684. working = strp_atoi(s, tm->tm_min, 0, 59, 0);
  685. break;
  686. case 'n': // arbitrary whitespace
  687. case 't':
  688. while (isspace((int)*s))
  689. ++s;
  690. break;
  691. case 'p': // am / pm
  692. if (!strnicmp(s, "am", 2))
  693. { // the hour will be 1 -> 12 maps to 12 am, 1 am .. 11 am, 12 noon 12 pm .. 11 pm
  694. if (tm->tm_hour == 12) // 12 am == 00 hours
  695. tm->tm_hour = 0;
  696. }
  697. else if (!strnicmp(s, "pm", 2))
  698. {
  699. if (tm->tm_hour < 12) // 12 pm == 12 hours
  700. tm->tm_hour += 12; // 1 pm -> 13 hours, 11 pm -> 23 hours
  701. }
  702. else
  703. working = false;
  704. break;
  705. case 'r': // 12 hour clock %I:%M:%S %p
  706. {
  707. const char * s_save = s;
  708. working = strp_atoi(s, tm->tm_hour, 1, 12, 0);
  709. if (working && *s == ':')
  710. {
  711. ++ s;
  712. working = strp_atoi(s, tm->tm_min, 0, 59, 0);
  713. if (working && *s == ':')
  714. {
  715. ++ s;
  716. working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
  717. if (working && isspace((int)*s))
  718. {
  719. ++ s;
  720. while (isspace((int)*s))
  721. ++s;
  722. if (!strnicmp(s, "am", 2))
  723. { // the hour will be 1 -> 12 maps to 12 am, 1 am .. 11 am, 12 noon 12 pm .. 11 pm
  724. if (tm->tm_hour == 12) // 12 am == 00 hours
  725. tm->tm_hour = 0;
  726. }
  727. else if (!strnicmp(s, "pm", 2))
  728. {
  729. if (tm->tm_hour < 12) // 12 pm == 12 hours
  730. tm->tm_hour += 12; // 1 pm -> 13 hours, 11 pm -> 23 hours
  731. }
  732. else
  733. working = false;
  734. }
  735. }
  736. }
  737. if (!working)
  738. s = s_save;
  739. }
  740. break;
  741. case 'R': // %H:%M
  742. {
  743. const char * s_save = s;
  744. working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
  745. if (working && *s == ':')
  746. {
  747. ++ s;
  748. working = strp_atoi(s, tm->tm_min, 0, 59, 0);
  749. }
  750. if (!working)
  751. s = s_save;
  752. }
  753. break;
  754. case 'S': // seconds
  755. working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
  756. break;
  757. case 'T': // %H:%M:%S
  758. {
  759. const char * s_save = s;
  760. working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
  761. if (working && *s == ':')
  762. {
  763. ++ s;
  764. working = strp_atoi(s, tm->tm_min, 0, 59, 0);
  765. if (working && *s == ':')
  766. {
  767. ++ s;
  768. working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
  769. }
  770. }
  771. if (!working)
  772. s = s_save;
  773. }
  774. break;
  775. case 'w': // weekday number 0->6 sunday->saturday
  776. working = strp_atoi(s, tm->tm_wday, 0, 6, 0);
  777. break;
  778. case 'Y': // year
  779. working = strp_atoi(s, tm->tm_year, 1900, 65535, -1900);
  780. break;
  781. case 'y': // 2-digit year
  782. working = strp_atoi(s, tm->tm_year, 0, 99, 0);
  783. if (working && tm->tm_year < 69)
  784. tm->tm_year += 100;
  785. break;
  786. case '%': // escaped
  787. if (*s != '%')
  788. working = false;
  789. ++s;
  790. break;
  791. default:
  792. working = false;
  793. }
  794. }
  795. break;
  796. case ' ':
  797. case '\t':
  798. case '\r':
  799. case '\n':
  800. case '\f':
  801. case '\v':
  802. // zero or more whitespaces:
  803. while (isspace((int)*s))
  804. ++ s;
  805. break;
  806. default:
  807. // match character
  808. if (*s != *format)
  809. working = false;
  810. else
  811. ++s;
  812. break;
  813. }
  814. ++format;
  815. }
  816. return (working?(char *)s:0);
  817. }
  818. #endif