utils.h 27 KB

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