123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- #include "BBS2chProxyPoster.h"
- #include <vector>
- #include <map>
- #include <sstream>
- #include <algorithm>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #ifdef _WIN32
- #include <windows.h>
- #endif
- #ifdef USE_LUA
- #include <lua.hpp>
- #endif
- #include "BBS2chProxyAuth.h"
- #include "BBS2chProxyKeyManager.h"
- #include "hmac.h"
- #include "stringEncodingConverter.h"
- #include "parson/parson.h"
- extern char *proxy_server;
- extern long proxy_port;
- extern long proxy_type;
- extern long timeout;
- extern char *user_agent;
- extern int force_ipv4;
- extern char *appKey;
- extern char *hmacKey;
- extern unsigned int api_mode;
- extern BBS2chProxyHttpHeaders bbscgi_headers;
- extern std::vector<std::string> bbscgi_postorder;
- extern unsigned int bbscgi_utf8;
- extern char *lua_script;
- extern int allow_chunked;
- extern CURLSH *curl_share;
- extern void log_printf(int level, const char *format ...);
- extern double getCurrentTime(void);
- static size_t header_callback_bbscgi(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxy5chPoster *poster = static_cast<BBS2chProxy5chPoster *>(userdata);
- BBS2chProxyConnection *conn = poster->connectionDelegate;
- if(poster->_status > 0) return size*nitems;
- else if(poster->_status == -1) {
- if(!memcmp(buffer,"\r\n",2)) {
- poster->_status = 0;
- }
- return size*nitems;
- }
- PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
- if (parsedHeader) { // Looks like a header
- const std::string &headerName = parsedHeader->getLowercasedName();
- //fprintf(stderr, "%s\n", parsedHeader->getFull().c_str());
- if (headerName == "connection") {
- poster->_responseHeaders.append("Connection: Close\r\n");
- return size*nitems;
- }
- else if (headerName == "transfer-encoding") {
- if (parsedHeader->contains("chunked")) {
- if (allow_chunked && !conn->isClientHttp1_0) {
- poster->_isResponseChunked = true;
- poster->_responseHeaders.append(buffer, size*nitems);
- }
- return size*nitems;
- }
- }
- else if (conn->force5ch && headerName == "set-cookie") {
- std::string value = parsedHeader->getFull(true);
- size_t start = value.find("domain=");
- if (start != std::string::npos) {
- start += 7;
- size_t end = value.find(";", start);
- size_t pos = value.find(".5ch.net", start);
- if (pos != std::string::npos && (end == std::string::npos || pos < end)) {
- value[start+1] = '2';
- poster->_responseHeaders.append(value);
- return size*nitems;
- }
- }
- }
- else if (headerName == "x-chx-error") {
- const std::string &headerValue = parsedHeader->getValue();
- const char *ptr = headerValue.c_str();
- log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", ptr);
- if (*ptr == 'E') {
- int code = atoi(ptr+1);
- if ((code >= 3310 && code <= 3311) || /* inconsistent with User-Agent or something? */
- (code >= 3320 && code <= 3324) || /* key expiration? */
- (code >= 3390 && code <= 3392)) /* 3390: BAN, 3391-3392: key expiration? */
- {
- BBS2chProxyConnection::keyManager.setKey("", poster->_monaKeyForRequest, poster->_userAgentForRequest, code);
- if (poster->_isFirstRun) {
- poster->_status = 2;
- return 0;
- }
- }
- }
- }
- else if (headerName == "x-monakey") {
- BBS2chProxyConnection::keyManager.setKey(parsedHeader->getValue(), poster->_monaKeyForRequest, poster->_userAgentForRequest, 0);
- }
- poster->_responseHeaders.append(buffer, size*nitems);
- return size*nitems;
- }
- else {
- if (!strncasecmp("HTTP/", buffer, 5)) {
- const char *ptr = buffer + 5;
- const char *end = buffer + size*nitems;
- while (ptr < end && *ptr != ' ') ptr++;
- while (ptr < end && *ptr == ' ') ptr++;
- if (ptr < end) {
- int code = atoi(ptr);
- if (code == 100) {
- poster->_status = -1;
- return size*nitems;
- }
- }
- }
- poster->_responseHeaders.append(buffer, size*nitems);
- if (!memcmp(buffer, "\r\n", 2)) {
- conn->socketToClient->write(poster->_responseHeaders.data(), poster->_responseHeaders.size());
- poster->_status = 1;
- }
- return size*nitems;
- }
- }
- static size_t write_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxy5chPoster *poster = static_cast<BBS2chProxy5chPoster *>(userdata);
- BBS2chProxyConnection *conn = poster->connectionDelegate;
- if(poster->_isResponseChunked) {
- char buf[64];
- snprintf(buf, 64, "%lx\r\n", size*nitems);
- conn->socketToClient->write(buf, strlen(buf));
- }
- size_t ret = conn->socketToClient->write(buffer, size*nitems);
- if(poster->_isResponseChunked) conn->socketToClient->writeString("\r\n");
- return ret;
- }
- static size_t header_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxyHttpHeaders *headers = static_cast<BBS2chProxyHttpHeaders *>(userdata);
- PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
- if (parsedHeader.get()) { // Looks like a header
- const std::string &headerName = parsedHeader->getLowercasedName();
- if (headerName == "connection") {
- if (headers) headers->add(parsedHeader->getName(), "close");
- return size*nitems;
- }
- else if (headerName == "transfer-encoding") {
- if (parsedHeader->contains("chunked")) {
- return size*nitems;
- }
- }
- if (headers) headers->add(parsedHeader->getName(), parsedHeader->getValue());
- }
- else if (headers) headers->setStatusLine(buffer, size*nitems);
- return size*nitems;
- }
- static size_t write_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- std::vector<char> *data = static_cast<std::vector<char> *>(userdata);
- size_t downloaded = size*nitems;
- if (data) data->insert(data->end(), buffer, buffer+downloaded);
- return downloaded;
- }
- static bool isValidAsUTF8(const char *input, size_t inputLength) {
- for (int i=0; i<inputLength; i++) {
- unsigned char c1 = input[i];
- if (c1 < 0x80) continue;
- else if (c1 >= 0xc2 && c1 <= 0xdf) {
- if (i >= inputLength - 1) return false;
- unsigned char c2 = input[++i];
- if (c2 < 0x80 || c2 > 0xbf) return false;
- unsigned int unicode = c2 & 0xf;
- unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 4;
- unicode |= ((c1 >> 2) & 0x7) << 8;
- if (unicode < 0x80 || unicode > 0x7ff) return false;
- }
- else if (c1 >= 0xe0 && c1 <= 0xef) {
- if (i >= inputLength - 2) return false;
- unsigned char c2 = input[++i];
- if (c2 < 0x80 || c2 > 0xbf) return false;
- unsigned char c3 = input[++i];
- if (c3 < 0x80 || c3 > 0xbf) return false;
- unsigned int unicode = c3 & 0xf;
- unicode |= (((c3 >> 4) & 0x3) | ((c2 & 0x3) << 2)) << 4;
- unicode |= ((c2 >> 2) & 0xf) << 8;
- unicode |= (c1 & 0xf) << 12;
- if (unicode < 0x800 || unicode > 0xffff) return false;
- else if (unicode >= 0xd800 && unicode <= 0xdfff) return false; /* for surrogate pairs */
- }
- else if (c1 >= 0xf0 && c1 <= 0xf7) {
- if (i >= inputLength - 3) return false;
- unsigned char c2 = input[++i];
- if (c2 < 0x80 || c2 > 0xbf) return false;
- unsigned char c3 = input[++i];
- if (c3 < 0x80 || c3 > 0xbf) return false;
- unsigned char c4 = input[++i];
- if (c4 < 0x80 || c4 > 0xbf) return false;
- unsigned int unicode = c4 & 0xf;
- unicode |= (((c4 >> 4) & 0x3) | ((c3 & 0x3) << 2)) << 4;
- unicode |= ((c3 >> 2) & 0xf) << 8;
- unicode |= (c2 & 0xf) << 12;
- unicode |= (((c2 >> 4) & 0x3) | ((c1 & 0x3) << 2)) << 16;
- unicode |= ((c1 >> 2) & 0x1) << 20;
- if (unicode < 0x10000 || unicode > 0x10ffff) return false;
- }
- else return false;
- }
- return true;
- }
- static void appendPostSignature(BBS2chProxyFormData &body, const std::string &userAgent, const std::string &monaKey, BBS2chProxyHttpHeaders &headers, bool forTalk)
- {
- char nonce[32];
- std::string message;
- if (!forTalk) {
- snprintf(nonce, 32, "%.3f", getCurrentTime());
- message.append(body["bbs"]);
- message.append("<>");
- message.append(body["key"]);
- message.append("<>");
- message.append(body["time"]);
- message.append("<>");
- message.append(body["FROM"]);
- message.append("<>");
- message.append(body["mail"]);
- message.append("<>");
- message.append(body["MESSAGE"]);
- message.append("<>");
- message.append(body["subject"]);
- message.append("<>");
- message.append(userAgent);
- message.append("<>");
- message.append(monaKey);
- message.append("<>");
- message.append("<>");
- message.append(nonce);
- } else {
- message.append(body["bbs"]);
- message.append("<>");
- message.append(body["key"]);
- message.append("<>");
- message.append(body["subject"]);
- message.append("<>");
- message.append(body["MESSAGE"]);
- message.append("<>");
- message.append(body["time"]);
- message.append("<>");
- message.append(body["sid"]);
- message.append("<>");
- }
- unsigned char digest[32];
- char digestStr[65];
- static const char *table = "0123456789abcdef";
- proxy2ch_HMAC_SHA256(hmacKey, strlen(hmacKey), message.data(), message.length(), digest);
- for (int i=0; i<32; i++) {
- unsigned char c = digest[i];
- unsigned char upper = (c >> 4) & 0xf;
- unsigned char lower = c & 0xf;
- digestStr[i*2] = table[upper];
- digestStr[i*2+1] = table[lower];
- }
- digestStr[64] = 0;
- if (!forTalk) {
- headers.set("X-APIKey", appKey);
- log_printf(1, "Appended header \"X-APIKey: %s\"\n", appKey);
- headers.set("X-PostSig", digestStr);
- log_printf(1, "Appended header \"X-PostSig: %s\"\n", digestStr);
- headers.set("X-PostNonce", nonce);
- log_printf(1, "Appended header \"X-PostNonce: %s\"\n", nonce);
- headers.set("X-MonaKey", monaKey);
- log_printf(1, "Appended header \"X-MonaKey: %s\"\n", monaKey.c_str());
- } else {
- headers.set("X-Write-Token", digestStr);
- log_printf(1, "Appended header \"X-Write-Token: %s\"\n", digestStr);
- if (monaKey != "00000000-0000-0000-0000-000000000000") {
- headers.set("X-Write-Key", monaKey);
- log_printf(1, "Appended header \"X-Write-Key: %s\"\n", monaKey.c_str());
- }
- }
- }
- static bool convertBodyToUTF8(BBS2chProxyFormData &body)
- {
- bool shouldConvertToUTF8 = true;
- bool shouldCheckWholeBody = true;
- const std::string &submit = body.get("submit");
- if (body.getEncoded("submit").size() != submit.size()) {
- if (isValidAsUTF8(submit.data(), submit.size())) {
- shouldConvertToUTF8 = false;
- }
- shouldCheckWholeBody = false;
- }
- if (shouldCheckWholeBody) {
- shouldConvertToUTF8 = false;
- for (BBS2chProxyFormData::iterator it = body.begin(); it != body.end(); ++it) {
- if (it->second.empty()) continue;
- if (!isValidAsUTF8(it->second.get().data(), it->second.get().size())) {
- shouldConvertToUTF8 = true;
- break;
- }
- }
- }
- if (shouldConvertToUTF8) {
- for (BBS2chProxyFormData::iterator it = body.begin(); it != body.end(); ++it) {
- if (it->second.empty()) continue;
- char *converted = convertShiftJISToUTF8(it->second.get().data(), it->second.get().size());
- if (converted) {
- it->second = std::string(converted);
- free(converted);
- }
- }
- }
- return shouldConvertToUTF8;
- }
- #ifdef USE_LUA
- extern "C" {
- static int lua_hmacSHA256(lua_State *l)
- {
- static const char *table = "0123456789abcdef";
- size_t keyLength, dataLength;
- const char *key = luaL_checklstring(l, 1, &keyLength);
- const char *data = luaL_checklstring(l, 2, &dataLength);
- if (!key || !data) return 0;
- unsigned char digest[32];
- char digestStr[65];
- proxy2ch_HMAC_SHA256(key, keyLength, data, dataLength, digest);
- for (int i=0; i<32; i++) {
- unsigned char c = digest[i];
- unsigned char upper = (c >> 4) & 0xf;
- unsigned char lower = c & 0xf;
- digestStr[i*2] = table[upper];
- digestStr[i*2+1] = table[lower];
- }
- digestStr[64] = 0;
- lua_pushstring(l, digestStr);
- return 1;
- }
- static int lua_decodeURIComponent(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- bool decodePlus = true;
- if (!lua_isnoneornil(l, 2)) {
- decodePlus = (lua_toboolean(l, 2));
- }
- std::string output = decodeURIComponent(input, length, decodePlus);
- lua_pushstring(l, output.c_str());
- return 1;
- }
- static int lua_encodeURIComponent(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- bool spaceAsPlus = true;
- if (!lua_isnoneornil(l, 2)) {
- spaceAsPlus = (lua_toboolean(l, 2));
- }
- std::string output = encodeURIComponent(input, length, spaceAsPlus);
- lua_pushstring(l, output.c_str());
- return 1;
- }
- static int lua_convertShiftJISToUTF8(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- if (length > 0) {
- char *output = convertShiftJISToUTF8(input, length);
- if (!output) lua_pushnil(l);
- else {
- lua_pushstring(l, output);
- free(output);
- }
- }
- else lua_pushstring(l, "");
- return 1;
- }
- static int lua_isExpiredKey(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- if (BBS2chProxyConnection::keyManager.isExpired(input)) {
- lua_pushboolean(l, 1);
- }
- else lua_pushboolean(l, 0);
- return 1;
- }
- static int lua_isValidAsUTF8(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- lua_pushboolean(l, isValidAsUTF8(input, length));
- return 1;
- }
- static int lua_getMonaKey(lua_State *l)
- {
- size_t length;
- const char *input = luaL_checklstring(l, 1, &length);
- if (!input) return 0;
- const std::string &key = BBS2chProxyConnection::keyManager.getKey(input);
- lua_pushstring(l, key.c_str());
- return 1;
- }
- static int lua_getSID(lua_State *l)
- {
- const std::string &sid = BBS2chProxyConnection::auth.getSID();
- lua_pushstring(l, sid.c_str());
- return 1;
- }
- }
- #endif
- IBBS2chProxyPoster::IBBS2chProxyPoster(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
- : _requestHeaders(headers), _requestBody(body), _verbose(0), connectionDelegate(delegate)
- {
- prepareHeadersAndBody();
- }
- BBS2chProxy5chPoster::BBS2chProxy5chPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
- : IBBS2chProxyPoster(headers, body, delegate), _isFirstRun(true), _status(0), _isResponseChunked(false)
- {
- _url = url.absoluteString();
- }
- BBS2chProxyTalkPoster::BBS2chProxyTalkPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
- : IBBS2chProxyPoster(headers, body, delegate)
- {
- _url = "https://api.talk-platform.com/v1/bbs.cgi";
- _requestBody.remove("sid");
- }
- BBS2chProxyTalkTo5chPoster::BBS2chProxyTalkTo5chPoster(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, BBS2chProxyConnection *delegate)
- : BBS2chProxy5chPoster(url, headers, body, delegate)
- {
- }
- void IBBS2chProxyPoster::prepareHeadersAndBody()
- {
- _host = _requestHeaders.get("Host");
- _board = _requestBody.get("bbs");
- _thread = _requestBody.get("key");
- if (!bbscgi_postorder.empty()) {
- _requestBody.reorder(bbscgi_postorder);
- log_printf(1, "Reordered request body is: %s\n", _requestBody.toString().c_str());
- }
- _requestHeaders.remove("Host");
- if (user_agent) _requestHeaders.set("User-Agent", user_agent);
- if (!bbscgi_headers.empty()) {
- for (std::map<std::string, PBBS2chProxyHttpHeaderEntry>::iterator it = bbscgi_headers.getMap().begin(); it != bbscgi_headers.getMap().end(); it++) {
- /* we create a copy of entry here, because the original entry shouldn't be modified */
- PBBS2chProxyHttpHeaderEntry entry(new BBS2chProxyHttpHeaderEntry(*it->second.get()));
- if (!_host.empty()) {
- entry->replaceValue("%HOST%", _host);
- }
- if (!_board.empty()) {
- entry->replaceValue("%BOARD%", _board);
- }
- if (!_thread.empty()) {
- entry->replaceValue("%THREAD%", _thread);
- }
- _requestHeaders.set(entry);
- log_printf(1, "Appended custom header \"%s\"\n", entry->getFull().c_str());
- }
- }
- }
- curl_slist* IBBS2chProxyPoster::prepareCurlHandle(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body, curl_slist* headersForCurl)
- {
- CURL *curl = connectionDelegate->curl;
- headersForCurl = headers.appendToCurlSlist(headersForCurl);
- if (!headers.has("Expect")) headersForCurl = curl_slist_append(headersForCurl, "Expect:");
- if (!headers.has("Accept")) headersForCurl = curl_slist_append(headersForCurl, "Accept:");
- if (curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
- curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.toString().c_str());
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, _verbose);
- if (force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
- if (!_nic.empty()) curl_easy_setopt(curl, CURLOPT_INTERFACE, _nic.c_str());
- if (!_forceProxy.empty()) {
- curl_easy_setopt(curl, CURLOPT_PROXY, _forceProxy.c_str());
- }
- else if (proxy_server) {
- curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
- curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
- curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
- }
- return headersForCurl;
- }
- void IBBS2chProxyPoster::runLuaScript(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
- {
- #ifdef USE_LUA
- lua_State* l = luaL_newstate();
- luaL_openlibs(l);
- if (luaL_loadfile(l, lua_script) != LUA_OK) {
- log_printf(0, "Lua: Failed to open script %s:\n %s\n", lua_script, lua_tostring(l, -1));
- goto lua_end;
- }
- lua_newtable(l);
- lua_pushcfunction(l, lua_hmacSHA256);
- lua_setfield(l, -2, "hmacSHA256");
- lua_pushcfunction(l, lua_decodeURIComponent);
- lua_setfield(l, -2, "decodeURIComponent");
- lua_pushcfunction(l, lua_encodeURIComponent);
- lua_setfield(l, -2, "encodeURIComponent");
- lua_pushcfunction(l, lua_convertShiftJISToUTF8);
- lua_setfield(l, -2, "convertShiftJISToUTF8");
- lua_pushcfunction(l, lua_isExpiredKey);
- lua_setfield(l, -2, "isExpiredKey");
- lua_pushcfunction(l, lua_isValidAsUTF8);
- lua_setfield(l, -2, "isValidAsUTF8");
- lua_pushcfunction(l, lua_getMonaKey);
- lua_setfield(l, -2, "getMonaKey");
- lua_pushcfunction(l, lua_getSID);
- lua_setfield(l, -2, "getSID");
- lua_pushstring(l, BBS2chProxyConnection::keyManager.getKey().c_str());
- lua_setfield(l, -2, "monaKey");
- lua_pushinteger(l, connectionDelegate->serverPort);
- lua_setfield(l, -2, "port");
- lua_setglobal(l, "proxy2ch");
- BBS2chProxyHttpHeaders::getClassDefinitionForLua(l);
- lua_setglobal(l, "HttpHeaders");
- if (lua_pcall(l, 0, 0, 0) != LUA_OK) {
- log_printf(0, "Lua: Failed to run script %s:\n %s\n", lua_script, lua_tostring(l, -1));
- goto lua_end;
- }
- lua_getglobal(l, "willSendRequestToBbsCgi");
- if (!lua_isfunction(l, -1)) {
- log_printf(0, "Lua: willSendRequestToBbsCgi function does not exist in the script\n");
- goto lua_end;
- }
- lua_newtable(l);
- headers.getUserdataForLua(l);
- lua_setfield(l, -2, "headers");
- lua_pushstring(l, body.toString().c_str());
- lua_setfield(l, -2, "body");
- lua_pushstring(l, _host.c_str());
- lua_pushstring(l, _board.c_str());
- lua_pushstring(l, _thread.c_str());
- if (lua_pcall(l, 4, 1, 0) != LUA_OK) {
- log_printf(0, "Lua: Failed to call willSendRequestToBbsCgi function:\n %s\n", lua_tostring(l, -1));
- goto lua_end;
- }
- if (!lua_istable(l, -1)) {
- log_printf(0, "Lua: A return type of willSendRequestToBbsCgi function should be a table\n");
- goto lua_end;
- }
- lua_pushstring(l, "body");
- lua_rawget(l, -2);
- if (lua_isstring(l, -1)) {
- size_t length;
- const char *newBody = lua_tolstring(l, -1, &length);
- body = BBS2chProxyFormData(newBody, length);
- log_printf(1, "Lua: Set request body \"%s\"\n", newBody);
- }
- lua_pop(l, 1);
- lua_pushstring(l, "headers");
- lua_rawget(l, -2);
- if (lua_istable(l, -1)) {
- headers = BBS2chProxyHttpHeaders();
- lua_pushnil(l);
- while (lua_next(l, -2)) {
- if (lua_isstring(l, -1) && lua_isstring(l, -2)) {
- const char *name = lua_tostring(l, -2);
- const char *value = lua_tostring(l, -1);
- headers.add(name, value);
- log_printf(1, "Lua: Set request header \"%s: %s\"\n", name, value);
- }
- lua_pop(l, 1);
- }
- }
- else if (lua_isuserdata(l, -1)) {
- if (lua_getmetatable(l, -1)) {
- if (lua_getfield(l, -1, "_type") == LUA_TSTRING) {
- if (!strcmp(lua_tostring(l, -1), "HttpHeaders")) {
- BBS2chProxyHttpHeaders *newHeaders = *((BBS2chProxyHttpHeaders **)lua_touserdata(l, -3));
- if (newHeaders != &headers) {
- headers = *newHeaders;
- }
- for (BBS2chProxyHttpHeaders::iterator it = headers.begin(); it != headers.end(); ++it) {
- log_printf(1, "Lua: Set request header \"%s\"\n", it->second.c_str());
- }
- }
- }
- lua_pop(l, 2);
- }
- }
- lua_pop(l, 1);
- lua_pushstring(l, "options");
- lua_rawget(l, -2);
- if (lua_istable(l, -1)) {
- lua_pushstring(l, "interface");
- lua_rawget(l, -2);
- if (lua_isstring(l, -1)) {
- _nic = std::string(lua_tostring(l, -1));
- }
- lua_pop(l, 1);
- lua_pushstring(l, "verbose");
- lua_rawget(l, -2);
- if (lua_isboolean(l, -1)) {
- _verbose = lua_toboolean(l, -1);
- }
- lua_pop(l, 1);
- lua_pushstring(l, "proxy");
- lua_rawget(l, -2);
- if (lua_isstring(l, -1)) {
- _forceProxy = std::string(lua_tostring(l, -1));
- }
- lua_pop(l, 1);
- }
- lua_end:
- lua_close(l);
- #endif
- }
- void BBS2chProxy5chPoster::makeSignature(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
- {
- bool isPink = _host.find("bbspink.com") != std::string::npos;
- bool shouldSign = appKey && (((api_mode & 2) && !isPink) || (api_mode & 4));
- bool shouldConvertBodyToUTF8 = (bbscgi_utf8 == 1 && shouldSign) || (bbscgi_utf8 == 2);
- _userAgentForRequest = headers.get("User-Agent");
- if (headers.has("X-MonaKey")) {
- _monaKeyForRequest = headers.get("X-MonaKey");
- }
- if (shouldConvertBodyToUTF8 && !headers.has("X-PostSig")) {
- if (convertBodyToUTF8(body)) {
- log_printf(1, "Converted request body to UTF-8: %s\n", body.toString().c_str());
- }
- else {
- log_printf(1, "Request body seems already to be UTF-8, will be sent without conversion\n");
- }
- std::string contentType = headers.get("Content-Type");
- std::transform(contentType.begin(), contentType.end(), contentType.begin(), tolower);
- if (contentType.find("charset=utf-8") == std::string::npos) {
- headers.set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
- log_printf(1, "Appended header \"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\"\n");
- }
- }
- if (shouldSign && (!lua_script || !headers.has("X-PostSig"))) {
- if (!_userAgentForRequest.empty()) {
- _monaKeyForRequest = BBS2chProxyConnection::keyManager.getKey(_userAgentForRequest);
- appendPostSignature(body, _userAgentForRequest, _monaKeyForRequest, headers, false);
- } else {
- log_printf(0, "API: User-Agent muse be set explicitly to post with API.\n");
- }
- }
- if (!_monaKeyForRequest.empty()) {
- double wait = BBS2chProxyConnection::keyManager.secondsToWaitBeforePosting(_monaKeyForRequest);
- if (wait > 0) {
- log_printf(1, "Sleeping for %.1f seconds to avoid posting too fast...\n", wait);
- #ifdef _WIN32
- Sleep(wait * 1e+3);
- #else
- usleep(wait * 1e+6);
- #endif
- }
- }
- }
- long BBS2chProxy5chPoster::post()
- {
- CURL *curl = connectionDelegate->curl;
- long statusCode = 0;
- for (int run=0; run<2; run++) {
- BBS2chProxyHttpHeaders _headers = _requestHeaders;
- BBS2chProxyFormData _body = _requestBody;
- curl_slist *headersForCurl = NULL;
- _verbose = 0;
- _status = 0;
- _monaKeyForRequest.clear();
- _nic.clear();
- _forceProxy.clear();
- _isFirstRun = run == 0;
- _responseHeaders.clear();
- #ifdef USE_LUA
- if (lua_script) {
- runLuaScript(_headers, _body);
- }
- #endif
- makeSignature(_headers, _body);
- headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_bbscgi);
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_proxy);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
- CURLcode res = curl_easy_perform(curl);
- if (res != CURLE_OK) {
- if (res == CURLE_WRITE_ERROR && _status == 2) {
- log_printf(1, "MonaKey should be reset. Sending the same request automatically...\n");
- curl_easy_reset(curl);
- curl_slist_free_all(headersForCurl);
- continue;
- }
- else {
- log_printf(0, "curl error: %s (%s)\n", curl_easy_strerror(res), _url.c_str());
- if (!_status) connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
- statusCode = 503;
- }
- }
- else {
- if (_isResponseChunked) {
- connectionDelegate->socketToClient->writeString("0\r\n\r\n");
- }
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
- }
- curl_easy_reset(curl);
- curl_slist_free_all(headersForCurl);
- break;
- }
- return statusCode;
- }
- void BBS2chProxyTalkPoster::makeSignature(BBS2chProxyHttpHeaders &headers, BBS2chProxyFormData &body)
- {
- bool shouldSign = appKey && (api_mode & 8);
- bool shouldConvertBodyToUTF8 = (bbscgi_utf8 == 1 && shouldSign) || (bbscgi_utf8 == 2);
- _userAgentForRequest = headers.get("User-Agent");
- if (headers.has("X-Write-Key")) {
- _monaKeyForRequest = headers.get("X-Write-Key");
- }
- if (shouldConvertBodyToUTF8 && !headers.has("X-Write-Token")) {
- if (convertBodyToUTF8(body)) {
- log_printf(1, "Converted request body to UTF-8: %s\n", body.toString().c_str());
- }
- else {
- log_printf(1, "Request body seems already to be UTF-8, will be sent without conversion\n");
- }
- std::string contentType = headers.get("Content-Type");
- std::transform(contentType.begin(), contentType.end(), contentType.begin(), tolower);
- if (contentType.find("charset=utf-8") == std::string::npos) {
- headers.set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
- log_printf(1, "Appended header \"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\"\n");
- }
- }
- if (shouldSign && (!lua_script || !headers.has("X-Write-Token"))) {
- if (!_userAgentForRequest.empty()) {
- body.append("sid", BBS2chProxyConnection::auth.getSID());
- body.append("appkey", appKey);
- body.append("anonymous", body.has("subject") ? "true" : "false");
- _monaKeyForRequest = BBS2chProxyConnection::keyManager.getKey(_userAgentForRequest);
- appendPostSignature(body, _userAgentForRequest, _monaKeyForRequest, headers, true);
- } else {
- log_printf(0, "API: User-Agent muse be set explicitly to post with API.\n");
- }
- }
- if (!_monaKeyForRequest.empty()) {
- double wait = BBS2chProxyConnection::keyManager.secondsToWaitBeforePosting(_monaKeyForRequest);
- if (wait > 0) {
- log_printf(1, "Sleeping for %.1f seconds to avoid posting too fast...\n", wait);
- #ifdef _WIN32
- Sleep(wait * 1e+3);
- #else
- usleep(wait * 1e+6);
- #endif
- }
- }
- }
- long BBS2chProxyTalkPoster::post()
- {
- CURL *curl = connectionDelegate->curl;
- long statusCode = 0;
- for (int run=0; run<2; run++) {
- BBS2chProxyHttpHeaders _headers = _requestHeaders;
- BBS2chProxyFormData _body = _requestBody;
- curl_slist *headersForCurl = NULL;
- std::string acceptEncoding;
- _verbose = 0;
- _nic.clear();
- _forceProxy.clear();
- #ifdef USE_LUA
- if (lua_script) {
- runLuaScript(_headers, _body);
- }
- #endif
- makeSignature(_headers, _body);
- if (_headers.has("Accept-Encoding")) {
- acceptEncoding = _headers.get("Accept-Encoding");
- _headers.remove("Accept-Encoding");
- }
- headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
- BBS2chProxyHttpHeaders receivedHeaders;
- std::vector<char> receivedBody;
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, &receivedHeaders);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &receivedBody);
- if (!acceptEncoding.empty()) curl_easy_setopt(curl, CURLOPT_ENCODING, acceptEncoding.c_str());
- CURLcode res = curl_easy_perform(curl);
- bool responseSent = false;
- if (res == CURLE_OK) curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
- if (receivedHeaders.hasNameAndValue("Content-Type", "application/json")) {
- receivedBody.push_back('\0');
- JSON_Value *json = json_parse_string(&receivedBody.front());
- if (json && json_type(json) == JSONObject) {
- JSON_Object *root = json_object(json);
- const char *error = json_object_dotget_string(root, "error.message");
- if (error) {
- log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s\n", error);
- std::string out = "<html>\n<head>\n<title>ERROR!</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n</head>\n<body bgcolor=\"#EFEFEF\">\n<font size=\"+1\" color=\"#FF0000\"><b>ERROR: ";
- out += error;
- out += "</b></font>\n</body>\n</html>";
- char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
- connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
- connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
- if (outSJIS) {
- connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
- free(outSJIS);
- }
- statusCode = 200;
- responseSent = true;
- } else if (statusCode == 200) {
- int resNum = json_object_get_number(root, "commentNumber");
- std::string out = "<html lang=\"ja\">\n<head>\n<title>書きこみました。</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n</head>\n<body>書きこみが終わりました。<br><br>\n画面を切り替えるまでしばらくお待ち下さい。<br><br>\n</body>\n</html>";
- char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
- connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
- if (resNum) {
- std::ostringstream ss;
- ss << "X-Resnum: " << resNum << "\r\n";
- connectionDelegate->socketToClient->writeString(ss.str());
- }
- connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
- if (outSJIS) {
- connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
- free(outSJIS);
- }
- responseSent = true;
- }
- }
- if (json) json_value_free(json);
- }
- else if (receivedHeaders.has("X-Write-Key")) {
- BBS2chProxyConnection::keyManager.setKey(receivedHeaders.get("X-Write-Key"), _monaKeyForRequest, _userAgentForRequest, 0);
- const std::string &extendToken = receivedHeaders.get("X-Write-Key-Extend-Token");
- if (run == 0) {
- log_printf(1, "MonaKey has been updated. Sending the same request automatically...\n");
- if (!extendToken.empty()) {
- _requestHeaders.set("X-Write-Key-Extend-Token", extendToken);
- }
- curl_easy_reset(curl);
- curl_slist_free_all(headersForCurl);
- continue;
- } else if (!extendToken.empty()) {
- log_printf(0, "WARNING: you must send header \"X-Write-Key-Extend-Token: %s\" to complete posting.\n", extendToken.c_str());
- }
- std::string out = "<html><head><title>■ 書き込み確認 ■</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\"></head><body bgcolor=\"#EEEEEE\"><font size=\"+1\" color=\"#FF0000\"><b>書きこみ&クッキー確認</b></font><br><br><b>投稿確認<br><b style=\"color: #F00; font-size: larger;\">この書き込みで本当にいいですか?<br>\n<form method=\"POST\" action=\"../test/bbs.cgi\" accept-charset=\"Shift_JIS\"><input type=hidden name=FROM value=><input type=hidden name=mail value=><input type=hidden name=MESSAGE value=><input type=hidden name=bbs value=><input type=hidden name=time value=><input type=hidden name=key value=><input type=submit value=\"上記全てを承諾して書き込む\" name=\"submit\"></form></body></html>";
- char *outSJIS = convertUTF8ToShiftJIS(out.c_str(), out.size());
- connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
- connectionDelegate->socketToClient->writeString("Content-Type: text/html; charset=Shift_JIS\r\n\r\n");
- if (outSJIS) {
- connectionDelegate->socketToClient->write(outSJIS, strlen(outSJIS));
- free(outSJIS);
- }
- statusCode = 200;
- responseSent = true;
- }
- if (!responseSent) {
- log_printf(5, "bbscgi response: %s\n", receivedHeaders.getStatusLine().c_str());
- for (BBS2chProxyHttpHeaders::iterator it = receivedHeaders.begin(); it != receivedHeaders.end(); ++it) {
- log_printf(5, "bbscgi response: %s\n", it->second.c_str());
- }
- receivedBody.push_back('\0');
- log_printf(5, "bbscgi response: %s\n", &receivedBody.front());
- connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
- statusCode = 503;
- }
- curl_easy_reset(curl);
- curl_slist_free_all(headersForCurl);
- break;
- }
- return statusCode;
- }
- long BBS2chProxyTalkTo5chPoster::post()
- {
- CURL *curl = connectionDelegate->curl;
- long statusCode = 0;
- for (int run=0; run<2; run++) {
- BBS2chProxyHttpHeaders _headers = _requestHeaders;
- BBS2chProxyFormData _body = _requestBody;
- curl_slist *headersForCurl = NULL;
- _verbose = 0;
- _status = 0;
- _monaKeyForRequest = "";
- _nic.clear();
- _forceProxy.clear();
- #ifdef USE_LUA
- if (lua_script) {
- runLuaScript(_headers, _body);
- }
- #endif
- makeSignature(_headers, _body);
- headersForCurl = prepareCurlHandle(_headers, _body, headersForCurl);
- BBS2chProxyHttpHeaders receivedHeaders;
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, &receivedHeaders);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
- CURLcode res = curl_easy_perform(curl);
- if (res == CURLE_OK) {
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
- if (receivedHeaders.has("Set-Cookie")) {
- std::vector<std::string>& values = receivedHeaders.getEntry("Set-Cookie")->getValueList();
- for (std::vector<std::string>::iterator it = values.begin(); it != values.end(); it++) {
- std::string &value = *it;
- size_t start = value.find("domain=");
- if (start == std::string::npos) continue;
- start += 7;
- size_t end = value.find(";", start);
- size_t pos = value.find(".5ch.net", start);
- size_t domainLen = 8;
- if (pos == std::string::npos || (end != std::string::npos && pos >= end)) {
- pos = value.find(".bbspink.com", start);
- domainLen = 12;
- }
- if (pos != std::string::npos && (end == std::string::npos || pos < end)) {
- value.replace(start, pos+domainLen-start, ".talk-platform.com");
- }
- }
- }
- if (statusCode == 200 && !receivedHeaders.has("X-Chx-Error")) {
- std::ostringstream ss;
- ss << "{\"boardCode\":\"" << "5channel_" << _board << "\",\"threadNumber\":" << _thread << ",\"commentNumber\":" << receivedHeaders.get("X-Resnum") << "}";
- connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
- if (receivedHeaders.has("Set-Cookie")) connectionDelegate->socketToClient->writeString(receivedHeaders.getFull("Set-Cookie", true));
- connectionDelegate->socketToClient->writeString("Content-Type: application/json\r\n\r\n");
- connectionDelegate->socketToClient->writeString(ss.str());
- } else if (statusCode == 200) {
- std::ostringstream ss;
- ss << "{\"error\":{\"message\":\"" << receivedHeaders.get("X-Chx-Error") << "\"}}";
- connectionDelegate->socketToClient->sendBasicHeaders(200, "OK");
- if (receivedHeaders.has("Set-Cookie")) connectionDelegate->socketToClient->writeString(receivedHeaders.getFull("Set-Cookie", true));
- connectionDelegate->socketToClient->writeString("Content-Type: application/json\r\n\r\n");
- connectionDelegate->socketToClient->writeString(ss.str());
- } else {
- connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
- statusCode = 503;
- }
- } else {
- connectionDelegate->socketToClient->sendResponse(503, "Service Unavailable");
- statusCode = 503;
- }
- curl_easy_reset(curl);
- curl_slist_free_all(headersForCurl);
- break;
- }
- return statusCode;
- }
|