123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- #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 > 0) return size*nitems;
- else if(conn->status == -1) {
- if(!memcmp(buffer,"\r\n",2)) {
- conn->status = 0;
- }
- return size*nitems;
- }
- if(!strncasecmp("HTTP/", buffer, 5) && !strchr(buffer, ':')) {
- const char *ptr = buffer + 5;
- const char *end = buffer + size*nitems;
- while(*ptr != ' ' && ptr < end) ptr++;
- while(*ptr == ' ' && ptr < end) ptr++;
- if(ptr < end) {
- int code = atoi(ptr);
- if (code == 100) {
- conn->status = -1;
- return size*nitems;
- }
- }
- }
-
- if(!strncasecmp("Connection",buffer,10)) {
- conn->socketToClient->writeString("Connection: Close\r\n");
- return size*nitems;
- }
- else if(!strncasecmp("Transfer-Encoding",buffer,17)) {
- if(allow_chunked && !conn->isClientHttp1_0 && strstr(buffer+19,"chunked")) {
- conn->isResponseChunked = true;
- //fprintf(stderr,"%s",buffer);
- size_t ret = conn->socketToClient->write(buffer, size*nitems);
- 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) {
- conn->socketToClient->write(buffer, ptr-buffer);
- conn->socketToClient->write("2", 1);
- conn->socketToClient->write(ptr+1, size*nitems-(ptr+1-buffer));
- return size*nitems;
- }
- }
- return conn->socketToClient->write(buffer, size*nitems);
- }
- 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 = conn->socketToClient->write(buffer, size*nitems);
- //fprintf(stderr,"%s",buffer);
- if(!memcmp(buffer,"\r\n",2)) {
- 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 > 0) return size*nitems;
- else if(conn->status == -1) {
- if(!memcmp(buffer,"\r\n",2)) {
- conn->status = 0;
- }
- return size*nitems;
- }
- if(!strncasecmp("HTTP/", buffer, 5) && !strchr(buffer, ':')) {
- const char *ptr = buffer + 5;
- const char *end = buffer + size*nitems;
- while(*ptr != ' ' && ptr < end) ptr++;
- while(*ptr == ' ' && ptr < end) ptr++;
- if(ptr < end) {
- int code = atoi(ptr);
- if (code == 100) {
- conn->status = -1;
- 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 && !conn->isClientHttp1_0 && strstr(buffer+19,"chunked")) {
- conn->isResponseChunked = 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)) {
- conn->socketToClient->write(conn->responseHeaders.data(), conn->responseHeaders.size());
- 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->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(conn->isResponseChunked) conn->socketToClient->writeString("\r\n");
- 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->isClientChunked) {
- size_t chunkSize;
- char tmp[64];
- if (!conn->content_length) {
- if (!conn->socketToClient->readLine(tmp, 64)) return 0;
- chunkSize = strtol(tmp, NULL, 16);
- if (chunkSize == 0) {
- return 0;
- }
- }
- else {
- chunkSize = conn->content_length;
- conn->content_length = 0;
- }
- if (chunkSize <= size*nitems) {
- size_t ret = conn->socketToClient->read(buffer, chunkSize);
- if (ret <= 0) return 0;
- conn->socketToClient->readLine(tmp, 64);
- return ret;
- }
- else {
- size_t ret = conn->socketToClient->read(buffer, size*nitems);
- if (ret <= 0) return 0;
- conn->content_length = chunkSize - ret;
- return ret;
- }
- }
- else if (conn->content_length) {
- size_t bytesToRead = conn->content_length;
- if (size*nitems < conn->content_length) bytesToRead = size*nitems;
- size_t ret = conn->socketToClient->read(buffer, bytesToRead);
- conn->content_length -= ret;
- return ret;
- }
- return 0;
- }
- static void sendBasicHeaders(int respCode, const char *respMsg, IBBS2chProxySocket *socket)
- {
- char date[256];
- time_t now = time(0);
- strftime(date, 256, httpTimestampFmt, gmtime(&now));
- std::ostringstream ss;
- ss << "HTTP/1.1 " << respCode << " " << respMsg << "\r\n";
- if(0 >= socket->writeString(ss.str())) return;
- if(0 >= socket->writeString("Connection: Close\r\n")) return;
- if(0 >= socket->writeString("Server: 2ch Proxy\r\n")) return;
- if(0 >= socket->writeString(std::string("Date: ") + date + "\r\n")) return;
- }
- static void sendResponse(int respCode, const char *respMsg, IBBS2chProxySocket *socket)
- {
- sendBasicHeaders(respCode, respMsg, socket);
- if(0 >= socket->writeString("Content-Type: text/plain; charset=UTF-8\r\n")) return;
- if(0 >= socket->writeString("\r\n")) return;
- if(respCode >= 400) {
- if(0 >= socket->writeString(" ∧_∧ / ̄ ̄ ̄ ̄ ̄\n")) return;
- if(0 >= socket->writeString(std::string(" ( ´∀`)< ") + respMsg + "\n")) return;
- if(0 >= socket->writeString(" ( ) \_____\n")) return;
- if(0 >= socket->writeString(" │ │ │\n")) return;
- if(0 >= socket->writeString(" (__)_)\n")) return;
- }
- }
- static double getCurrentTime(void)
- {
- #ifdef _WIN32
- FILETIME ft;
- GetSystemTimeAsFileTime(&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 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;
- }
- }
- return true;
- }
- static void appendPostSignature(const char *body, const std::string &userAgent, const std::string &monaKey, BBS2chProxyHttpHeaders *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++;
- std::string deocdedValue = decodeURIComponent(ptr, tmp-ptr, true);
- fields.insert(std::make_pair(key, deocdedValue));
- 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;
- 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());
- }
- static std::string convertBodyToUTF8(const char *body)
- {
- std::string newBody;
- bool shouldConvertToUTF8 = true;
- bool shouldCheckWholeBody = true;
- const char *ptr = strstr(body, "submit=");
- if (ptr && (ptr == body || *(ptr-1) == '&')) {
- ptr += 7;
- const char *start = ptr;
- while (*ptr != '&' && *ptr != 0) ptr++;
- std::string decoded = decodeURIComponent(start, ptr-start, true);
- if (decoded.length() != (ptr-start)) {
- if (isValidAsUTF8(decoded.data(), decoded.length())) {
- shouldConvertToUTF8 = false;
- }
- shouldCheckWholeBody = false;
- }
- }
- if (shouldCheckWholeBody) {
- std::string decoded = decodeURIComponent(body, strlen(body), true);
- if (isValidAsUTF8(decoded.data(), decoded.length())) {
- shouldConvertToUTF8 = false;
- }
- }
- if (shouldConvertToUTF8) {
- 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++;
- std::string deocdedValue = decodeURIComponent(ptr, tmp-ptr, true);
- if (!deocdedValue.empty()) {
- char *converted = convertShiftJISToUTF8(deocdedValue.data(), deocdedValue.length());
- if (converted) {
- deocdedValue = std::string(converted);
- free(converted);
- }
- }
- if (!newBody.empty()) newBody.append("&");
- newBody.append(key);
- newBody.append("=");
- newBody.append(encodeURIComponent(deocdedValue.data(), deocdedValue.length(), true));
- if (*tmp == 0) break;
- ptr = tmp + 1;
- }
- }
- return newBody;
- }
- #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
|