BBS2chProxyConnection.cpp 63 KB

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