BBS2chProxyConnection.cpp 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253
  1. #include <string>
  2. #include <vector>
  3. #include <map>
  4. #include <set>
  5. #include <sstream>
  6. #include <stdexcept>
  7. #include <pthread.h>
  8. #include <time.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #include <curl/curl.h>
  13. #ifdef USE_LUA
  14. #include <lua.hpp>
  15. #endif
  16. #ifdef _WIN32
  17. #include <fcntl.h>
  18. #include <winsock2.h>
  19. #include <ws2tcpip.h>
  20. #include <mswsock.h>
  21. #define CLOSESOCKET(x) closesocket(x)
  22. #else
  23. #include <sys/socket.h>
  24. #include <netinet/in.h>
  25. #include <netdb.h>
  26. #include <arpa/inet.h>
  27. #define CLOSESOCKET(x) close(x)
  28. #endif
  29. #include "BBS2chProxyConnection.h"
  30. #include "DataStorage.h"
  31. #include "hmac.h"
  32. #include "stringEncodingConverter.h"
  33. #include "BBS2chProxyRawSocket.h"
  34. #ifdef USE_MITM
  35. #include "BBS2chProxySecureSocket.h"
  36. #endif
  37. //#define DEBUG 1
  38. extern char *proxy_server;
  39. extern long proxy_port;
  40. extern long proxy_type;
  41. extern long timeout;
  42. extern char *user_agent;
  43. extern char *api_ua_dat;
  44. extern char *x_2ch_ua_dat;
  45. extern char *appKey;
  46. extern char *hmacKey;
  47. extern int allow_chunked;
  48. extern int curl_features;
  49. extern unsigned int curl_version_number;
  50. extern bool accept_https;
  51. extern int force_5chnet;
  52. extern int force_5chnet_https;
  53. extern int force_ipv4;
  54. extern char *bbsmenu_url;
  55. extern char *api_server;
  56. extern std::map<std::string, std::string> bbscgi_headers;
  57. extern int gikofix;
  58. extern CURLSH *curl_share;
  59. extern char *lua_script;
  60. extern unsigned int api_mode;
  61. extern std::vector<std::string> bbscgi_postorder;
  62. #ifdef USE_MITM
  63. extern unsigned int mitm_mode;
  64. #endif
  65. extern void log_printf(int level, const char *format ...);
  66. #include "utils.h"
  67. static std::string monaKey;
  68. static std::map<std::string, double> monaKeyIssueTime;
  69. static std::set<std::string> expiredMonaKeys;
  70. #ifdef USE_LUA
  71. extern "C" {
  72. static int lua_hmacSHA256(lua_State *l)
  73. {
  74. static const char *table = "0123456789abcdef";
  75. size_t keyLength, dataLength;
  76. const char *key = luaL_checklstring(l, 1, &keyLength);
  77. const char *data = luaL_checklstring(l, 2, &dataLength);
  78. if (!key || !data) return 0;
  79. unsigned char digest[32];
  80. char digestStr[65];
  81. proxy2ch_HMAC_SHA256(key, keyLength, data, dataLength, digest);
  82. for (int i=0; i<32; i++) {
  83. unsigned char c = digest[i];
  84. unsigned char upper = (c >> 4) & 0xf;
  85. unsigned char lower = c & 0xf;
  86. digestStr[i*2] = table[upper];
  87. digestStr[i*2+1] = table[lower];
  88. }
  89. digestStr[64] = 0;
  90. lua_pushstring(l, digestStr);
  91. return 1;
  92. }
  93. static int lua_decodeURIComponent(lua_State *l)
  94. {
  95. size_t length;
  96. const char *input = luaL_checklstring(l, 1, &length);
  97. if (!input) return 0;
  98. bool decodePlus = true;
  99. if (!lua_isnoneornil(l, 2)) {
  100. decodePlus = (lua_toboolean(l, 2));
  101. }
  102. std::string output = decodeURIComponent(input, length, decodePlus);
  103. lua_pushstring(l, output.c_str());
  104. return 1;
  105. }
  106. static int lua_encodeURIComponent(lua_State *l)
  107. {
  108. size_t length;
  109. const char *input = luaL_checklstring(l, 1, &length);
  110. if (!input) return 0;
  111. bool spaceAsPlus = true;
  112. if (!lua_isnoneornil(l, 2)) {
  113. spaceAsPlus = (lua_toboolean(l, 2));
  114. }
  115. std::string output = encodeURIComponent(input, length, spaceAsPlus);
  116. lua_pushstring(l, output.c_str());
  117. return 1;
  118. }
  119. static int lua_convertShiftJISToUTF8(lua_State *l)
  120. {
  121. size_t length;
  122. const char *input = luaL_checklstring(l, 1, &length);
  123. if (!input) return 0;
  124. if (length > 0) {
  125. char *output = convertShiftJISToUTF8(input, length);
  126. if (!output) lua_pushnil(l);
  127. else {
  128. lua_pushstring(l, output);
  129. free(output);
  130. }
  131. }
  132. else lua_pushstring(l, "");
  133. return 1;
  134. }
  135. static int lua_isExpiredKey(lua_State *l)
  136. {
  137. size_t length;
  138. const char *input = luaL_checklstring(l, 1, &length);
  139. if (!input) return 0;
  140. if (expiredMonaKeys.count(input)) {
  141. lua_pushboolean(l, 1);
  142. }
  143. else lua_pushboolean(l, 0);
  144. return 1;
  145. }
  146. }
  147. #endif
  148. void BBS2chProxyConnection::setMonaKey(const std::string &key, int reason) {
  149. pthread_mutex_lock(mutex);
  150. if (key.empty()) {
  151. if ((reason >= 3320 && reason <= 3324) || (reason >= 3390 && reason <= 3392)) {
  152. expiredMonaKeys.insert(monaKeyForRequest);
  153. }
  154. log_printf(1, "Reset MonaKey\n");
  155. }
  156. else {
  157. monaKeyIssueTime[key] = getCurrentTime();
  158. log_printf(1, "Updated MonaKey: %s\n", key.c_str());
  159. }
  160. monaKey = key;
  161. pthread_mutex_unlock(mutex);
  162. }
  163. std::string BBS2chProxyConnection::getMonaKey() {
  164. if (monaKey.empty())
  165. return "00000000-0000-0000-0000-000000000000";
  166. return monaKey;
  167. }
  168. void *BBS2chProxyConnection::launch(void *param)
  169. {
  170. reinterpret_cast<BBS2chProxyConnection *>(param)->connect();
  171. return (void *)NULL;
  172. }
  173. void BBS2chProxyConnection::run(void)
  174. {
  175. pthread_t thread;
  176. pthread_attr_t thread_attr;
  177. pthread_attr_init(&thread_attr);
  178. pthread_attr_setdetachstate(&thread_attr , PTHREAD_CREATE_DETACHED);
  179. if(0 != pthread_create(&thread , &thread_attr , &BBS2chProxyConnection::launch , this))
  180. perror("pthread_create");
  181. pthread_attr_destroy(&thread_attr);
  182. }
  183. void *BBS2chProxyConnection::tunnel_c2s(void *param)
  184. {
  185. char buf[2048];
  186. fd_set fds;
  187. int sock_c = reinterpret_cast<BBS2chProxyConnection *>(param)->sock_c;
  188. int sock_s = reinterpret_cast<BBS2chProxyConnection *>(param)->sock_s;
  189. #ifdef _WIN32
  190. struct timeval timeout;
  191. timeout.tv_sec = 0;
  192. timeout.tv_usec = 10000;
  193. #endif
  194. while(1) {
  195. #ifdef _WIN32
  196. FD_ZERO(&fds);
  197. FD_SET(sock_c,&fds);
  198. if(select(sock_c + 1,&fds,NULL,NULL,&timeout) < 0) break;
  199. if(FD_ISSET(sock_c,&fds)) {
  200. #endif
  201. int ret = recv(sock_c, buf, 2048, 0);
  202. if(ret > 0) send(sock_s, buf, ret, 0);
  203. else if(ret <= 0) break;
  204. #ifdef _WIN32
  205. }
  206. #endif
  207. }
  208. //fprintf(stderr,"tunnel_c2s end\n");
  209. CLOSESOCKET(sock_s);
  210. return NULL;
  211. }
  212. void *BBS2chProxyConnection::tunnel_s2c(void *param)
  213. {
  214. char buf[2048];
  215. fd_set fds;
  216. int sock_c = reinterpret_cast<BBS2chProxyConnection *>(param)->sock_c;
  217. int sock_s = reinterpret_cast<BBS2chProxyConnection *>(param)->sock_s;
  218. while(1) {
  219. int ret = recv(sock_s, buf, 2048, 0);
  220. if(ret > 0) send(sock_c, buf, ret, 0);
  221. else if(ret <= 0) break;
  222. }
  223. //fprintf(stderr,"tunnel_s2c end\n");
  224. CLOSESOCKET(sock_c);
  225. return NULL;
  226. }
  227. int BBS2chProxyConnection::tunnel(const char *addr, int port)
  228. {
  229. struct sockaddr_in server;
  230. memset(&server, 0, sizeof(server));
  231. server.sin_family = AF_INET;
  232. server.sin_addr.s_addr = inet_addr(addr);
  233. server.sin_port = htons(port);
  234. if(server.sin_addr.s_addr == 0xffffffff) {
  235. struct hostent *host;
  236. host = gethostbyname(addr);
  237. if (host == NULL) {
  238. sendResponse(400, "Bad Request", socketToClient);
  239. return -1;
  240. }
  241. server.sin_addr.s_addr = *(unsigned int *)host->h_addr_list[0];
  242. }
  243. log_printf(1,"Tunneling connection to %s:%d\n",addr,port);
  244. sock_s = socket(AF_INET, SOCK_STREAM, 0);
  245. if(-1 == ::connect(sock_s, (struct sockaddr *)&server, sizeof(server))) {
  246. perror("connect");
  247. sendResponse(400, "Bad Request", socketToClient);
  248. return -1;
  249. }
  250. send(sock_c, "HTTP/1.1 200 Connection established\r\n\r\n", 39, 0);
  251. pthread_t thread_c2s, thread_s2c;
  252. if(0 != pthread_create(&thread_c2s, NULL, &BBS2chProxyConnection::tunnel_c2s, this))
  253. perror("pthread_create");
  254. if(0 != pthread_create(&thread_s2c, NULL, &BBS2chProxyConnection::tunnel_s2c, this))
  255. perror("pthread_create");
  256. pthread_join(thread_c2s, NULL);
  257. pthread_join(thread_s2c, NULL);
  258. log_printf(1,"Tunneling connection to %s:%d finished\n",addr,port);
  259. return 0;
  260. }
  261. void BBS2chProxyConnection::connect(void)
  262. {
  263. char method[32], url[1024], protocol[32];
  264. int i;
  265. char *buf, *ptr;
  266. bool html2dat = false;
  267. bool html2dat_kako = false;
  268. regmatch_t match[7];
  269. long statusCode = 0;
  270. std::string baseURL;
  271. socketToClient = new BBS2chProxyRawSocket(sock_c);
  272. buf = (char *)malloc(16384);
  273. if(!buf) goto end;
  274. beginHandleRequest:
  275. ptr = buf;
  276. if(!socketToClient->readLine(buf, 1024)) {
  277. sendResponse(400, "Bad Request", socketToClient);
  278. statusCode = 400;
  279. goto end;
  280. }
  281. i=0;
  282. while(*ptr != ' ' && *ptr != 0 && i < 32) method[i++] = *ptr++;
  283. if(*ptr == 0 || i == 32) {
  284. sendResponse(400, "Bad Request", socketToClient);
  285. statusCode = 400;
  286. goto end;
  287. }
  288. method[i] = 0;
  289. ptr++;
  290. i=0;
  291. while(*ptr != ' ' && *ptr != 0 && i < 1024) url[i++] = *ptr++;
  292. if(*ptr == 0 || i == 1024) {
  293. sendResponse(400, "Bad Request", socketToClient);
  294. statusCode = 400;
  295. goto end;
  296. }
  297. url[i] = 0;
  298. ptr++;
  299. i=0;
  300. while(*ptr != '\r' && *ptr != '\n' && *ptr != 0 && i < 32) protocol[i++] = *ptr++;
  301. if(*ptr == 0 || i == 32) {
  302. sendResponse(400, "Bad Request", socketToClient);
  303. statusCode = 400;
  304. goto end;
  305. }
  306. protocol[i] = 0;
  307. log_printf(1, "Received %s %s %s\n",method,url,protocol);
  308. if(strcasecmp(method,"GET") && strcasecmp(method,"POST") && strcasecmp(method,"HEAD") && strcasecmp(method,"CONNECT") && strcasecmp(method,"PUT") && strcasecmp(method, "OPTIONS")) {
  309. sendResponse(400, "Bad Request", socketToClient);
  310. statusCode = 400;
  311. goto end;
  312. }
  313. if(!url[0]) {
  314. sendResponse(400, "Bad Request", socketToClient);
  315. statusCode = 400;
  316. goto end;
  317. }
  318. if(strncasecmp(protocol,"HTTP",4)) {
  319. sendResponse(400, "Bad Request", socketToClient);
  320. statusCode = 400;
  321. goto end;
  322. }
  323. if(!strcasecmp(method,"CONNECT")) {
  324. if(!accept_https || !baseURL.empty()) {
  325. sendResponse(400, "Bad Request", socketToClient);
  326. statusCode = 400;
  327. goto end;
  328. }
  329. while(socketToClient->readLine(buf, 16384)) {
  330. if(!strcmp("\r\n",buf)) break;
  331. }
  332. int port = 443;
  333. char *ptr = strchr(url, ':');
  334. if(ptr) {
  335. *ptr = 0;
  336. port = atoi(ptr+1);
  337. }
  338. #ifdef USE_MITM
  339. bool useMITM = false;
  340. if (mitm_mode) {
  341. if (mitm_mode == 2) useMITM = true;
  342. else if (mitm_mode == 1) {
  343. int length = strlen(url);
  344. if (length >= strlen("5ch.net") && !strcasecmp(url+length-strlen("5ch.net"), "5ch.net")) {
  345. int pos = length-strlen("5ch.net");
  346. if (pos == 0) useMITM = true;
  347. else useMITM = (url[pos-1] == '.');
  348. }
  349. else if (length >= strlen("2ch.net") && !strcasecmp(url+length-strlen("2ch.net"), "2ch.net")) {
  350. int pos = length-strlen("2ch.net");
  351. if (pos == 0) useMITM = true;
  352. else useMITM = (url[pos-1] == '.');
  353. }
  354. else if (length >= strlen("bbspink.com") && !strcasecmp(url+length-strlen("bbspink.com"), "bbspink.com")) {
  355. int pos = length-strlen("bbspink.com");
  356. if (pos == 0) useMITM = true;
  357. else useMITM = (url[pos-1] == '.');
  358. }
  359. }
  360. }
  361. if (useMITM) {
  362. socketToClient->writeString("HTTP/1.1 200 Connection established\r\n\r\n");
  363. if (port == 80) {
  364. baseURL = "http://";
  365. baseURL += url;
  366. goto beginHandleRequest;
  367. }
  368. else {
  369. try {
  370. BBS2chProxySecureSocket *secureSocket = new BBS2chProxySecureSocket(sock_c, url);
  371. delete socketToClient;
  372. socketToClient = secureSocket;
  373. baseURL = "https://";
  374. baseURL += url;
  375. if (port != 443) {
  376. snprintf(buf, 16384, "%d", port);
  377. baseURL += ":";
  378. baseURL += buf;
  379. }
  380. goto beginHandleRequest;
  381. } catch (const std::runtime_error& e) {
  382. log_printf(0, "%s\n", e.what());
  383. sendResponse(400, "Bad Request", socketToClient);
  384. statusCode = 400;
  385. goto end;
  386. }
  387. }
  388. }
  389. else
  390. #endif
  391. {
  392. delete socketToClient;
  393. socketToClient = NULL;
  394. tunnel(url, port);
  395. goto end;
  396. }
  397. }
  398. #if USE_MITM
  399. if(!baseURL.empty()) {
  400. std::string completeURL = baseURL + url;
  401. strcpy(url, completeURL.c_str());
  402. log_printf(1, "Running as MITM proxy for %s\n", url);
  403. }
  404. #endif
  405. if(force_5chnet && !strncasecmp(url, "http", 4)) {
  406. char *ptr = strstr(url, "://");
  407. if (ptr) {
  408. ptr += 3;
  409. char *end = ptr;
  410. while(*end != '/' && *end != 0) end++;
  411. ptr = strstr(ptr,".2ch.net");
  412. if(ptr && ptr < end && memcmp(ptr-4,"menu.",5)) {
  413. memcpy(ptr+1,"5ch",3);
  414. force5ch = true;
  415. log_printf(1, "Detected *.2ch.net URL, changed target URL to %s\n",url);
  416. }
  417. }
  418. }
  419. if(regexec(&regex, url, 6, match, 0) != REG_NOMATCH) {
  420. if((appKey && (api_mode & 1)) || strncasecmp(url+match[1].rm_so,"headline.",9)) html2dat = true;
  421. }
  422. else if(regexec(&regex_kako, url, 7, match, 0) != REG_NOMATCH) {
  423. html2dat_kako = true;
  424. }
  425. else if(regexec(&regex_offlaw, url, 5, match, 0) != REG_NOMATCH) {
  426. char *thread = strstr(url,"key=");
  427. if(thread) {
  428. match[6].rm_so = thread+4-url;
  429. match[6].rm_eo = thread+4-url;
  430. char *ptr = thread+4;
  431. while(*ptr != '&' && *ptr != 0) {
  432. ptr++;
  433. match[6].rm_eo++;
  434. }
  435. if(match[6].rm_so != match[6].rm_eo) html2dat_kako = true;
  436. }
  437. }
  438. if(html2dat || html2dat_kako) {
  439. char tmp[1024];
  440. regmatch_t *tid = html2dat ? match+5 : match+6;
  441. strcpy(tmp,url);
  442. tmp[match[2].rm_eo] = 0;
  443. tmp[match[4].rm_eo] = 0;
  444. tmp[tid->rm_eo] = 0;
  445. if(!appKey || !(api_mode & 1) || html2dat_kako) {
  446. log_printf(1, "Retrieving thread via read.cgi...\n");
  447. snprintf(url,1024,"%s/%s/%s",tmp+match[1].rm_so,tmp+match[4].rm_so,tmp+tid->rm_so);
  448. threadKey = std::string(url);
  449. snprintf(url,1024,"%s://%s/test/read.cgi/%s/%s/",force_5chnet_https?"https":"http",tmp+match[1].rm_so,tmp+match[4].rm_so,tmp+tid->rm_so);
  450. statusCode = datProxy(url, method);
  451. }
  452. else {
  453. log_printf(1, "Retrieving thread via API...\n");
  454. tmp[match[1].rm_eo] = 0;
  455. snprintf(url,1024,"https://%s/v1/%s/%s/%s",api_server,tmp+match[1].rm_so,tmp+match[4].rm_so,tmp+tid->rm_so);
  456. statusCode = datProxyAPI(url, method);
  457. }
  458. }
  459. else {
  460. char urlMod[1024];
  461. strcpy(urlMod,url);
  462. if(force_5chnet_https) {
  463. const char *host = "5ch.net";
  464. char *ptr = strstr(url,host);
  465. if(!ptr) {
  466. host = "2ch.net";
  467. ptr = strstr(url,host);
  468. }
  469. if(!ptr) {
  470. host = "bbspink.com";
  471. ptr = strstr(url,host);
  472. }
  473. if(ptr) {
  474. char *start = url+7;
  475. char *end = strchr(start,'/');
  476. int hostLength = strlen(host);
  477. if(!end) end = url+strlen(url);
  478. if(ptr >= start && ptr < end && (*(ptr-1) == '.' || *(ptr-1) == '/') && (*(ptr+hostLength) == '/' || *(ptr+hostLength) == ':' || *(ptr+hostLength) == 0)) {
  479. log_printf(1, "This is %s URL, connecting with HTTPS\n",host);
  480. if(*(ptr+hostLength) == ':') {
  481. *(ptr+hostLength) = 0;
  482. snprintf(urlMod,1024,"https://%s%s",start,end);
  483. *(ptr+hostLength) = ':';
  484. }
  485. else snprintf(urlMod,1024,"https://%s",start);
  486. log_printf(2, "URL is: %s\n",urlMod);
  487. }
  488. }
  489. }
  490. if(bbsmenu_url && !strcmp(url, bbsmenu_url)) {
  491. log_printf(1, "Running as a BBS menu proxy...\n");
  492. statusCode = bbsmenuProxy(urlMod, method);
  493. }
  494. else {
  495. bool isPostRequest = !strcasecmp(method, "POST");
  496. bool isPutRequest = !strcasecmp(method, "PUT");
  497. if(isPostRequest && (strstr(urlMod,".5ch.net") || strstr(urlMod,".bbspink.com")) && strstr(urlMod,"/test/bbs.cgi")) bbscgi = true;
  498. log_printf(1, "Not a thread request, passthrough...\n");
  499. BBS2chProxyHttpHeaders headers;
  500. while (socketToClient->readLine(buf, 16384)) {
  501. if (!strcmp("\r\n",buf)) break;
  502. headers.add(buf);
  503. }
  504. if (force_5chnet) {
  505. if (headers.has("Host")) {
  506. std::string host = headers.get("Host");
  507. size_t pos = host.find("2ch.net");
  508. if (pos != std::string::npos && pos+7 == host.length()) {
  509. if (pos == 0 || host[pos-1] == '.') {
  510. host.replace(pos, 1, "5");
  511. headers.set("Host", host);
  512. }
  513. }
  514. }
  515. if (bbscgi && headers.has("Referer")) {
  516. std::string referrer = headers.get("Referer");
  517. size_t pos = referrer.find("2ch.net");
  518. if (pos != std::string::npos) {
  519. if (pos == 0 || referrer[pos-1] == '.') {
  520. referrer.replace(pos, 1, "5");
  521. headers.set("Referer", referrer);
  522. }
  523. }
  524. }
  525. }
  526. headers.remove("Connection");
  527. if (user_agent) headers.remove("User-Agent");
  528. if (headers.has("Content-Length")) {
  529. content_length = atoi(headers.get("Content-Length").c_str());
  530. }
  531. if (headers.has("Expect")) {
  532. if (!strcasecmp(headers.get("Expect").c_str(), "100-continue")) {
  533. log_printf(1, "Received Expect: 100-continue header, sending 100 Continue response to the client\n");
  534. socketToClient->writeString("HTTP/1.1 100 Continue\r\n\r\n");
  535. }
  536. }
  537. if (bbscgi && content_length > 0) {
  538. bool isNotFormURLEncoded = false;
  539. if (headers.has("Content-Type") && headers.get("Content-Type").find("application/x-www-form-urlencoded") == std::string::npos) {
  540. isNotFormURLEncoded = true;
  541. }
  542. if (!isNotFormURLEncoded) {
  543. headers.remove("Content-Length");
  544. if (!bbscgi_headers.empty()) {
  545. for (std::map<std::string, std::string>::iterator it = bbscgi_headers.begin(); it != bbscgi_headers.end(); it++) {
  546. if (headers.has(it->first)) {
  547. log_printf(1, "Ignoring header \"%s\" because custom header will be appended\n", it->first.c_str());
  548. headers.remove(it->first);
  549. }
  550. }
  551. }
  552. char *postdata = (char *)calloc(content_length+1, 1);
  553. content_length = socketToClient->read(postdata, content_length);
  554. if(gikofix) {
  555. char *ptr = postdata+content_length-1;
  556. while(*ptr == '\r' || *ptr == '\n') {
  557. *ptr-- = 0;
  558. }
  559. }
  560. curl_slist *headersForCurl = NULL;
  561. headersForCurl = headers.appendToCurlSlist(headersForCurl);
  562. statusCode = bbsCgiProxy(urlMod, protocol, &headers, postdata);
  563. free(postdata);
  564. curl_slist_free_all(headersForCurl);
  565. goto end;
  566. }
  567. }
  568. curl_slist *headersForCurl = NULL;
  569. headersForCurl = headers.appendToCurlSlist(headersForCurl);
  570. if (!headers.has("Expect")) {
  571. headersForCurl = curl_slist_append(headersForCurl, "Expext:");
  572. }
  573. CURL *curl = curl_easy_init();
  574. if(curl) {
  575. CURLcode res;
  576. if(curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  577. curl_easy_setopt(curl, CURLOPT_URL, urlMod);
  578. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  579. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  580. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_proxy);
  581. curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
  582. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_proxy);
  583. curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
  584. if(content_length) {
  585. /* set Content-Length explicitly via API to work properly with curl >= 7.66.0 */
  586. if(isPostRequest)
  587. curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, content_length);
  588. else if(isPutRequest)
  589. curl_easy_setopt(curl, CURLOPT_INFILESIZE, content_length);
  590. }
  591. curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback_proxy);
  592. curl_easy_setopt(curl, CURLOPT_READDATA, this);
  593. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  594. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  595. //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  596. if(force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  597. if(!strncasecmp(protocol,"HTTP/1.0",8) && !strncasecmp(urlMod, "http://", 7)) {
  598. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  599. }
  600. else {
  601. /* force use HTTP 1.1 because CURL_HTTP_VERSION_2TLS is used on curl (w/ nghttp2) >= 7.62.0 */
  602. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  603. }
  604. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
  605. if(user_agent) {
  606. curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
  607. }
  608. if(isPostRequest) {
  609. curl_easy_setopt(curl, CURLOPT_POST, 1L);
  610. }
  611. else if(isPutRequest) {
  612. curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
  613. }
  614. else if(!strcasecmp(method, "HEAD")) {
  615. curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
  616. }
  617. else if(!strcasecmp(method, "OPTIONS")) {
  618. curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
  619. }
  620. if(proxy_server) {
  621. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  622. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  623. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  624. }
  625. res = curl_easy_perform(curl);
  626. if(res != CURLE_OK) {
  627. log_printf(0,"curl error: %s (%s)\n",curl_easy_strerror(res),urlMod);
  628. if(!status) sendResponse(503, "Service Unavailable", socketToClient);
  629. statusCode = 503;
  630. }
  631. else {
  632. if(chunked) {
  633. socketToClient->writeString("0\r\n\r\n");
  634. }
  635. curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &statusCode);
  636. }
  637. curl_easy_cleanup(curl);
  638. }
  639. curl_slist_free_all(headersForCurl);
  640. }
  641. }
  642. end:
  643. if(statusCode) log_printf(1, "Returned status code %d to client\n",statusCode);
  644. if(buf) free(buf);
  645. if(socketToClient) socketToClient->close();
  646. delete this;
  647. }
  648. int BBS2chProxyConnection::datProxy(const char *url, const char *method)
  649. {
  650. DataStorage *html = NULL;
  651. long statusCode = 0;
  652. long rangeStart = 0, rangeEnd = 0;
  653. time_t lastModified = 0;
  654. time_t ifModifiedSince = 0;
  655. char *buf = (char *)malloc(16384);
  656. BBS2chProxyHttpHeaders headers;
  657. if(!buf) goto last;
  658. while(socketToClient->readLine(buf, 16384)) {
  659. //fprintf(stderr,"%s",buf);
  660. if(!strcmp("\r\n",buf)) break;
  661. headers.add(buf);
  662. };
  663. if(headers.has("Range")) {
  664. std::string value = headers.get("Range");
  665. if(value.find("bytes=") == 0 && value.find(",") == std::string::npos) {
  666. char *ptr = (char *)value.c_str() + 6;
  667. if(*ptr == '-') {
  668. rangeStart = atoi(ptr);
  669. }
  670. else {
  671. rangeStart = strtol(ptr, &ptr, 10);
  672. if(*ptr == '-') ptr++;
  673. if(*ptr && *ptr != '\r') {
  674. rangeEnd = strtol(ptr, NULL, 10);
  675. if(rangeEnd && rangeStart > rangeEnd) {
  676. sendResponse(416, "Requested range not satisfiable", socketToClient);
  677. statusCode = 416;
  678. goto last;
  679. }
  680. }
  681. }
  682. //fprintf(stderr, "range=%ld-%ld\n",rangeStart,rangeEnd);
  683. }
  684. else {
  685. sendResponse(416, "Requested range not satisfiable", socketToClient);
  686. statusCode = 416;
  687. goto last;
  688. }
  689. }
  690. if(headers.has("If-Modified-Since")) {
  691. struct tm time_ = {};
  692. strptime(headers.get("If-Modified-Since").c_str(), httpTimestampFmt, &time_);
  693. ifModifiedSince = mktime(&time_);
  694. }
  695. if(rangeStart > 0) {
  696. PBBS2chProxyThreadInfo info;
  697. pthread_mutex_lock(mutex);
  698. BBS2chProxyThreadCache::iterator it = threadCache->find(threadKey);
  699. if(it != threadCache->end()) {
  700. info = it->second;
  701. }
  702. pthread_mutex_unlock(mutex);
  703. log_printf(5,"range request from %ld bytes\n",rangeStart);
  704. if(info) {
  705. int from = info->lastResNum;
  706. int alreadyRead = info->cachedSize;
  707. int lastResLength = info->cachedData->length;
  708. log_printf(5,"hit %s: cached %d bytes, last res size %d\n",threadKey.c_str(),alreadyRead,lastResLength);
  709. if(rangeStart <= alreadyRead && rangeStart >= alreadyRead - lastResLength) {
  710. CURL *curl = curl_easy_init();
  711. if(curl) {
  712. CURLcode res;
  713. DataStorage *dat = new DataStorage();
  714. log_printf(5,"partial access from res num %d\n",from);
  715. snprintf(buf,16384,"%s%d-n",url,from);
  716. if(curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  717. curl_easy_setopt(curl, CURLOPT_URL, buf);
  718. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  719. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  720. curl_easy_setopt(curl, CURLOPT_ENCODING, "");
  721. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  722. curl_easy_setopt(curl, CURLOPT_WRITEDATA, dat);
  723. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  724. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  725. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  726. if(force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  727. if(proxy_server) {
  728. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  729. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  730. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  731. }
  732. if(user_agent) {
  733. curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
  734. }
  735. else if(headers.has("User-Agent")) {
  736. curl_easy_setopt(curl, CURLOPT_USERAGENT, headers.get("User-Agent").c_str());
  737. }
  738. res = curl_easy_perform(curl);
  739. if(res == CURLE_OK) {
  740. curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &statusCode);
  741. curl_easy_cleanup(curl);
  742. if(statusCode == 200 && dat->length) {
  743. DataStorage *updated = html2dat(dat, from, &lastModified, true);
  744. if(ifModifiedSince && lastModified && updated && updated->length == lastResLength) {
  745. struct tm time_ = {};
  746. gmtime_r(&lastModified,&time_);
  747. time_t tmp = mktime(&time_);
  748. if(ifModifiedSince >= tmp) {
  749. sendResponse(304, "Not Modified", socketToClient);
  750. log_printf(5,"not modified!\n");
  751. delete updated;
  752. delete dat;
  753. statusCode = 304;
  754. goto last;
  755. }
  756. }
  757. if(updated && updated->length && updated->length >= lastResLength) {
  758. html = new DataStorage(alreadyRead - lastResLength);
  759. html->appendBytes(updated->bytes, updated->length);
  760. if(!rangeEnd) rangeEnd = html->length - 1;
  761. if(rangeStart > rangeEnd) {
  762. sendResponse(416, "Requested range not satisfiable", socketToClient);
  763. delete updated;
  764. delete dat;
  765. statusCode = 416;
  766. goto last;
  767. }
  768. statusCode = 206;
  769. log_printf(5,"cache hit; reconstructed data length:%ld\n",(long)html->length);
  770. }
  771. else {
  772. log_printf(5,"cache misshit?\n");
  773. sendResponse(416, "Requested range not satisfiable", socketToClient);
  774. delete updated;
  775. delete dat;
  776. statusCode = 416;
  777. goto last;
  778. }
  779. delete updated;
  780. }
  781. }
  782. else {
  783. log_printf(0,"curl error: %s (%s)\n",curl_easy_strerror(res),buf);
  784. curl_easy_cleanup(curl);
  785. }
  786. delete dat;
  787. if(html) goto resp;
  788. }
  789. }
  790. else {
  791. log_printf(5,"invalid cache contents\n");
  792. pthread_mutex_lock(mutex);
  793. BBS2chProxyThreadCache::iterator it = threadCache->find(threadKey);
  794. if(it != threadCache->end()) {
  795. threadCache->erase(it);
  796. }
  797. pthread_mutex_unlock(mutex);
  798. }
  799. }
  800. }
  801. {
  802. CURL *curl = curl_easy_init();
  803. if(curl) {
  804. CURLcode res;
  805. DataStorage *dat = new DataStorage();
  806. if(curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  807. curl_easy_setopt(curl, CURLOPT_URL, url);
  808. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  809. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  810. curl_easy_setopt(curl, CURLOPT_ENCODING, "");
  811. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  812. curl_easy_setopt(curl, CURLOPT_WRITEDATA, dat);
  813. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  814. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  815. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  816. if(force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  817. if(proxy_server) {
  818. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  819. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  820. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  821. }
  822. if(user_agent) {
  823. curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
  824. }
  825. else if(headers.has("User-Agent")) {
  826. curl_easy_setopt(curl, CURLOPT_USERAGENT, headers.get("User-Agent").c_str());
  827. }
  828. res = curl_easy_perform(curl);
  829. if(res != CURLE_OK) {
  830. log_printf(0,"curl error: %s (%s)\n",curl_easy_strerror(res),url);
  831. sendResponse(503, "Service Unavailable", socketToClient);
  832. curl_easy_cleanup(curl);
  833. delete dat;
  834. statusCode = 503;
  835. goto last;
  836. }
  837. curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &statusCode);
  838. curl_easy_cleanup(curl);
  839. if(statusCode == 200) {
  840. html = html2dat(dat, 1, &lastModified, false);
  841. }
  842. delete dat;
  843. }
  844. if(!html || !html->length) {
  845. sendResponse(503, "Service Unavailable", socketToClient);
  846. statusCode = 503;
  847. goto last;
  848. }
  849. if((rangeStart || rangeEnd) && html && html->length) {
  850. if(!rangeEnd) rangeEnd = html->length - 1;
  851. if(rangeStart < 0) rangeStart = html->length + rangeStart;
  852. if(rangeStart < html->length && rangeEnd < html->length && rangeStart <= rangeEnd) {
  853. statusCode = 206;
  854. }
  855. else {
  856. if(ifModifiedSince && lastModified && rangeStart == html->length) {
  857. struct tm time_ = {};
  858. gmtime_r(&lastModified,&time_);
  859. time_t tmp = mktime(&time_);
  860. if(ifModifiedSince >= tmp) {
  861. sendResponse(304, "Not Modified", socketToClient);
  862. log_printf(5,"not modified!\n");
  863. statusCode = 304;
  864. goto last;
  865. }
  866. }
  867. sendResponse(416, "Requested range not satisfiable", socketToClient);
  868. statusCode = 416;
  869. goto last;
  870. }
  871. }
  872. }
  873. resp:
  874. if(statusCode == 206) sendBasicHeaders(statusCode,"Partial Content",socketToClient);
  875. else sendBasicHeaders(statusCode,"OK",socketToClient);
  876. if(0 >= socketToClient->writeString("Content-Type: text/plain\r\n")) goto last;
  877. if(0 >= socketToClient->writeString("Accept-Ranges: bytes\r\n")) goto last;
  878. if(statusCode == 206) {
  879. std::ostringstream ss;
  880. ss << "Content-Range: bytes " << rangeStart << "-" << rangeEnd << "/" << html->length << "\r\n";
  881. if (0 >= socketToClient->writeString(ss.str())) goto last;
  882. //fprintf(stderr,"Content-Length: %ld\r\n",rangeEnd - rangeStart + 1);
  883. //fprintf(stderr,"Content-Range: bytes %ld-%ld/%ld\r\n",rangeStart,rangeEnd,(long)html->length);
  884. DataStorage *newHtml = new DataStorage();
  885. newHtml->appendBytes(html->bytes+rangeStart, rangeEnd - rangeStart + 1);
  886. delete html;
  887. html = newHtml;
  888. }
  889. {
  890. std::ostringstream ss;
  891. ss << "Content-Length: " << html->length << "\r\n";
  892. if(0 >= socketToClient->writeString(ss.str())) goto last;
  893. }
  894. if(lastModified) {
  895. struct tm time_ = {};
  896. char date[256];
  897. gmtime_r(&lastModified,&time_);
  898. strftime(date,256,httpTimestampFmt,&time_);
  899. std::string header = "Last-Modified: ";
  900. header += date;
  901. header += "\r\n";
  902. if(0 >= socketToClient->writeString(header)) goto last;
  903. //fprintf(stderr,"Last-Modified: %s\r\n",date);
  904. }
  905. if(0 > socketToClient->writeString("\r\n")) goto last;
  906. if(html && statusCode >= 200 && statusCode < 300 && strcasecmp(method, "HEAD")) {
  907. if(html->length > socketToClient->write(html->bytes, html->length)) goto last;
  908. }
  909. last:
  910. if(buf) free(buf);
  911. if(html) delete html;
  912. return statusCode;
  913. }
  914. DataStorage *BBS2chProxyConnection::html2dat_old(DataStorage *html, int startResNum, time_t *lastModified, bool useCache)
  915. {
  916. char *ptr = html->bytes;
  917. char *end = html->bytes + html->length - 1;
  918. DataStorage *txt = new DataStorage();
  919. int res = startResNum, i=0;
  920. char signature[32];
  921. char title[1024];
  922. int cachedSize = 0;
  923. bool bbspink = strstr(threadKey.c_str(),"bbspink.com") ? true : false;
  924. ptr = (char *)memmem_priv(ptr, end-ptr+1, "<title>", 7);
  925. if(!ptr) {
  926. delete txt;
  927. return NULL;
  928. }
  929. ptr += 7;
  930. while(1) {
  931. if(*ptr == '<') {
  932. if(!strncasecmp(ptr,"</title>",8)) {
  933. ptr += 8;
  934. break;
  935. }
  936. else title[i++] = *ptr++;
  937. }
  938. else title[i++] = *ptr++;
  939. }
  940. title[i] = 0;
  941. snprintf(signature,32,"<dt>%d ",res);
  942. ptr = (char *)memmem_priv(ptr, end-ptr+1, signature, strlen(signature));
  943. if(!ptr) {
  944. delete txt;
  945. return NULL;
  946. }
  947. unsigned char *buffer = (unsigned char *)malloc(65536+1024+1024+1024+2048);
  948. if(!buffer) {
  949. delete txt;
  950. return NULL;
  951. }
  952. unsigned char *body = buffer;
  953. char *mail = (char *)body + 65536;
  954. char *name = mail + 1024;
  955. char *date = name + 1024;
  956. char *encrypted = date + 1024;
  957. while(ptr < end) {
  958. //fprintf(stderr,"%s\n",signature);
  959. DataStorage *resData = new DataStorage();
  960. i=0;
  961. mail[0] = 0;
  962. ptr = strstr(ptr,signature);
  963. ptr += strlen(signature);
  964. while(*ptr != '<') ptr++;
  965. ptr++;
  966. const char *endStr;
  967. if(*ptr == 'a' || *ptr == 'A') {
  968. replay:
  969. // has mail
  970. while(*ptr != '"') ptr++;
  971. ptr++;
  972. if(!strncmp(ptr,"/cdn-cgi/l/email-protection#",28)) {
  973. ptr += 28;
  974. while(*ptr != '"' && *ptr != 'X') encrypted[i++] = *ptr++;
  975. encrypted[i] = 0;
  976. i = decryptMail((unsigned char *)mail,encrypted);
  977. int reconstruct_len = *ptr == 'X' ? i + 15 : i + 16;
  978. ptr -= reconstruct_len;
  979. char *start = ptr;
  980. memcpy(ptr, "<a href=\"mailto:", 16);
  981. ptr += 16;
  982. memcpy(ptr, mail, i);
  983. ptr = start;
  984. i=0;
  985. goto replay;
  986. }
  987. else {
  988. if(!strncmp(ptr,"mailto:",7)) ptr += 7;
  989. while(*ptr != '"') mail[i++] = *ptr++;
  990. mail[i] = 0;
  991. }
  992. endStr = "</a>";
  993. }
  994. else if(*ptr == 'b') {
  995. endStr = NULL;
  996. }
  997. else {
  998. endStr = "</font>";
  999. }
  1000. if(endStr) {
  1001. ptr = strstr(ptr,"<b>");
  1002. ptr += 3;
  1003. }
  1004. else {
  1005. ptr = strchr(ptr,'>');
  1006. ptr++;
  1007. }
  1008. i=0;
  1009. while(1) {
  1010. if(*ptr == '<') {
  1011. if(!strncasecmp(ptr,"</b>",4) && (!endStr || !strncasecmp(ptr+4,endStr,strlen(endStr)))) {
  1012. ptr += 4;
  1013. if(endStr) ptr += strlen(endStr);
  1014. break;
  1015. }
  1016. else if(!strncmp(ptr,"<span class=\"__cf_email__\"",26)) {
  1017. int j=0;
  1018. ptr = strstr(ptr,"data-cfemail=\"");
  1019. ptr += 14;
  1020. while(*ptr != '"') encrypted[j++] = *ptr++;
  1021. encrypted[j] = 0;
  1022. j = decryptMail((unsigned char *)name+i,encrypted);
  1023. i += j;
  1024. ptr = strstr(ptr,"</script>");
  1025. ptr += 9;
  1026. }
  1027. else name[i++] = *ptr++;
  1028. }
  1029. else name[i++] = *ptr++;
  1030. }
  1031. resData->appendBytes(name, i);
  1032. resData->appendBytes("<>", 2);
  1033. if(mail[0]) resData->appendBytes(mail ,strlen(mail));
  1034. resData->appendBytes("<>", 2);
  1035. ptr += 2;
  1036. i=0;
  1037. while(1) {
  1038. if(*ptr == '<') {
  1039. if(!strncasecmp(ptr,"<dd>",4)) {
  1040. ptr += 4;
  1041. break;
  1042. }
  1043. else if(!strncmp(ptr,"<a href=\"javascript:be(",23)) {
  1044. memcpy(date+i,"BE:",3);
  1045. ptr += 23;
  1046. i += 3;
  1047. while(*ptr != ')') date[i++] = *ptr++;
  1048. date[i++] = '-';
  1049. ptr = strchr(ptr,'?');
  1050. ptr++;
  1051. char *tmp = strstr(ptr,"</a>");
  1052. memcpy(date+i,ptr,tmp-ptr);
  1053. i += tmp-ptr;
  1054. ptr = tmp + 4;
  1055. }
  1056. else date[i++] = *ptr++;
  1057. }
  1058. else date[i++] = *ptr++;
  1059. }
  1060. resData->appendBytes(date ,i);
  1061. resData->appendBytes("<>", 2);
  1062. i=0;
  1063. while(1) {
  1064. if(*ptr == '<') {
  1065. if(!strncasecmp(ptr,"<br><br>\n",9)) {
  1066. ptr += 9;
  1067. break;
  1068. }
  1069. else if(!strncasecmp(ptr,"<dt>",4) || !strncasecmp(ptr,"</dl>",5)) {
  1070. while(i>0 &&body[i-1] == '\n') i--;
  1071. break;
  1072. }
  1073. else if(!strncmp(ptr,"<span class=\"__cf_email__\"",26) || !strncmp(ptr,"<a class=\"__cf_email__\"",23)) {
  1074. int j=0;
  1075. ptr = strstr(ptr,"data-cfemail=\"");
  1076. ptr += 14;
  1077. while(*ptr != '"') encrypted[j++] = *ptr++;
  1078. encrypted[j] = 0;
  1079. j = decryptMail(body+i,encrypted);
  1080. i += j;
  1081. ptr = strstr(ptr,"</script>");
  1082. ptr += 9;
  1083. }
  1084. else if(!strncmp(ptr,"<a href=\"http",13)) {
  1085. ptr = strchr(ptr,'>');
  1086. ptr++;
  1087. char *link = ptr;
  1088. ptr = strstr(link,"</a>");
  1089. memcpy(body+i,link,ptr-link);
  1090. i += ptr-link;
  1091. ptr += 4;
  1092. }
  1093. else if(!strncmp(ptr,"<img src=\"",10)) {
  1094. ptr += 10;
  1095. char *img = ptr;
  1096. ptr = strstr(img,"\">");
  1097. memcpy(body+i,img,ptr-img);
  1098. if(memmem_priv(img,ptr-img,"/img.2ch.net",12) || memmem_priv(img,ptr-img,"/img.5ch.net",12) || memmem_priv(img,ptr-img,"/o.8ch.net",10) || memmem_priv(img,ptr-img,"/o.5ch.net",10)) {
  1099. int length = ptr-img;
  1100. while(*img != '/') {
  1101. img++;
  1102. length--;
  1103. }
  1104. memcpy(body+i,"sssp:",5);
  1105. memcpy(body+i+5,img,length);
  1106. i += length + 5;
  1107. }
  1108. else i += ptr-img;
  1109. ptr += 2;
  1110. }
  1111. else if(!bbspink && !strncmp(ptr,"<br>",4)) {
  1112. if(i>5 && !strncmp((char *)body+i-5,"<br> ",5)) {
  1113. memcpy(body+i," <br>",5);
  1114. i += 5;
  1115. }
  1116. else {
  1117. memcpy(body+i,"<br>",4);
  1118. i += 4;
  1119. }
  1120. ptr += 4;
  1121. }
  1122. else body[i++] = *ptr++;
  1123. }
  1124. else if(!bbspink && *ptr == ' ') {
  1125. if(*(ptr+1) == ' ') ptr++;
  1126. else body[i++] = *ptr++;
  1127. }
  1128. else body[i++] = *ptr++;
  1129. }
  1130. resData->appendBytes(body ,i);
  1131. resData->appendBytes("<>", 2);
  1132. if(res == 1) resData->appendBytes(title ,strlen(title));
  1133. resData->appendBytes("\n" ,1);
  1134. if(useCache && res == startResNum) {
  1135. PBBS2chProxyThreadInfo info;
  1136. bool hit = false;
  1137. pthread_mutex_lock(mutex);
  1138. BBS2chProxyThreadCache::iterator it = threadCache->find(threadKey);
  1139. if(it != threadCache->end()) {
  1140. info = it->second;
  1141. threadCache->erase(it);
  1142. }
  1143. pthread_mutex_unlock(mutex);
  1144. if(info) {
  1145. log_printf(5,"cache hit");
  1146. if(info->cachedData->length == resData->length) {
  1147. log_printf(5,"... size match");
  1148. if(!memcmp(info->cachedData->bytes,resData->bytes,resData->length)) {
  1149. log_printf(5,"... content match");
  1150. hit = true;
  1151. cachedSize = info->cachedSize - resData->length;
  1152. }
  1153. }
  1154. log_printf(5,"\n");
  1155. }
  1156. if(!hit) {
  1157. delete resData;
  1158. free(buffer);
  1159. return NULL;
  1160. }
  1161. }
  1162. txt->appendBytes(resData->bytes, resData->length);
  1163. res++;
  1164. while(*ptr == '\n' || *ptr == '\r') ptr++;
  1165. snprintf(signature,32,"<dt>%d ",res);
  1166. if(!memmem_priv(ptr, end-ptr+1, signature, strlen(signature))) {
  1167. PBBS2chProxyThreadInfo info(new BBS2chProxyThreadInfo());
  1168. info->lastResNum = res-1;
  1169. info->cachedSize = txt->length+cachedSize;
  1170. info->cachedData = resData;
  1171. pthread_mutex_lock(mutex);
  1172. threadCache->insert(std::make_pair(threadKey,info));
  1173. pthread_mutex_unlock(mutex);
  1174. log_printf(5,"cached thread %s (%ld bytes)\n",threadKey.c_str(),(long)resData->length);
  1175. if(lastModified) {
  1176. *lastModified = 0;
  1177. char formattedDate[256];
  1178. char *ptr;
  1179. ptr = date;
  1180. int year = strtol(ptr,&ptr,10);
  1181. if(*ptr != '/') break;
  1182. ptr++;
  1183. int month = strtol(ptr,&ptr,10);
  1184. if(*ptr != '/') break;
  1185. ptr++;
  1186. int day = strtol(ptr,&ptr,10);
  1187. if(!*ptr) break;
  1188. while(*ptr != ' ' && *ptr != 0) ptr++;
  1189. if(!*ptr) break;
  1190. ptr++;
  1191. int hour = strtol(ptr,&ptr,10);
  1192. if(*ptr != ':') break;
  1193. ptr++;
  1194. int minutes = strtol(ptr,&ptr,10);
  1195. if(*ptr != ':') break;
  1196. ptr++;
  1197. int seconds = strtol(ptr,&ptr,10);
  1198. if(!(month>0 && month<13) || !(day>0 && day<32)) break;
  1199. if(year < 100) year += 2000;
  1200. snprintf(formattedDate,256,"%d/%d/%d %02d:%02d:%02d JST",year,month,day,hour,minutes,seconds);
  1201. //fprintf(stderr,"%s\n",formattedDate);
  1202. struct tm time = {};
  1203. strptime(formattedDate,threadTimestampFmt,&time);
  1204. *lastModified = mktime(&time);
  1205. //gmtime_r(lastModified,&time);
  1206. //strftime(formattedDate,256,httpTimestampFmt,&time);
  1207. //fprintf(stderr,"%s\n",formattedDate);
  1208. }
  1209. //fprintf(stderr,"not found,%ld\n",end-ptr+1);
  1210. break;
  1211. }
  1212. delete resData;
  1213. }
  1214. free(buffer);
  1215. return txt;
  1216. }
  1217. DataStorage *BBS2chProxyConnection::html2dat(DataStorage *html, int startResNum, time_t *lastModified, bool useCache)
  1218. {
  1219. char *ptr = html->bytes;
  1220. char *end = html->bytes + html->length - 1;
  1221. DataStorage *txt = new DataStorage();
  1222. int res = startResNum, i=0;
  1223. char signature[64];
  1224. char title[1024];
  1225. int cachedSize = 0;
  1226. char signatureTag[32];
  1227. char closeTag[32];
  1228. int closeTagLen;
  1229. ptr = (char *)memmem_priv(ptr, end-ptr+1, "<h1 class=\"title\">", 18);
  1230. if(!ptr) {
  1231. delete txt;
  1232. return html2dat_old(html, startResNum, lastModified, useCache);
  1233. }
  1234. else {
  1235. char *ptr2 = (char *)memmem_priv(ptr, end-ptr+1, " class=\"post\"", 13);
  1236. if(ptr2) {
  1237. char *tmp = ptr2;
  1238. *ptr2 = 0;
  1239. while(*ptr2 != '<') ptr2--;
  1240. strcpy(signatureTag, ptr2);
  1241. *tmp = ' ';
  1242. }
  1243. else {
  1244. delete txt;
  1245. return NULL;
  1246. }
  1247. /*char *ptr2 = (char *)memmem_priv(ptr, end-ptr+1, "<dl class=\"post\"", 16);
  1248. if(ptr2) {
  1249. delete txt;
  1250. return html2dat_pink(html, startResNum, lastModified, useCache);
  1251. }*/
  1252. }
  1253. ptr += 18;
  1254. while(1) {
  1255. if(*ptr == '<') {
  1256. if(!strncasecmp(ptr,"</h1>",5)) {
  1257. ptr += 5;
  1258. break;
  1259. }
  1260. else title[i++] = *ptr++;
  1261. }
  1262. else if(*ptr == '\n') break;
  1263. else title[i++] = *ptr++;
  1264. }
  1265. title[i] = 0;
  1266. snprintf(signature,32,"%s class=\"post\" id=\"%d\"",signatureTag,res);
  1267. ptr = (char *)memmem_priv(ptr, end-ptr+1, signature, strlen(signature));
  1268. if(!ptr) {
  1269. delete txt;
  1270. return NULL;
  1271. }
  1272. unsigned char *buffer = (unsigned char *)malloc(65536+1024+1024+1024+2048);
  1273. if(!buffer) {
  1274. delete txt;
  1275. return NULL;
  1276. }
  1277. unsigned char *body = buffer;
  1278. char *mail = (char *)body + 65536;
  1279. char *name = mail + 1024;
  1280. char *date = name + 1024;
  1281. char *encrypted = date + 1024;
  1282. while(ptr < end) {
  1283. //fprintf(stderr,"%s\n",signature);
  1284. DataStorage *resData = new DataStorage();
  1285. i=0;
  1286. mail[0] = 0;
  1287. ptr = strstr(ptr," class=\"name\"><b>");
  1288. if(ptr) {
  1289. char *tmp = ptr;
  1290. *ptr = 0;
  1291. while(*ptr != '<') ptr--;
  1292. snprintf(closeTag,32,"</%s>",ptr+1);
  1293. closeTagLen = strlen(closeTag);
  1294. ptr = tmp + 17;
  1295. }
  1296. else {
  1297. delete resData;
  1298. break;
  1299. }
  1300. char endStr[64];
  1301. if(!strncmp(ptr,"<a href=\"mailto:",16)) {
  1302. replay:
  1303. // has mail
  1304. while(*ptr != '"') ptr++;
  1305. ptr++;
  1306. if(!strncmp(ptr,"/cdn-cgi/l/email-protection#",28)) {
  1307. ptr += 28;
  1308. while(*ptr != '"' && *ptr != 'X') encrypted[i++] = *ptr++;
  1309. encrypted[i] = 0;
  1310. i = decryptMail((unsigned char *)mail,encrypted);
  1311. int reconstruct_len = *ptr == 'X' ? i + 15 : i + 16;
  1312. ptr -= reconstruct_len;
  1313. char *start = ptr;
  1314. memcpy(ptr, "<a href=\"mailto:", 16);
  1315. ptr += 16;
  1316. memcpy(ptr, mail, i);
  1317. ptr = start;
  1318. i=0;
  1319. goto replay;
  1320. }
  1321. else {
  1322. if(!strncmp(ptr,"mailto:",7)) ptr += 7;
  1323. while(1) {
  1324. if(*ptr == '<' && !strncmp(ptr,"<a href=\"",9)) {
  1325. ptr = strchr(ptr,'>');
  1326. ptr++;
  1327. char *link = ptr;
  1328. ptr = strstr(link,"</a>");
  1329. memcpy(mail+i,link,ptr-link);
  1330. i += ptr-link;
  1331. ptr += 4;
  1332. }
  1333. else if(*ptr == '"') break;
  1334. else mail[i++] = *ptr++;
  1335. }
  1336. //while(*ptr != '"') mail[i++] = *ptr++;
  1337. mail[i] = 0;
  1338. }
  1339. snprintf(endStr,64,"</a></b>%s",closeTag);
  1340. while(*ptr != '>') ptr++;
  1341. ptr++;
  1342. }
  1343. /* we do not have to handle this special case because read.cgi on bbspink doesn't
  1344. emit font tags anymore and it conflicts with text decorations using "melon point" */
  1345. /*else if(!strncmp(ptr,"<font",5)) {
  1346. snprintf(endStr,64,"</font></b>%s",closeTag);
  1347. while(*ptr != '>') ptr++;
  1348. ptr++;
  1349. }*/
  1350. else {
  1351. snprintf(endStr,64,"</b>%s",closeTag);
  1352. }
  1353. i=0;
  1354. while(1) {
  1355. if(*ptr == '<') {
  1356. if(!strncmp(ptr,endStr,strlen(endStr))) {
  1357. ptr += strlen(endStr);
  1358. break;
  1359. }
  1360. else if(!strncmp(ptr,"<span class=\"__cf_email__\"",26)) {
  1361. int j=0;
  1362. ptr = strstr(ptr,"data-cfemail=\"");
  1363. ptr += 14;
  1364. while(*ptr != '"') encrypted[j++] = *ptr++;
  1365. encrypted[j] = 0;
  1366. j = decryptMail((unsigned char *)name+i,encrypted);
  1367. i += j;
  1368. ptr = strstr(ptr,"</script>");
  1369. ptr += 9;
  1370. }
  1371. else if(!strncmp(ptr,"<a href=\"",9)) {
  1372. ptr = strchr(ptr,'>');
  1373. ptr++;
  1374. char *link = ptr;
  1375. ptr = strstr(link,"</a>");
  1376. memcpy(name+i,link,ptr-link);
  1377. i += ptr-link;
  1378. ptr += 4;
  1379. }
  1380. else name[i++] = *ptr++;
  1381. }
  1382. else name[i++] = *ptr++;
  1383. }
  1384. resData->appendBytes(name, i);
  1385. resData->appendBytes("<>", 2);
  1386. if(mail[0]) resData->appendBytes(mail ,strlen(mail));
  1387. resData->appendBytes("<>", 2);
  1388. ptr = strstr(ptr," class=\"date\">");
  1389. if(ptr) {
  1390. char *tmp = ptr;
  1391. *ptr = 0;
  1392. while(*ptr != '<') ptr--;
  1393. snprintf(closeTag,32,"</%s>",ptr+1);
  1394. closeTagLen = strlen(closeTag);
  1395. ptr = tmp + 14;
  1396. }
  1397. else {
  1398. delete resData;
  1399. break;
  1400. }
  1401. i=0;
  1402. while(1) {
  1403. if(*ptr == '<') {
  1404. if(!strncasecmp(ptr,closeTag,closeTagLen)) {
  1405. ptr += closeTagLen;
  1406. break;
  1407. }
  1408. else date[i++] = *ptr++;
  1409. }
  1410. else date[i++] = *ptr++;
  1411. }
  1412. if(!strncmp(ptr,"<div class=\"uid",15) || !strncmp(ptr,"<span class=\"uid",16)) {
  1413. char *tmp = ptr+1;
  1414. while(*ptr != ' ') ptr++;
  1415. *ptr = 0;
  1416. snprintf(closeTag,32,"</%s>",tmp);
  1417. closeTagLen = strlen(closeTag);
  1418. ptr += 11;
  1419. while(*ptr != '>') ptr++;
  1420. ptr++;
  1421. date[i++] = ' ';
  1422. while(1) {
  1423. if(*ptr == '<') {
  1424. if(!strncasecmp(ptr,closeTag,closeTagLen)) {
  1425. ptr += closeTagLen;
  1426. break;
  1427. }
  1428. else date[i++] = *ptr++;
  1429. }
  1430. else date[i++] = *ptr++;
  1431. }
  1432. }
  1433. if(!strncmp(ptr,"<div class=\"be",14) || !strncmp(ptr,"<span class=\"be",15)) {
  1434. ptr += 14;
  1435. while(*ptr != '>') ptr++;
  1436. ptr++;
  1437. if(!strncmp(ptr,"<a href=\"",9)) {
  1438. ptr += 9;
  1439. while(*ptr != '/' && *ptr != '"') ptr++;
  1440. if(*ptr == '/' && (!strncmp(ptr,"//be.2ch.net/user/",18) || !strncmp(ptr,"//be.5ch.net/user/",18))) {
  1441. memcpy(date+i," BE:",4);
  1442. i += 4;
  1443. ptr += 18;
  1444. while(*ptr != '"') date[i++] = *ptr++;
  1445. date[i++] = '-';
  1446. ptr = strchr(ptr,'?');
  1447. ptr++;
  1448. char *tmp = strstr(ptr,"</a>");
  1449. memcpy(date+i,ptr,tmp-ptr);
  1450. i += tmp-ptr;
  1451. ptr = tmp + 4;
  1452. }
  1453. }
  1454. }
  1455. resData->appendBytes(date ,i);
  1456. resData->appendBytes("<>", 2);
  1457. if(!strcmp(signatureTag,"<div")) {
  1458. ptr = strstr(ptr,"<div class=\"message\">");
  1459. if(!ptr) {
  1460. delete resData;
  1461. break;
  1462. }
  1463. else {
  1464. ptr += 21;
  1465. if(!strncasecmp(ptr,"<span class=\"escaped\">",22)) {
  1466. if(!strncasecmp(ptr+22,"<span class=\"AA\">",17)) {
  1467. strcpy(closeTag,"</span></span></div>");
  1468. closeTagLen = 20;
  1469. ptr += 22+17;
  1470. }
  1471. else {
  1472. strcpy(closeTag,"</span></div>");
  1473. closeTagLen = 13;
  1474. ptr += 22;
  1475. }
  1476. }
  1477. else {
  1478. strcpy(closeTag,"</div>");
  1479. closeTagLen = 6;
  1480. }
  1481. }
  1482. }
  1483. else {
  1484. ptr = strstr(ptr,"<dd class=\"thread_in\">");
  1485. if(!ptr) {
  1486. delete resData;
  1487. break;
  1488. }
  1489. strcpy(closeTag,"</dd>");
  1490. closeTagLen = 5;
  1491. ptr += 22;
  1492. }
  1493. i=0;
  1494. while(1) {
  1495. if(*ptr == '<') {
  1496. if(!strncasecmp(ptr,closeTag,closeTagLen)) {
  1497. ptr += closeTagLen;
  1498. break;
  1499. }
  1500. else if(!strncmp(ptr,"<span class=\"__cf_email__\"",26) || !strncmp(ptr,"<a class=\"__cf_email__\"",23)) {
  1501. int j=0;
  1502. ptr = strstr(ptr,"data-cfemail=\"");
  1503. ptr += 14;
  1504. while(*ptr != '"') encrypted[j++] = *ptr++;
  1505. encrypted[j] = 0;
  1506. j = decryptMail(body+i,encrypted);
  1507. i += j;
  1508. ptr = strstr(ptr,"</script>");
  1509. ptr += 9;
  1510. }
  1511. else if(!strncmp(ptr,"<a ",3)) {
  1512. char *tmp = strchr(ptr,'>');
  1513. char *href = (char *)memmem_priv(ptr,tmp-ptr,"href=\"",6);
  1514. char *link = tmp+1;
  1515. if(href && !strncmp(link,"&gt;&gt;",8) && memmem_priv(href,link-href,"test/read.cgi/",14)) {
  1516. while(ptr < link) {
  1517. if(!strncmp(ptr," class=\"",8)) {
  1518. ptr += 8;
  1519. while(*ptr != '"' && *ptr != '>') ptr++;
  1520. if(*ptr == '"') ptr++;
  1521. }
  1522. else body[i++] = *ptr++;
  1523. }
  1524. }
  1525. else {
  1526. ptr = strstr(link,"</a>");
  1527. memcpy(body+i,link,ptr-link);
  1528. i += ptr-link;
  1529. ptr += 4;
  1530. }
  1531. }
  1532. else if(!strncmp(ptr,"<img src=\"",10)) {
  1533. ptr += 10;
  1534. char *img = ptr;
  1535. ptr = strstr(img,"\">");
  1536. memcpy(body+i,img,ptr-img);
  1537. if(memmem_priv(img,ptr-img,"/img.2ch.net",12) || memmem_priv(img,ptr-img,"/img.5ch.net",12) || memmem_priv(img,ptr-img,"/o.8ch.net",10) || memmem_priv(img,ptr-img,"/o.5ch.net",10)) {
  1538. int length = ptr-img;
  1539. while(*img != '/') {
  1540. img++;
  1541. length--;
  1542. }
  1543. memcpy(body+i,"sssp:",5);
  1544. memcpy(body+i+5,img,length);
  1545. i += length + 5;
  1546. }
  1547. else i += ptr-img;
  1548. ptr += 2;
  1549. }
  1550. else if(!strncmp(ptr,"<br>",4)) {
  1551. if(i>5 && !strncmp((char *)body+i-5,"<br> ",5)) {
  1552. memcpy(body+i," <br>",5);
  1553. i += 5;
  1554. }
  1555. else {
  1556. memcpy(body+i,"<br>",4);
  1557. i += 4;
  1558. }
  1559. ptr += 4;
  1560. }
  1561. else body[i++] = *ptr++;
  1562. }
  1563. else body[i++] = *ptr++;
  1564. }
  1565. resData->appendBytes(body ,i);
  1566. resData->appendBytes("<>", 2);
  1567. if(res == 1) resData->appendBytes(title ,strlen(title));
  1568. resData->appendBytes("\n" ,1);
  1569. if(useCache && res == startResNum) {
  1570. PBBS2chProxyThreadInfo info;
  1571. bool hit = false;
  1572. pthread_mutex_lock(mutex);
  1573. BBS2chProxyThreadCache::iterator it = threadCache->find(threadKey);
  1574. if(it != threadCache->end()) {
  1575. info = it->second;
  1576. threadCache->erase(it);
  1577. }
  1578. pthread_mutex_unlock(mutex);
  1579. if(info) {
  1580. log_printf(5,"cache hit");
  1581. if(info->cachedData->length == resData->length) {
  1582. log_printf(5,"... size match");
  1583. if(!memcmp(info->cachedData->bytes,resData->bytes,resData->length)) {
  1584. log_printf(5,"... content match");
  1585. hit = true;
  1586. cachedSize = info->cachedSize - resData->length;
  1587. }
  1588. }
  1589. log_printf(5,"\n");
  1590. }
  1591. if(!hit) {
  1592. delete resData;
  1593. free(buffer);
  1594. return NULL;
  1595. }
  1596. }
  1597. txt->appendBytes(resData->bytes, resData->length);
  1598. res++;
  1599. while(*ptr == '\n' || *ptr == '\r') ptr++;
  1600. snprintf(signature,64,"%s class=\"post\" id=\"",signatureTag);
  1601. ptr = (char *)memmem_priv(ptr, end-ptr+1, signature, strlen(signature));
  1602. if(ptr) {
  1603. int next = atoi(ptr+strlen(signature));
  1604. if(next >= res) {
  1605. while(next > res) {
  1606. txt->appendBytes("broken<><>broken<> broken <>\n", 29);
  1607. res++;
  1608. }
  1609. }
  1610. else ptr = NULL;
  1611. }
  1612. if(!ptr) {
  1613. PBBS2chProxyThreadInfo info(new BBS2chProxyThreadInfo());
  1614. info->lastResNum = res-1;
  1615. info->cachedSize = txt->length+cachedSize;
  1616. info->cachedData = resData;
  1617. pthread_mutex_lock(mutex);
  1618. threadCache->insert(std::make_pair(threadKey,info));
  1619. pthread_mutex_unlock(mutex);
  1620. log_printf(5,"cached thread %s (%ld bytes)\n",threadKey.c_str(),(long)resData->length);
  1621. if(lastModified) {
  1622. *lastModified = 0;
  1623. char formattedDate[256];
  1624. char *ptr;
  1625. ptr = date;
  1626. int year = strtol(ptr,&ptr,10);
  1627. if(*ptr != '/') break;
  1628. ptr++;
  1629. int month = strtol(ptr,&ptr,10);
  1630. if(*ptr != '/') break;
  1631. ptr++;
  1632. int day = strtol(ptr,&ptr,10);
  1633. if(!*ptr) break;
  1634. while(*ptr != ' ' && *ptr != 0) ptr++;
  1635. if(!*ptr) break;
  1636. ptr++;
  1637. int hour = strtol(ptr,&ptr,10);
  1638. if(*ptr != ':') break;
  1639. ptr++;
  1640. int minutes = strtol(ptr,&ptr,10);
  1641. if(*ptr != ':') break;
  1642. ptr++;
  1643. int seconds = strtol(ptr,&ptr,10);
  1644. if(!(month>0 && month<13) || !(day>0 && day<32)) break;
  1645. if(year < 100) year += 2000;
  1646. snprintf(formattedDate,256,"%d/%d/%d %02d:%02d:%02d JST",year,month,day,hour,minutes,seconds);
  1647. //fprintf(stderr,"%s\n",formattedDate);
  1648. struct tm time = {};
  1649. strptime(formattedDate,threadTimestampFmt,&time);
  1650. *lastModified = mktime(&time);
  1651. //gmtime_r(lastModified,&time);
  1652. //strftime(formattedDate,256,httpTimestampFmt,&time);
  1653. //fprintf(stderr,"%s\n",formattedDate);
  1654. }
  1655. //fprintf(stderr,"not found,%ld\n",end-ptr+1);
  1656. break;
  1657. }
  1658. delete resData;
  1659. }
  1660. free(buffer);
  1661. return txt;
  1662. }
  1663. int BBS2chProxyConnection::datProxyAPI(const char *url, const char *method)
  1664. {
  1665. long statusCode = 0;
  1666. std::string postBody = auth->requestBodyForURL(url);
  1667. if(postBody.empty()) {
  1668. sendResponse(401, "Unauthorized", socketToClient);
  1669. return 401;
  1670. }
  1671. CURL *curl = curl_easy_init();
  1672. char *buf = (char *)malloc(16384);
  1673. if(curl) {
  1674. CURLcode res;
  1675. struct curl_slist *headersForCurl = NULL;
  1676. DataStorage *receivedHeader = new DataStorage();
  1677. DataStorage *receivedBody = new DataStorage();
  1678. BBS2chProxyHttpHeaders headers;
  1679. while(socketToClient->readLine(buf, 16384)) {
  1680. //fprintf(stderr,"%s",buf);
  1681. if(!strcmp("\r\n",buf)) break;
  1682. headers.add(buf);
  1683. }
  1684. headersForCurl = headers.appendToCurlSlist(headersForCurl, "Range");
  1685. headersForCurl = headers.appendToCurlSlist(headersForCurl, "If-Modified-Since");
  1686. headersForCurl = headers.appendToCurlSlist(headersForCurl, "Accept-Encoding");
  1687. if(x_2ch_ua_dat) headersForCurl = curl_slist_append(headersForCurl, x_2ch_ua_dat);
  1688. if(curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  1689. curl_easy_setopt(curl, CURLOPT_URL, url);
  1690. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
  1691. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  1692. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  1693. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  1694. curl_easy_setopt(curl, CURLOPT_WRITEDATA, receivedBody);
  1695. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
  1696. curl_easy_setopt(curl, CURLOPT_HEADERDATA, receivedHeader);
  1697. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  1698. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  1699. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  1700. if(force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  1701. if(proxy_server) {
  1702. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  1703. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  1704. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  1705. }
  1706. curl_easy_setopt(curl, CURLOPT_USERAGENT, api_ua_dat?api_ua_dat:"");
  1707. curl_easy_setopt(curl, CURLOPT_POST, 1L);
  1708. #if LIBCURL_VERSION_NUM >= 0x071101
  1709. curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, postBody.c_str());
  1710. #else
  1711. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postBody.c_str());
  1712. #endif
  1713. //return;
  1714. res = curl_easy_perform(curl);
  1715. if(res == CURLE_OK) {
  1716. curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &statusCode);
  1717. receivedHeader->appendBytes("",1);
  1718. char *ptr = strstr(receivedHeader->bytes,"\r\n\r\n");
  1719. *(ptr+4) = 0;
  1720. int threadStatus = 0;
  1721. for(char *ptr2 = receivedHeader->bytes; ptr2<ptr;) {
  1722. if(!strncasecmp(ptr2, "Thread-Status:", strlen("Thread-Status:"))) {
  1723. ptr2 += strlen("Thread-Status:");
  1724. while(*ptr2 == ' ') ptr2++;
  1725. threadStatus = atoi(ptr2);
  1726. break;
  1727. }
  1728. ptr2 = strstr(ptr2, "\r\n") + 2;
  1729. }
  1730. if(threadStatus == 1) {
  1731. if(ptr+4-receivedHeader->bytes > socketToClient->write(receivedHeader->bytes, ptr+4-receivedHeader->bytes)) goto last;
  1732. if(receivedBody->length > socketToClient->write(receivedBody->bytes, receivedBody->length)) goto last;
  1733. goto last;
  1734. }
  1735. else if(threadStatus == 8) {
  1736. sendBasicHeaders(302, "Found", socketToClient);
  1737. if(0 >= socketToClient->writeString("Location: http://www2.2ch.net/live.html\r\n")) goto last;
  1738. if(0 >= socketToClient->writeString("\r\n")) goto last;
  1739. statusCode = 302;
  1740. goto last;
  1741. }
  1742. else {
  1743. if (statusCode < 400) {
  1744. sendResponse(401, "Unauthorized", socketToClient);
  1745. statusCode = 401;
  1746. }
  1747. else {
  1748. sendResponse(503, "Service Unavailable", socketToClient);
  1749. statusCode = 503;
  1750. }
  1751. receivedBody->appendBytes("",1);
  1752. if(!strncasecmp(receivedBody->bytes,"ng (",4)) {
  1753. log_printf(0,"API gateway returned error: %s\n",receivedBody->bytes);
  1754. }
  1755. }
  1756. //fprintf(stderr,"%ld\n",statusCode);
  1757. }
  1758. else {
  1759. log_printf(0,"curl error: %s\n",curl_easy_strerror(res));
  1760. sendResponse(503, "Service Unavailable", socketToClient);
  1761. statusCode = 503;
  1762. }
  1763. last:
  1764. curl_easy_cleanup(curl);
  1765. curl_slist_free_all(headersForCurl);
  1766. delete receivedBody;
  1767. delete receivedHeader;
  1768. }
  1769. free(buf);
  1770. return statusCode;
  1771. }
  1772. int BBS2chProxyConnection::bbsmenuProxy(const char *url, const char *method)
  1773. {
  1774. long statusCode = 0;
  1775. DataStorage *dat = new DataStorage();
  1776. DataStorage *outHTML = new DataStorage();
  1777. char *buf = (char *)malloc(16384);
  1778. CURL *curl = NULL;
  1779. BBS2chProxyHttpHeaders headers;
  1780. if(!buf) goto last;
  1781. while(socketToClient->readLine(buf, 16384)) {
  1782. //fprintf(stderr,"%s",buf);
  1783. if(!strcmp("\r\n",buf)) break;
  1784. headers.add(buf);
  1785. }
  1786. curl = curl_easy_init();
  1787. if(curl) {
  1788. CURLcode res;
  1789. if(curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  1790. curl_easy_setopt(curl, CURLOPT_URL, url);
  1791. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  1792. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  1793. curl_easy_setopt(curl, CURLOPT_ENCODING, "");
  1794. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
  1795. curl_easy_setopt(curl, CURLOPT_WRITEDATA, dat);
  1796. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  1797. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  1798. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  1799. if(force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  1800. if(proxy_server) {
  1801. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  1802. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  1803. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  1804. }
  1805. if(user_agent) {
  1806. curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
  1807. }
  1808. else if(headers.has("User-Agent")) {
  1809. curl_easy_setopt(curl, CURLOPT_USERAGENT, headers.get("User-Agent").c_str());
  1810. }
  1811. res = curl_easy_perform(curl);
  1812. if(res == CURLE_OK) {
  1813. curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &statusCode);
  1814. if(statusCode == 200 && dat->length) {
  1815. dat->appendBytes("",1);
  1816. dat->length--;
  1817. char *ptr = dat->bytes;
  1818. while(*ptr) {
  1819. if(!strncasecmp(ptr,"<a href=",8)) {
  1820. char *start = ptr;
  1821. char *end = strchr(ptr+8,'>');
  1822. ptr = strstr(ptr+8,"://");
  1823. if(ptr && ptr < end) {
  1824. char *protocol = ptr;
  1825. char *end2 = strchr(ptr+3,'/');
  1826. ptr = strstr(protocol+3,"5ch.net");
  1827. if(ptr && ptr < end2 && (*(ptr-1)=='.' || *(ptr-1)=='/')) {
  1828. memcpy(ptr,"2ch",3);
  1829. if(*(protocol-1) == 's') outHTML->appendBytes(start, protocol-start-1);
  1830. else outHTML->appendBytes(start, protocol-start);
  1831. outHTML->appendBytes(protocol, end-protocol);
  1832. ptr = end;
  1833. continue;
  1834. }
  1835. ptr = strstr(protocol+3,"bbspink.com");
  1836. if(ptr && ptr < end2 && (*(ptr-1)=='.' || *(ptr-1)=='/')) {
  1837. if(*(protocol-1) == 's') outHTML->appendBytes(start, protocol-start-1);
  1838. else outHTML->appendBytes(start, protocol-start);
  1839. outHTML->appendBytes(protocol, end-protocol);
  1840. ptr = end;
  1841. continue;
  1842. }
  1843. }
  1844. ptr = start;
  1845. }
  1846. outHTML->appendBytes(ptr++, 1);
  1847. }
  1848. }
  1849. }
  1850. else {
  1851. log_printf(0,"curl error: %s (%s)\n",curl_easy_strerror(res),buf);
  1852. statusCode = 503;
  1853. }
  1854. }
  1855. if(statusCode == 200) {
  1856. std::ostringstream ss;
  1857. ss << "Content-Length: " << outHTML->length << "\r\n";
  1858. sendBasicHeaders(statusCode,"OK",socketToClient);
  1859. if(0 >= socketToClient->writeString("Content-Type: text/html\r\n")) goto last;
  1860. if(0 >= socketToClient->writeString(ss.str())) goto last;
  1861. if(0 >= socketToClient->writeString("\r\n")) goto last;
  1862. if(strcasecmp(method, "HEAD")) {
  1863. if(outHTML->length > socketToClient->write(outHTML->bytes, outHTML->length)) goto last;
  1864. }
  1865. }
  1866. else {
  1867. sendResponse(503, "Service Unavailable", socketToClient);
  1868. statusCode = 503;
  1869. }
  1870. last:
  1871. if(curl) curl_easy_cleanup(curl);
  1872. if(buf) free(buf);
  1873. if(dat) delete dat;
  1874. if(outHTML) delete outHTML;
  1875. return statusCode;
  1876. }
  1877. int BBS2chProxyConnection::bbsCgiProxy(const char *url, const char *protocol, BBS2chProxyHttpHeaders *requestHeaders, const char *requestBody)
  1878. {
  1879. CURL *curl = curl_easy_init();
  1880. long statusCode = 0;
  1881. std::string hostStr = requestHeaders->get("Host");
  1882. std::string boardStr;
  1883. std::string threadStr;
  1884. requestHeaders->remove("Host");
  1885. if (user_agent) requestHeaders->set("User-Agent", user_agent);
  1886. if (requestBody && (lua_script || !bbscgi_headers.empty() || !bbscgi_postorder.empty())) {
  1887. std::map<std::string, std::string> fields;
  1888. const char *ptr = requestBody;
  1889. size_t bodyLength = 0;
  1890. while (1) {
  1891. const char *tmp = ptr;
  1892. while (*tmp != '=' && *tmp != 0) tmp++;
  1893. if (*tmp == 0) {
  1894. bodyLength = tmp - requestBody;
  1895. break;
  1896. }
  1897. std::string key(ptr, tmp-ptr);
  1898. tmp++;
  1899. ptr = tmp;
  1900. while (*tmp != '&' && *tmp != 0) tmp++;
  1901. std::string value(ptr, tmp-ptr);
  1902. fields.insert(std::make_pair(key, value));
  1903. if (*tmp == 0) {
  1904. bodyLength = tmp - requestBody;
  1905. break;
  1906. }
  1907. ptr = tmp + 1;
  1908. }
  1909. if (fields.count("bbs")) boardStr = fields["bbs"];
  1910. if (fields.count("key")) threadStr = fields["key"];
  1911. if (!bbscgi_postorder.empty()) {
  1912. std::string newBody;
  1913. for (std::vector<std::string>::iterator it = bbscgi_postorder.begin(); it != bbscgi_postorder.end(); it++) {
  1914. std::string &name = *it;
  1915. if (fields.count(name)) {
  1916. if (!newBody.empty()) newBody.append("&");
  1917. newBody.append(name);
  1918. newBody.append("=");
  1919. newBody.append(fields[name]);
  1920. fields.erase(name);
  1921. }
  1922. }
  1923. for (std::map<std::string, std::string>::iterator it = fields.begin(); it != fields.end(); it++) {
  1924. if (!newBody.empty()) newBody.append("&");
  1925. newBody.append(it->first);
  1926. newBody.append("=");
  1927. newBody.append(it->second);
  1928. }
  1929. if (bodyLength == newBody.length()) {
  1930. strcpy((char *)requestBody, newBody.c_str());
  1931. log_printf(1, "Reordered request body is: %s\n", requestBody);
  1932. }
  1933. else {
  1934. log_printf(0, "Error occured while reordering the request body - skipping\n");
  1935. }
  1936. }
  1937. }
  1938. if (!bbscgi_headers.empty()) {
  1939. for (std::map<std::string, std::string>::iterator it = bbscgi_headers.begin(); it!=bbscgi_headers.end(); it++) {
  1940. /* we cannot use a reference here, because the original string shouldn't be replaced */
  1941. std::string value = it->second;
  1942. if (!hostStr.empty()) {
  1943. std::string::size_type pos = value.find("%HOST%");
  1944. while (pos != std::string::npos) {
  1945. value.replace(pos, 6, hostStr);
  1946. pos = value.find("%HOST%", pos+hostStr.length());
  1947. }
  1948. }
  1949. if (!boardStr.empty()) {
  1950. std::string::size_type pos = value.find("%BOARD%");
  1951. while (pos != std::string::npos) {
  1952. value.replace(pos, 7, boardStr);
  1953. pos = value.find("%BOARD%", pos+boardStr.length());
  1954. }
  1955. }
  1956. if (!threadStr.empty()) {
  1957. std::string::size_type pos = value.find("%THREAD%");
  1958. while (pos != std::string::npos) {
  1959. value.replace(pos, 8, threadStr);
  1960. pos = value.find("%THREAD%", pos+threadStr.length());
  1961. }
  1962. }
  1963. requestHeaders->set(it->first, value);
  1964. log_printf(1, "Appended custom header \"%s: %s\"\n", it->first.c_str(), value.c_str());
  1965. }
  1966. }
  1967. for (int run=0; run<2; run++) {
  1968. BBS2chProxyHttpHeaders *_headers = requestHeaders;
  1969. curl_slist *headersForCurl = NULL;
  1970. char *_body = (char *)requestBody;
  1971. status = 0;
  1972. monaKeyForRequest = "";
  1973. #ifdef USE_LUA
  1974. if (lua_script) {
  1975. lua_State* l = luaL_newstate();
  1976. luaL_openlibs(l);
  1977. if (luaL_loadfile(l, lua_script) != LUA_OK) {
  1978. log_printf(0, "Lua: Failed to open script %s:\n %s\n", lua_script, lua_tostring(l, -1));
  1979. goto lua_end;
  1980. }
  1981. lua_newtable(l);
  1982. lua_pushcfunction(l, lua_hmacSHA256);
  1983. lua_setfield(l, -2, "hmacSHA256");
  1984. lua_pushcfunction(l, lua_decodeURIComponent);
  1985. lua_setfield(l, -2, "decodeURIComponent");
  1986. lua_pushcfunction(l, lua_encodeURIComponent);
  1987. lua_setfield(l, -2, "encodeURIComponent");
  1988. lua_pushcfunction(l, lua_convertShiftJISToUTF8);
  1989. lua_setfield(l, -2, "convertShiftJISToUTF8");
  1990. lua_pushcfunction(l, lua_isExpiredKey);
  1991. lua_setfield(l, -2, "isExpiredKey");
  1992. lua_pushstring(l, getMonaKey().c_str());
  1993. lua_setfield(l, -2, "monaKey");
  1994. lua_setglobal(l, "proxy2ch");
  1995. BBS2chProxyHttpHeaders::getClassDefinitionForLua(l);
  1996. lua_setglobal(l, "HttpHeaders");
  1997. if (lua_pcall(l, 0, 0, 0) != LUA_OK) {
  1998. log_printf(0, "Lua: Failed to run script %s:\n %s\n", lua_script, lua_tostring(l, -1));
  1999. goto lua_end;
  2000. }
  2001. lua_getglobal(l, "willSendRequestToBbsCgi");
  2002. if (!lua_isfunction(l, -1)) {
  2003. log_printf(0, "Lua: willSendRequestToBbsCgi function does not exist in the script\n");
  2004. goto lua_end;
  2005. }
  2006. lua_newtable(l);
  2007. _headers->getUserdataForLua(l);
  2008. lua_setfield(l, -2, "headers");
  2009. lua_pushstring(l, _body);
  2010. lua_setfield(l, -2, "body");
  2011. lua_pushstring(l, hostStr.c_str());
  2012. lua_pushstring(l, boardStr.c_str());
  2013. lua_pushstring(l, threadStr.c_str());
  2014. if (lua_pcall(l, 4, 1, 0) != LUA_OK) {
  2015. log_printf(0, "Lua: Failed to call willSendRequestToBbsCgi function:\n %s\n", lua_tostring(l, -1));
  2016. goto lua_end;
  2017. }
  2018. if (!lua_istable(l, -1)) {
  2019. log_printf(0, "Lua: A return type of willSendRequestToBbsCgi function should be a table\n");
  2020. goto lua_end;
  2021. }
  2022. lua_pushstring(l, "body");
  2023. lua_rawget(l, -2);
  2024. if (lua_isstring(l, -1)) {
  2025. const char *newBody = lua_tostring(l, -1);
  2026. _body = strdup(newBody);
  2027. log_printf(1, "Lua: Set request body \"%s\"\n", newBody);
  2028. }
  2029. lua_pop(l, 1);
  2030. lua_pushstring(l, "headers");
  2031. lua_rawget(l, -2);
  2032. if (lua_istable(l, -1)) {
  2033. _headers = new BBS2chProxyHttpHeaders();
  2034. lua_pushnil(l);
  2035. while (lua_next(l, -2)) {
  2036. if (lua_isstring(l, -1) && lua_isstring(l, -2)) {
  2037. const char *name = lua_tostring(l, -2);
  2038. const char *value = lua_tostring(l, -1);
  2039. _headers->add(name, value);
  2040. log_printf(1, "Lua: Set request header \"%s: %s\"\n", name, value);
  2041. }
  2042. lua_pop(l, 1);
  2043. }
  2044. }
  2045. else if (lua_isuserdata(l, -1)) {
  2046. if (lua_getmetatable(l, -1)) {
  2047. if (lua_getfield(l, -1, "_type") == LUA_TSTRING) {
  2048. if (!strcmp(lua_tostring(l, -1), "HttpHeaders")) {
  2049. BBS2chProxyHttpHeaders *newHeaders = *((BBS2chProxyHttpHeaders **)lua_touserdata(l, -3));
  2050. if (newHeaders != _headers) {
  2051. /* remove metatable to prevent the object from garbage collected by lua */
  2052. lua_newtable(l);
  2053. lua_setmetatable(l, -4);
  2054. _headers = newHeaders;
  2055. }
  2056. for (std::map<std::string, PBBS2chProxyHttpHeaderEntry>::iterator it = _headers->getMap().begin(); it != _headers->getMap().end(); it++) {
  2057. log_printf(1, "Lua: Set request header \"%s\"\n", it->second->getFull().c_str());
  2058. }
  2059. }
  2060. }
  2061. lua_pop(l, 2);
  2062. }
  2063. }
  2064. lua_end:
  2065. lua_close(l);
  2066. }
  2067. #endif
  2068. do {
  2069. bool isPink = hostStr.find("bbspink.com") != std::string::npos;
  2070. bool shouldSign = appKey && (((api_mode & 2) && !isPink) || (api_mode & 4));
  2071. std::string userAgent = _headers->get("User-Agent");
  2072. if (_headers->has("X-MonaKey")) {
  2073. monaKeyForRequest = _headers->get("X-MonaKey");
  2074. }
  2075. if (shouldSign && !_headers->has("X-PostSig")) {
  2076. if (userAgent.empty() && user_agent) userAgent = user_agent;
  2077. if (!userAgent.empty()) {
  2078. monaKeyForRequest = getMonaKey();
  2079. appendPostSignature(_body, userAgent, monaKeyForRequest, _headers);
  2080. } else {
  2081. log_printf(0, "API: User-Agent muse be set explicitly to post with API.\n");
  2082. }
  2083. }
  2084. if (!monaKeyForRequest.empty() && monaKeyIssueTime.count(monaKeyForRequest)) {
  2085. double issued = monaKeyIssueTime[monaKeyForRequest];
  2086. double now = getCurrentTime();
  2087. double wait = 4.0 - (now - issued);
  2088. if (wait > 0) {
  2089. log_printf(1, "Sleeping for %.1f seconds to avoid posting too fast...\n", wait);
  2090. #ifdef _WIN32
  2091. Sleep(wait * 1e+3);
  2092. #else
  2093. usleep(wait * 1e+6);
  2094. #endif
  2095. }
  2096. monaKeyIssueTime.erase(monaKeyForRequest);
  2097. }
  2098. headersForCurl = _headers->appendToCurlSlist(headersForCurl);
  2099. if (!_headers->has("Expect")) headersForCurl = curl_slist_append(headersForCurl, "Expect:");
  2100. if (!_headers->has("Accept")) headersForCurl = curl_slist_append(headersForCurl, "Accept:");
  2101. } while (0);
  2102. if (curl) {
  2103. CURLcode res;
  2104. if (curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
  2105. curl_easy_setopt(curl, CURLOPT_URL, url);
  2106. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
  2107. curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
  2108. if (run == 0)
  2109. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_bbscgi);
  2110. else
  2111. curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_proxy);
  2112. curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
  2113. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_proxy);
  2114. curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
  2115. curl_easy_setopt(curl, CURLOPT_POST, 1L);
  2116. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, _body);
  2117. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  2118. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  2119. //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  2120. if (force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  2121. if (!strncasecmp(protocol,"HTTP/1.0",8) && !strncasecmp(url, "http://", 7)) {
  2122. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  2123. }
  2124. else {
  2125. /* force use HTTP 1.1 because CURL_HTTP_VERSION_2TLS is used on curl (w/ nghttp2) >= 7.62.0 */
  2126. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  2127. }
  2128. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
  2129. if (user_agent) {
  2130. curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
  2131. }
  2132. if (proxy_server) {
  2133. curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
  2134. curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
  2135. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
  2136. }
  2137. res = curl_easy_perform(curl);
  2138. if (res != CURLE_OK) {
  2139. if (res == CURLE_WRITE_ERROR && status == 2) {
  2140. log_printf(1, "MonaKey should be reset. Sending the same request automatically...\n");
  2141. curl_easy_reset(curl);
  2142. curl_slist_free_all(headersForCurl);
  2143. if (_headers != requestHeaders) delete _headers;
  2144. if (_body != requestBody) free(_body);
  2145. continue;
  2146. }
  2147. else {
  2148. log_printf(0,"curl error: %s (%s)\n",curl_easy_strerror(res),url);
  2149. if (!status) sendResponse(503, "Service Unavailable", socketToClient);
  2150. statusCode = 503;
  2151. }
  2152. }
  2153. else {
  2154. if (chunked) {
  2155. socketToClient->writeString("0\r\n\r\n");
  2156. }
  2157. curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
  2158. }
  2159. curl_easy_cleanup(curl);
  2160. }
  2161. curl_slist_free_all(headersForCurl);
  2162. if (_headers != requestHeaders) delete _headers;
  2163. if (_body != requestBody) free(_body);
  2164. break;
  2165. }
  2166. return statusCode;
  2167. }