123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- #pragma once
- static const char threadTimestampFmt[] = "%Y/%m/%d %H:%M:%S %Z";
- static const char httpTimestampFmt[] = "%a, %d %b %Y %H:%M:%S GMT";
- static void *
- memmem_priv(const void *l, size_t l_len, const void *s, size_t s_len)
- {
- register char *cur, *last;
- const char *cl = (const char *)l;
- const char *cs = (const char *)s;
-
- /* we need something to compare */
- if (l_len == 0 || s_len == 0)
- return NULL;
-
- /* "s" must be smaller or equal to "l" */
- if (l_len < s_len)
- return NULL;
-
- /* special case where s_len == 1 */
- if (s_len == 1)
- return (void *)memchr(l, (int)*cs, l_len);
-
- /* the last position where its possible to find "s" in "l" */
- last = (char *)cl + l_len - s_len;
-
- for (cur = (char *)cl; cur <= last; cur++)
- if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
- return cur;
-
- return NULL;
- }
- static int decryptMail(unsigned char *decrypted, char *encrypted)
- {
- char current[5]="0x";
- unsigned char *ptr = decrypted;
- current[2] = encrypted[0];
- current[3] = encrypted[1];
- unsigned int r = strtol(current,NULL,16);
- int len = strlen(encrypted);
- int n = 2;
- for(;n<len;n+=2) {
- current[2] = encrypted[n];
- current[3] = encrypted[n+1];
- unsigned int i = strtol(current,NULL,16);
- *ptr++ = i^r;
- }
- *ptr = 0;
- //fprintf(stderr,"%s->%s\n",encrypted,decrypted);
- return ptr - decrypted;
- }
- static size_t header_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
- if(conn->status) return size*nitems;
-
- if(!strncasecmp("Connection",buffer,10)) {
- fprintf(conn->fpw,"Connection: Close\r\n");
- return size*nitems;
- }
- else if(!strncasecmp("Transfer-Encoding",buffer,17)) {
- if(allow_chunked && strstr(buffer+19,"chunked")) {
- conn->chunked = true;
- //fprintf(stderr,"%s",buffer);
- size_t ret = fwrite(buffer,size,nitems,conn->fpw);
- return ret;
- }
- return size*nitems;
- }
- else if(conn->force5ch && !strncasecmp("Set-Cookie:",buffer,11)) {
- char *ptr = (char *)memmem_priv(buffer,size*nitems,"domain=",7);
- if(ptr) {
- char *end = ptr;
- while(*end != ';' && *end != '\r') end++;
- ptr = (char *)memmem_priv(ptr,end-ptr,"5ch.net",7);
- if(ptr) {
- fwrite(buffer,1,ptr-buffer,conn->fpw);
- fputc('2',conn->fpw);
- fwrite(ptr+1,1,size*nitems-(ptr+1-buffer),conn->fpw);
- return size*nitems;
- }
- }
- return fwrite(buffer,1,size*nitems,conn->fpw);
- }
- else {
- if (conn->bbscgi) {
- if (!strncasecmp("X-Chx-Error:", buffer, 12)) {
- const char *ptr = buffer + 12;
- while (*ptr == ' ') ptr++;
- log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s", 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? */
- conn->setMonaKey("", code);
- }
- }
- else if (!strncasecmp("X-MonaKey:", buffer, 10)) {
- const char *ptr = buffer + 10;
- while (*ptr == ' ') ptr++;
- const char *end = ptr;
- while (*end != '\n' && *end != '\r' && *end) end++;
- conn->setMonaKey(std::string(ptr, end-ptr), 0);
- }
- }
- size_t ret = fwrite(buffer,size,nitems,conn->fpw);
- //fprintf(stderr,"%s",buffer);
- if(!memcmp(buffer,"\r\n",2)) {
- fflush(conn->fpw);
- conn->status = 1;
- }
- return ret;
- }
- }
- static size_t header_callback_bbscgi(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
- if(conn->status) return size*nitems;
-
- if(!strncasecmp("Connection",buffer,10)) {
- conn->responseHeaders.append("Connection: Close\r\n");
- return size*nitems;
- }
- else if(!strncasecmp("Transfer-Encoding",buffer,17)) {
- if(allow_chunked && strstr(buffer+19,"chunked")) {
- conn->chunked = true;
- //fprintf(stderr,"%s",buffer);
- conn->responseHeaders.append(buffer, size*nitems);
- }
- return size*nitems;
- }
- else if(conn->force5ch && !strncasecmp("Set-Cookie:",buffer,11)) {
- char *ptr = (char *)memmem_priv(buffer,size*nitems,"domain=",7);
- if(ptr) {
- char *end = ptr;
- while(*end != ';' && *end != '\r') end++;
- ptr = (char *)memmem_priv(ptr,end-ptr,"5ch.net",7);
- if(ptr) {
- conn->responseHeaders.append(buffer, ptr-buffer);
- conn->responseHeaders.append(1, '2');
- conn->responseHeaders.append(ptr+1, size*nitems-(ptr+1-buffer));
- return size*nitems;
- }
- }
- conn->responseHeaders.append(buffer, size*nitems);
- return size*nitems;
- }
- else {
- if (!strncasecmp("X-Chx-Error:", buffer, 12)) {
- const char *ptr = buffer + 12;
- while (*ptr == ' ') ptr++;
- log_printf(0, "Posting to bbs.cgi has been cancelled. Reason: %s", 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? */
- {
- conn->setMonaKey("", code);
- conn->status = 2;
- return 0;
- }
- }
- }
- else if (!strncasecmp("X-MonaKey:", buffer, 10)) {
- const char *ptr = buffer + 10;
- while (*ptr == ' ') ptr++;
- const char *end = ptr;
- while (*end != '\n' && *end != '\r' && *end) end++;
- conn->setMonaKey(std::string(ptr, end-ptr), 0);
- }
- conn->responseHeaders.append(buffer, size*nitems);
- //fprintf(stderr,"%s",buffer);
- if(!memcmp(buffer,"\r\n",2)) {
- fwrite(conn->responseHeaders.data(), 1, conn->responseHeaders.size(), conn->fpw);
- fflush(conn->fpw);
- conn->status = 1;
- }
- return size*nitems;
- }
- }
- static size_t write_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
- if(conn->chunked) fprintf(conn->fpw,"%lx\r\n",size*nitems);
- size_t ret = fwrite(buffer,size,nitems,conn->fpw);
- if(conn->chunked) fprintf(conn->fpw,"\r\n");
- fflush(conn->fpw);
- return ret;
- }
- static size_t header_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- DataStorage *data = reinterpret_cast<DataStorage *>(userdata);
- if(!strncasecmp("Connection",buffer,10)) {
- data->appendBytes("Connection: Close\r\n",19);
- return size*nitems;
- }
- else if(!strncasecmp("Transfer-Encoding",buffer,17)) {
- return size*nitems;
- }
- else {
- data->appendBytes(buffer,size*nitems);
- return size*nitems;
- }
- }
- static size_t write_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- DataStorage *data = reinterpret_cast<DataStorage *>(userdata);
- size_t downloaded = size*nitems;
- data->appendBytes(buffer, downloaded);
- return downloaded;
- }
- static size_t read_callback_proxy(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- BBS2chProxyConnection *conn = reinterpret_cast<BBS2chProxyConnection *>(userdata);
- if(size*nitems < 1) return 0;
- if(conn->content_length) {
- size_t bytesToRead = conn->content_length;
- if(size*nitems < conn->content_length) bytesToRead = size*nitems;
- size_t ret = fread(buffer,1,bytesToRead,conn->fpr);
- conn->content_length -= ret;
- return ret;
- }
- return 0;
- }
- static void sendBasicHeaders(int respCode, const char *respMsg, FILE *fpw)
- {
- char date[256];
- time_t now = time(0);
- strftime(date, 256, httpTimestampFmt, gmtime(&now));
- if(0 > fprintf(fpw, "HTTP/1.1 %d %s\r\n",respCode,respMsg)) return;
- if(0 > fprintf(fpw, "Connection: Close\r\n")) return;
- if(0 > fprintf(fpw, "Server: 2ch Proxy\r\n")) return;
- if(0 > fprintf(fpw, "Date: %s\r\n",date)) return;
- fflush(fpw);
- }
- static void sendResponse(int respCode, const char *respMsg, FILE *fpw)
- {
- sendBasicHeaders(respCode, respMsg, fpw);
- if(0 > fprintf(fpw, "Content-Type: text/plain; charset=UTF-8\r\n")) return;
- if(0 > fprintf(fpw, "\r\n")) return;
- if(respCode >= 400) {
- if(0 > fprintf(fpw, " ∧_∧ / ̄ ̄ ̄ ̄ ̄\n")) return;
- if(0 > fprintf(fpw, " ( ´∀`)< %s\n",respMsg)) return;
- if(0 > fprintf(fpw, " ( ) \_____\n")) return;
- if(0 > fprintf(fpw, " │ │ │\n")) return;
- if(0 > fprintf(fpw, " (__)_)\n")) return;
- }
- fflush(fpw);
- }
- static double getCurrentTime(void)
- {
- #ifdef _WIN32
- SYSTEMTIME st;
- FILETIME ft;
- GetLocalTime(&st);
- SystemTimeToFileTime(&st, &ft);
- unsigned long long now = ft.dwHighDateTime;
- now <<= 32;
- now |= ft.dwLowDateTime;
- return (double)(now * 1e-7 - 11644473600.0);
- #else
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (double)(tv.tv_sec + tv.tv_usec * 1e-6);
- #endif
- }
- static std::string decodeURIComponent(const char *input, size_t inputLength, bool decodePlus)
- {
- std::string output;
- for (int i=0;i<inputLength;i++) {
- if (input[i] == '%') {
- if (i < inputLength - 2) {
- char from[3];
- char *end;
- from[0] = input[i+1];
- from[1] = input[i+2];
- from[2] = 0;
- unsigned long n = strtoul(from, &end, 16);
- if (n < 256 && end == from+2) {
- output.append(1, n);
- i += 2;
- continue;
- }
- }
- }
- else if (decodePlus && input[i] == '+') {
- output.append(" ");
- continue;
- }
- output.append(1, input[i]);
- }
- return output;
- }
- static std::string encodeURIComponent(const char *input, size_t inputLength, bool spaceAsPlus)
- {
- std::string output;
- for (int i=0;i<inputLength;i++) {
- unsigned char c = (unsigned char)input[i];
- if ((c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c == '*') || (c == '-') || (c == '.') || (c == '_')) {
- output.append(1, c);
- }
- else if (c == ' ' && spaceAsPlus) {
- output.append("+");
- }
- else {
- char percentEncoded[4];
- snprintf(percentEncoded, 4, "%%%02X", c);
- output.append(percentEncoded);
- }
- }
- return output;
- }
- static void appendPostSignature(const char *body, const std::string &userAgent, const std::string &monaKey, curl_slist *headers)
- {
- std::map<std::string, std::string> fields;
- const char *ptr = body;
- while (1) {
- const char *tmp = ptr;
- while (*tmp != '=' && *tmp != 0) tmp++;
- if (*tmp == 0) break;
- std::string key(ptr, tmp-ptr);
- tmp++;
- ptr = tmp;
- while (*tmp != '&' && *tmp != 0) tmp++;
- fields.insert(std::make_pair(key, decodeURIComponent(ptr, tmp-ptr, true)));
- if (*tmp == 0) break;
- ptr = tmp + 1;
- }
- char nonce[32];
- snprintf(nonce, 32, "%.3f", getCurrentTime());
- std::string message;
- message.append(fields["bbs"]);
- message.append("<>");
- message.append(fields["key"]);
- message.append("<>");
- message.append(fields["time"]);
- message.append("<>");
- message.append(fields["FROM"]);
- message.append("<>");
- message.append(fields["mail"]);
- message.append("<>");
- message.append(fields["MESSAGE"]);
- message.append("<>");
- message.append(fields["subject"]);
- message.append("<>");
- message.append(userAgent);
- message.append("<>");
- message.append(monaKey);
- message.append("<>");
- message.append("<>");
- message.append(nonce);
- 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;
- char header[256];
- snprintf(header, 256, "X-APIKey: %s", appKey);
- headers = curl_slist_append(headers, header);
- log_printf(1, "Appended header \"%s\"\n", header);
- snprintf(header, 256, "X-PostSig: %s", digestStr);
- headers = curl_slist_append(headers, header);
- log_printf(1, "Appended header \"%s\"\n", header);
- snprintf(header, 256, "X-PostNonce: %s", nonce);
- headers = curl_slist_append(headers, header);
- log_printf(1, "Appended header \"%s\"\n", header);
- snprintf(header, 256, "X-MonaKey: %s", monaKey.c_str());
- headers = curl_slist_append(headers, header);
- log_printf(1, "Appended header \"%s\"\n", header);
- }
- #ifdef _WIN32
- const char * strp_weekdays[] =
- { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
- const char * strp_monthnames[] =
- { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
- bool strp_atoi(const char * & s, int & result, int low, int high, int offset)
- {
- bool worked = false;
- char * end;
- unsigned long num = strtoul(s, & end, 10);
- if (num >= (unsigned long)low && num <= (unsigned long)high)
- {
- result = (int)(num + offset);
- s = end;
- worked = true;
- }
- return worked;
- }
- char * strptime(const char *s, const char *format, struct tm *tm)
- {
- bool working = true;
- while (working && *format && *s)
- {
- switch (*format)
- {
- case '%':
- {
- ++format;
- switch (*format)
- {
- case 'a':
- case 'A': // weekday name
- tm->tm_wday = -1;
- working = false;
- for (size_t i = 0; i < 7; ++ i)
- {
- size_t len = strlen(strp_weekdays[i]);
- if (!strnicmp(strp_weekdays[i], s, len))
- {
- tm->tm_wday = i;
- s += len;
- working = true;
- break;
- }
- else if (!strnicmp(strp_weekdays[i], s, 3))
- {
- tm->tm_wday = i;
- s += 3;
- working = true;
- break;
- }
- }
- break;
- case 'b':
- case 'B':
- case 'h': // month name
- tm->tm_mon = -1;
- working = false;
- for (size_t i = 0; i < 12; ++ i)
- {
- size_t len = strlen(strp_monthnames[i]);
- if (!strnicmp(strp_monthnames[i], s, len))
- {
- tm->tm_mon = i;
- s += len;
- working = true;
- break;
- }
- else if (!strnicmp(strp_monthnames[i], s, 3))
- {
- tm->tm_mon = i;
- s += 3;
- working = true;
- break;
- }
- }
- break;
- case 'd':
- case 'e': // day of month number
- working = strp_atoi(s, tm->tm_mday, 1, 31, 0);
- break;
- case 'D': // %m/%d/%y
- {
- const char * s_save = s;
- working = strp_atoi(s, tm->tm_mon, 1, 12, -1);
- if (working && *s == '/')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_mday, 1, 31, 0);
- if (working && *s == '/')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_year, 0, 99, 0);
- if (working && tm->tm_year < 69)
- tm->tm_year += 100;
- }
- }
- if (!working)
- s = s_save;
- }
- break;
- case 'H': // hour
- working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
- break;
- case 'I': // hour 12-hour clock
- working = strp_atoi(s, tm->tm_hour, 1, 12, 0);
- break;
- case 'j': // day number of year
- working = strp_atoi(s, tm->tm_yday, 1, 366, -1);
- break;
- case 'm': // month number
- working = strp_atoi(s, tm->tm_mon, 1, 12, -1);
- break;
- case 'M': // minute
- working = strp_atoi(s, tm->tm_min, 0, 59, 0);
- break;
- case 'n': // arbitrary whitespace
- case 't':
- while (isspace((int)*s))
- ++s;
- break;
- case 'p': // am / pm
- if (!strnicmp(s, "am", 2))
- { // the hour will be 1 -> 12 maps to 12 am, 1 am .. 11 am, 12 noon 12 pm .. 11 pm
- if (tm->tm_hour == 12) // 12 am == 00 hours
- tm->tm_hour = 0;
- }
- else if (!strnicmp(s, "pm", 2))
- {
- if (tm->tm_hour < 12) // 12 pm == 12 hours
- tm->tm_hour += 12; // 1 pm -> 13 hours, 11 pm -> 23 hours
- }
- else
- working = false;
- break;
- case 'r': // 12 hour clock %I:%M:%S %p
- {
- const char * s_save = s;
- working = strp_atoi(s, tm->tm_hour, 1, 12, 0);
- if (working && *s == ':')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_min, 0, 59, 0);
- if (working && *s == ':')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
- if (working && isspace((int)*s))
- {
- ++ s;
- while (isspace((int)*s))
- ++s;
- if (!strnicmp(s, "am", 2))
- { // the hour will be 1 -> 12 maps to 12 am, 1 am .. 11 am, 12 noon 12 pm .. 11 pm
- if (tm->tm_hour == 12) // 12 am == 00 hours
- tm->tm_hour = 0;
- }
- else if (!strnicmp(s, "pm", 2))
- {
- if (tm->tm_hour < 12) // 12 pm == 12 hours
- tm->tm_hour += 12; // 1 pm -> 13 hours, 11 pm -> 23 hours
- }
- else
- working = false;
- }
- }
- }
- if (!working)
- s = s_save;
- }
- break;
- case 'R': // %H:%M
- {
- const char * s_save = s;
- working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
- if (working && *s == ':')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_min, 0, 59, 0);
- }
- if (!working)
- s = s_save;
- }
- break;
- case 'S': // seconds
- working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
- break;
- case 'T': // %H:%M:%S
- {
- const char * s_save = s;
- working = strp_atoi(s, tm->tm_hour, 0, 23, 0);
- if (working && *s == ':')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_min, 0, 59, 0);
- if (working && *s == ':')
- {
- ++ s;
- working = strp_atoi(s, tm->tm_sec, 0, 60, 0);
- }
- }
- if (!working)
- s = s_save;
- }
- break;
- case 'w': // weekday number 0->6 sunday->saturday
- working = strp_atoi(s, tm->tm_wday, 0, 6, 0);
- break;
- case 'Y': // year
- working = strp_atoi(s, tm->tm_year, 1900, 65535, -1900);
- break;
- case 'y': // 2-digit year
- working = strp_atoi(s, tm->tm_year, 0, 99, 0);
- if (working && tm->tm_year < 69)
- tm->tm_year += 100;
- break;
- case '%': // escaped
- if (*s != '%')
- working = false;
- ++s;
- break;
- default:
- working = false;
- }
- }
- break;
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- case '\f':
- case '\v':
- // zero or more whitespaces:
- while (isspace((int)*s))
- ++ s;
- break;
- default:
- // match character
- if (*s != *format)
- working = false;
- else
- ++s;
- break;
- }
- ++format;
- }
- return (working?(char *)s:0);
- }
- #endif
|