/* * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include using std::stringstream; static constexpr unsigned char XX = 0xff; static constexpr unsigned char base64_table[256] = { XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, 62, XX, XX, XX, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, XX, XX, XX, XX, XX, XX, XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, XX, XX, XX, XX, XX, XX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, }; CString::CString(char c) : string() { stringstream s; s << c; *this = s.str(); } CString::CString(unsigned char c) : string() { stringstream s; s << c; *this = s.str(); } CString::CString(short i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(unsigned short i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(int i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(unsigned int i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(long i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(unsigned long i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(long long i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(unsigned long long i) : string() { stringstream s; s << i; *this = s.str(); } CString::CString(double i, int precision) : string() { stringstream s; s.precision(precision); s << std::fixed << i; *this = s.str(); } CString::CString(float i, int precision) : string() { stringstream s; s.precision(precision); s << std::fixed << i; *this = s.str(); } unsigned char* CString::strnchr(const unsigned char* src, unsigned char c, unsigned int iMaxBytes, unsigned char* pFill, unsigned int* piCount) const { for (unsigned int a = 0; a < iMaxBytes && *src; a++, src++) { if (pFill) { pFill[a] = *src; } if (*src == c) { if (pFill) { pFill[a + 1] = 0; } if (piCount) { *piCount = a; } return (unsigned char*)src; } } if (pFill) { *pFill = 0; } if (piCount) { *piCount = 0; } return nullptr; } int CString::CaseCmp(const CString& s, CString::size_type uLen) const { if (uLen != CString::npos) { return strncasecmp(c_str(), s.c_str(), uLen); } return strcasecmp(c_str(), s.c_str()); } int CString::StrCmp(const CString& s, CString::size_type uLen) const { if (uLen != CString::npos) { return strncmp(c_str(), s.c_str(), uLen); } return strcmp(c_str(), s.c_str()); } bool CString::Equals(const CString& s, CaseSensitivity cs) const { if (cs == CaseSensitive) { return (StrCmp(s) == 0); } else { return (CaseCmp(s) == 0); } } bool CString::Equals(const CString& s, bool bCaseSensitive, CString::size_type uLen) const { if (bCaseSensitive) { return (StrCmp(s, uLen) == 0); } else { return (CaseCmp(s, uLen) == 0); } } bool CString::WildCmp(const CString& sWild, const CString& sString, CaseSensitivity cs) { // avoid a copy when cs == CaseSensitive (C++ deliberately specifies that // binding a temporary object to a reference to const on the stack // lengthens the lifetime of the temporary to the lifetime of the reference // itself) const CString& sWld = (cs == CaseSensitive ? sWild : sWild.AsLower()); const CString& sStr = (cs == CaseSensitive ? sString : sString.AsLower()); // Written by Jack Handy - jakkhandy@hotmail.com const char* wild = sWld.c_str(), *CString = sStr.c_str(); const char* cp = nullptr, *mp = nullptr; while ((*CString) && (*wild != '*')) { if ((*wild != *CString) && (*wild != '?')) { return false; } wild++; CString++; } while (*CString) { if (*wild == '*') { if (!*++wild) { return true; } mp = wild; cp = CString + 1; } else if ((*wild == *CString) || (*wild == '?')) { wild++; CString++; } else { wild = mp; CString = cp++; } } while (*wild == '*') { wild++; } return (*wild == 0); } bool CString::WildCmp(const CString& sWild, CaseSensitivity cs) const { return CString::WildCmp(sWild, *this, cs); } CString& CString::MakeUpper() { for (char& c : *this) { // TODO use unicode c = (char)toupper(c); } return *this; } CString& CString::MakeLower() { for (char& c : *this) { // TODO use unicode c = (char)tolower(c); } return *this; } CString CString::AsUpper() const { CString sRet = *this; sRet.MakeUpper(); return sRet; } CString CString::AsLower() const { CString sRet = *this; sRet.MakeLower(); return sRet; } CString::EEscape CString::ToEscape(const CString& sEsc) { if (sEsc.Equals("ASCII")) { return EASCII; } else if (sEsc.Equals("HTML")) { return EHTML; } else if (sEsc.Equals("URL")) { return EURL; } else if (sEsc.Equals("SQL")) { return ESQL; } else if (sEsc.Equals("NAMEDFMT")) { return ENAMEDFMT; } else if (sEsc.Equals("DEBUG")) { return EDEBUG; } else if (sEsc.Equals("MSGTAG")) { return EMSGTAG; } else if (sEsc.Equals("HEXCOLON")) { return EHEXCOLON; } return EASCII; } CString CString::Escape_n(EEscape eFrom, EEscape eTo) const { CString sRet; const char szHex[] = "0123456789ABCDEF"; const unsigned char* pStart = (const unsigned char*)data(); const unsigned char* p = (const unsigned char*)data(); size_type iLength = length(); sRet.reserve(iLength * 3); unsigned char pTmp[21] = {}; unsigned int iCounted = 0; for (unsigned int a = 0; a < iLength; a++, p = pStart + a) { unsigned char ch = 0; switch (eFrom) { case EHTML: if ((*p == '&') && (strnchr((unsigned char*)p, ';', sizeof(pTmp) - 1, pTmp, &iCounted))) { // please note that we do not have any Unicode or UTF-8 // support here at all. if ((iCounted >= 3) && (pTmp[1] == '#')) { // do XML and HTML a < int base = 10; if ((pTmp[2] & 0xDF) == 'X') { base = 16; } char* endptr = nullptr; unsigned long int b = strtol((const char*)(pTmp + 2 + (base == 16)), &endptr, base); if ((*endptr == ';') && (b <= 255)) { // incase they do something like � ch = (unsigned char)b; a += iCounted; break; } } if (ch == 0) { if (!strncasecmp((const char*)&pTmp, "<", 4)) ch = '<'; else if (!strncasecmp((const char*)&pTmp, ">", 4)) ch = '>'; else if (!strncasecmp((const char*)&pTmp, """, 6)) ch = '"'; else if (!strncasecmp((const char*)&pTmp, "&", 5)) ch = '&'; } if (ch > 0) { a += iCounted; } else { ch = *p; // Not a valid escape, just record the & } } else { ch = *p; } break; case EASCII: ch = *p; break; case EURL: if (*p == '%' && (a + 2) < iLength && isxdigit(*(p + 1)) && isxdigit(*(p + 2))) { p++; if (isdigit(*p)) { ch = (unsigned char)((*p - '0') << 4); } else { ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4); } p++; if (isdigit(*p)) { ch |= (unsigned char)(*p - '0'); } else { ch |= (unsigned char)(tolower(*p) - 'a' + 10); } a += 2; } else if (pStart[a] == '+') { ch = ' '; } else { ch = *p; } break; case ESQL: if (*p != '\\' || iLength < (a + 1)) { ch = *p; } else { a++; p++; if (*p == 'n') { ch = '\n'; } else if (*p == 'r') { ch = '\r'; } else if (*p == '0') { ch = '\0'; } else if (*p == 't') { ch = '\t'; } else if (*p == 'b') { ch = '\b'; } else { ch = *p; } } break; case ENAMEDFMT: if (*p != '\\' || iLength < (a + 1)) { ch = *p; } else { a++; p++; ch = *p; } break; case EDEBUG: if (*p == '\\' && (a + 3) < iLength && *(p + 1) == 'x' && isxdigit(*(p + 2)) && isxdigit(*(p + 3))) { p += 2; if (isdigit(*p)) { ch = (unsigned char)((*p - '0') << 4); } else { ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4); } p++; if (isdigit(*p)) { ch |= (unsigned char)(*p - '0'); } else { ch |= (unsigned char)(tolower(*p) - 'a' + 10); } a += 3; } else if (*p == '\\' && a + 1 < iLength && *(p + 1) == '.') { a++; p++; ch = '\\'; } else { ch = *p; } break; case EMSGTAG: if (*p != '\\' || iLength < (a + 1)) { ch = *p; } else { a++; p++; if (*p == ':') { ch = ';'; } else if (*p == 's') { ch = ' '; } else if (*p == '0') { ch = '\0'; } else if (*p == '\\') { ch = '\\'; } else if (*p == 'r') { ch = '\r'; } else if (*p == 'n') { ch = '\n'; } else { ch = *p; } } break; case EHEXCOLON: { while (!isxdigit(*p) && a < iLength) { a++; p++; } if (a == iLength) { continue; } if (isdigit(*p)) { ch = (unsigned char)((*p - '0') << 4); } else { ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4); } a++; p++; while (!isxdigit(*p) && a < iLength) { a++; p++; } if (a == iLength) { continue; } if (isdigit(*p)) { ch |= (unsigned char)(*p - '0'); } else { ch |= (unsigned char)(tolower(*p) - 'a' + 10); } } break; } switch (eTo) { case EHTML: if (ch == '<') sRet += "<"; else if (ch == '>') sRet += ">"; else if (ch == '"') sRet += """; else if (ch == '&') sRet += "&"; else { sRet += ch; } break; case EASCII: sRet += ch; break; case EURL: if (isalnum(ch) || ch == '_' || ch == '.' || ch == '-') { sRet += ch; } else if (ch == ' ') { sRet += '+'; } else { sRet += '%'; sRet += szHex[ch >> 4]; sRet += szHex[ch & 0xf]; } break; case ESQL: if (ch == '\0') { sRet += '\\'; sRet += '0'; } else if (ch == '\n') { sRet += '\\'; sRet += 'n'; } else if (ch == '\t') { sRet += '\\'; sRet += 't'; } else if (ch == '\r') { sRet += '\\'; sRet += 'r'; } else if (ch == '\b') { sRet += '\\'; sRet += 'b'; } else if (ch == '\"') { sRet += '\\'; sRet += '\"'; } else if (ch == '\'') { sRet += '\\'; sRet += '\''; } else if (ch == '\\') { sRet += '\\'; sRet += '\\'; } else { sRet += ch; } break; case ENAMEDFMT: if (ch == '\\') { sRet += '\\'; sRet += '\\'; } else if (ch == '{') { sRet += '\\'; sRet += '{'; } else if (ch == '}') { sRet += '\\'; sRet += '}'; } else { sRet += ch; } break; case EDEBUG: if (ch < 0x20 || ch == 0x7F) { sRet += "\\x"; sRet += szHex[ch >> 4]; sRet += szHex[ch & 0xf]; } else if (ch == '\\') { sRet += "\\."; } else { sRet += ch; } break; case EMSGTAG: if (ch == ';') { sRet += '\\'; sRet += ':'; } else if (ch == ' ') { sRet += '\\'; sRet += 's'; } else if (ch == '\0') { sRet += '\\'; sRet += '0'; } else if (ch == '\\') { sRet += '\\'; sRet += '\\'; } else if (ch == '\r') { sRet += '\\'; sRet += 'r'; } else if (ch == '\n') { sRet += '\\'; sRet += 'n'; } else { sRet += ch; } break; case EHEXCOLON: { sRet += tolower(szHex[ch >> 4]); sRet += tolower(szHex[ch & 0xf]); sRet += ":"; } break; } } if (eTo == EHEXCOLON) { sRet.TrimRight(":"); } return sRet; } CString CString::Escape_n(EEscape eTo) const { return Escape_n(EASCII, eTo); } CString& CString::Escape(EEscape eFrom, EEscape eTo) { return (*this = Escape_n(eFrom, eTo)); } CString& CString::Escape(EEscape eTo) { return (*this = Escape_n(eTo)); } CString CString::Replace_n(const CString& sReplace, const CString& sWith, const CString& sLeft, const CString& sRight, bool bRemoveDelims) const { CString sRet = *this; CString::Replace(sRet, sReplace, sWith, sLeft, sRight, bRemoveDelims); return sRet; } unsigned int CString::Replace(const CString& sReplace, const CString& sWith, const CString& sLeft, const CString& sRight, bool bRemoveDelims) { return CString::Replace(*this, sReplace, sWith, sLeft, sRight, bRemoveDelims); } unsigned int CString::Replace(CString& sStr, const CString& sReplace, const CString& sWith, const CString& sLeft, const CString& sRight, bool bRemoveDelims) { unsigned int uRet = 0; CString sCopy = sStr; sStr.clear(); size_type uReplaceWidth = sReplace.length(); size_type uLeftWidth = sLeft.length(); size_type uRightWidth = sRight.length(); const char* p = sCopy.c_str(); bool bInside = false; while (*p) { if (!bInside && uLeftWidth && strncmp(p, sLeft.c_str(), uLeftWidth) == 0) { if (!bRemoveDelims) { sStr += sLeft; } p += uLeftWidth - 1; bInside = true; } else if (bInside && uRightWidth && strncmp(p, sRight.c_str(), uRightWidth) == 0) { if (!bRemoveDelims) { sStr += sRight; } p += uRightWidth - 1; bInside = false; } else if (!bInside && strncmp(p, sReplace.c_str(), uReplaceWidth) == 0) { sStr += sWith; p += uReplaceWidth - 1; uRet++; } else { sStr.append(p, 1); } p++; } return uRet; } CString CString::Token(size_t uPos, bool bRest, const CString& sSep, bool bAllowEmpty, const CString& sLeft, const CString& sRight, bool bTrimQuotes) const { VCString vsTokens; if (Split(sSep, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes) > uPos) { CString sRet; for (size_t a = uPos; a < vsTokens.size(); a++) { if (a > uPos) { sRet += sSep; } sRet += vsTokens[a]; if (!bRest) { break; } } return sRet; } return Token(uPos, bRest, sSep, bAllowEmpty); } CString CString::Token(size_t uPos, bool bRest, const CString& sSep, bool bAllowEmpty) const { const char* sep_str = sSep.c_str(); size_t sep_len = sSep.length(); const char* str = c_str(); size_t str_len = length(); size_t start_pos = 0; size_t end_pos; if (!bAllowEmpty) { while (strncmp(&str[start_pos], sep_str, sep_len) == 0) { start_pos += sep_len; } } // First, find the start of our token while (uPos != 0 && start_pos < str_len) { bool bFoundSep = false; while (strncmp(&str[start_pos], sep_str, sep_len) == 0 && (!bFoundSep || !bAllowEmpty)) { start_pos += sep_len; bFoundSep = true; } if (bFoundSep) { uPos--; } else { start_pos++; } } // String is over? if (start_pos >= str_len) return ""; // If they want everything from here on, give it to them if (bRest) { return substr(start_pos); } // Now look for the end of the token they want end_pos = start_pos; while (end_pos < str_len) { if (strncmp(&str[end_pos], sep_str, sep_len) == 0) return substr(start_pos, end_pos - start_pos); end_pos++; } // They want the last token in the string, not something in between return substr(start_pos); } CString CString::Ellipsize(unsigned int uLen) const { if (uLen >= size()) { return *this; } string sRet; // @todo this looks suspect if (uLen < 4) { for (unsigned int a = 0; a < uLen; a++) { sRet += "."; } return sRet; } sRet = substr(0, uLen - 3) + "..."; return sRet; } CString CString::Left(size_type uCount) const { uCount = (uCount > length()) ? length() : uCount; return substr(0, uCount); } CString CString::Right(size_type uCount) const { uCount = (uCount > length()) ? length() : uCount; return substr(length() - uCount, uCount); } CString::size_type CString::URLSplit(MCString& msRet) const { msRet.clear(); VCString vsPairs; Split("&", vsPairs); for (const CString& sPair : vsPairs) { msRet[sPair.Token(0, false, "=") .Escape(CString::EURL, CString::EASCII)] = sPair.Token(1, true, "=").Escape(CString::EURL, CString::EASCII); } return msRet.size(); } CString::size_type CString::OptionSplit(MCString& msRet, bool bUpperKeys) const { CString sName; CString sCopy(*this); msRet.clear(); while (!sCopy.empty()) { sName = sCopy.Token(0, false, "=", false, "\"", "\"", false).Trim_n(); sCopy = sCopy.Token(1, true, "=", false, "\"", "\"", false).TrimLeft_n(); if (sName.empty()) { continue; } VCString vsNames; sName.Split(" ", vsNames, false, "\"", "\""); for (unsigned int a = 0; a < vsNames.size(); a++) { CString sKeyName = vsNames[a]; if (bUpperKeys) { sKeyName.MakeUpper(); } if ((a + 1) == vsNames.size()) { msRet[sKeyName] = sCopy.Token(0, false, " ", false, "\"", "\""); sCopy = sCopy.Token(1, true, " ", false, "\"", "\"", false); } else { msRet[sKeyName] = ""; } } } return msRet.size(); } CString::size_type CString::QuoteSplit(VCString& vsRet) const { vsRet.clear(); return Split(" ", vsRet, false, "\"", "\"", true); } CString::size_type CString::Split(const CString& sDelim, VCString& vsRet, bool bAllowEmpty, const CString& sLeft, const CString& sRight, bool bTrimQuotes, bool bTrimWhiteSpace) const { vsRet.clear(); if (empty()) { return 0; } CString sTmp; bool bInside = false; size_type uDelimLen = sDelim.length(); size_type uLeftLen = sLeft.length(); size_type uRightLen = sRight.length(); const char* p = c_str(); if (!bAllowEmpty) { while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) { p += uDelimLen; } } while (*p) { if (uLeftLen && uRightLen && !bInside && strncasecmp(p, sLeft.c_str(), uLeftLen) == 0) { if (!bTrimQuotes) { sTmp += sLeft; } p += uLeftLen; bInside = true; continue; } if (uLeftLen && uRightLen && bInside && strncasecmp(p, sRight.c_str(), uRightLen) == 0) { if (!bTrimQuotes) { sTmp += sRight; } p += uRightLen; bInside = false; continue; } if (uDelimLen && !bInside && strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) { if (bTrimWhiteSpace) { sTmp.Trim(); } vsRet.push_back(sTmp); sTmp.clear(); p += uDelimLen; if (!bAllowEmpty) { while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) { p += uDelimLen; } } bInside = false; continue; } else { sTmp += *p; } p++; } if (!sTmp.empty()) { if (bTrimWhiteSpace) { sTmp.Trim(); } vsRet.push_back(sTmp); } return vsRet.size(); } CString::size_type CString::Split(const CString& sDelim, SCString& ssRet, bool bAllowEmpty, const CString& sLeft, const CString& sRight, bool bTrimQuotes, bool bTrimWhiteSpace) const { VCString vsTokens; Split(sDelim, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes, bTrimWhiteSpace); ssRet.clear(); for (const CString& sToken : vsTokens) { ssRet.insert(sToken); } return ssRet.size(); } CString CString::NamedFormat(const CString& sFormat, const MCString& msValues) { CString sRet; CString sKey; bool bEscape = false; bool bParam = false; const char* p = sFormat.c_str(); while (*p) { if (!bParam) { if (bEscape) { sRet += *p; bEscape = false; } else if (*p == '\\') { bEscape = true; } else if (*p == '{') { bParam = true; sKey.clear(); } else { sRet += *p; } } else { if (bEscape) { sKey += *p; bEscape = false; } else if (*p == '\\') { bEscape = true; } else if (*p == '}') { bParam = false; MCString::const_iterator it = msValues.find(sKey); if (it != msValues.end()) { sRet += (*it).second; } } else { sKey += *p; } } p++; } return sRet; } CString CString::RandomString(unsigned int uLength) { const char chars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789!?.,:;/*-+_()"; // -1 because sizeof() includes the trailing '\0' byte const size_t len = sizeof(chars) / sizeof(chars[0]) - 1; size_t p; CString sRet; for (unsigned int a = 0; a < uLength; a++) { p = (size_t)(len * (rand() / (RAND_MAX + 1.0))); sRet += chars[p]; } return sRet; } bool CString::Base64Encode(unsigned int uWrap) { CString sCopy(*this); return sCopy.Base64Encode(*this, uWrap); } unsigned long CString::Base64Decode() { CString sCopy(*this); return sCopy.Base64Decode(*this); } CString CString::Base64Encode_n(unsigned int uWrap) const { CString sRet; Base64Encode(sRet, uWrap); return sRet; } CString CString::Base64Decode_n() const { CString sRet; Base64Decode(sRet); return sRet; } bool CString::Base64Encode(CString& sRet, unsigned int uWrap) const { const char b64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; sRet.clear(); size_t len = size(); const unsigned char* input = (const unsigned char*)c_str(); unsigned char* output, *p; size_t i = 0, mod = len % 3, toalloc; toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1 + 8; if (uWrap) { toalloc += len / 57; if (len % 57) { toalloc++; } } if (toalloc < len) { return 0; } p = output = new unsigned char[toalloc]{}; while (i < len - mod) { *p++ = b64table[input[i++] >> 2]; *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f]; *p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f]; *p++ = b64table[input[i + 1] & 0x3f]; i += 2; if (uWrap && !(i % 57)) { *p++ = '\n'; } } if (!mod) { if (uWrap && i % 57) { *p++ = '\n'; } } else { *p++ = b64table[input[i++] >> 2]; *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f]; if (mod == 1) { *p++ = '='; } else { *p++ = b64table[(input[i] << 2) & 0x3f]; } *p++ = '='; if (uWrap) { *p++ = '\n'; } } *p = 0; sRet = (char*)output; delete[] output; return true; } unsigned long CString::Base64Decode(CString& sRet) const { CString sTmp(*this); // remove new lines sTmp.Replace("\r", ""); sTmp.Replace("\n", ""); const char* in = sTmp.c_str(); char c, c1, *p; unsigned long i; unsigned long uLen = sTmp.size(); char* out = new char[uLen + 1]{}; for (i = 0, p = out; i < uLen; i++) { c = (char)base64_table[(unsigned char)in[i++]]; c1 = (char)base64_table[(unsigned char)in[i++]]; *p++ = char((c << 2) | ((c1 >> 4) & 0x3)); if (i < uLen) { if (in[i] == '=') { break; } c = (char)base64_table[(unsigned char)in[i]]; *p++ = char(((c1 << 4) & 0xf0) | ((c >> 2) & 0xf)); } if (++i < uLen) { if (in[i] == '=') { break; } *p++ = char(((c << 6) & 0xc0) | (char)base64_table[(unsigned char)in[i]]); } } *p = '\0'; unsigned long uRet = p - out; sRet.clear(); sRet.append(out, uRet); delete[] out; return uRet; } CString CString::MD5() const { return (const char*)CMD5(*this); } CString CString::SHA256() const { unsigned char digest[SHA256_DIGEST_SIZE]; char digest_hex[SHA256_DIGEST_SIZE * 2 + 1]; const unsigned char* message = (const unsigned char*)c_str(); sha256(message, length(), digest); snprintf(digest_hex, sizeof(digest_hex), "%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15], digest[16], digest[17], digest[18], digest[19], digest[20], digest[21], digest[22], digest[23], digest[24], digest[25], digest[26], digest[27], digest[28], digest[29], digest[30], digest[31]); return digest_hex; } #ifdef HAVE_LIBSSL CString CString::Encrypt_n(const CString& sPass, const CString& sIvec) const { CString sRet; sRet.Encrypt(sPass, sIvec); return sRet; } CString CString::Decrypt_n(const CString& sPass, const CString& sIvec) const { CString sRet; sRet.Decrypt(sPass, sIvec); return sRet; } void CString::Encrypt(const CString& sPass, const CString& sIvec) { Crypt(sPass, true, sIvec); } void CString::Decrypt(const CString& sPass, const CString& sIvec) { Crypt(sPass, false, sIvec); } void CString::Crypt(const CString& sPass, bool bEncrypt, const CString& sIvec) { unsigned char szIvec[8] = {0, 0, 0, 0, 0, 0, 0, 0}; BF_KEY bKey; if (sIvec.length() >= 8) { memcpy(szIvec, sIvec.data(), 8); } BF_set_key(&bKey, (unsigned int)sPass.length(), (unsigned char*)sPass.data()); unsigned int uPad = (length() % 8); if (uPad) { uPad = 8 - uPad; append(uPad, '\0'); } size_t uLen = length(); unsigned char* szBuff = (unsigned char*)malloc(uLen); BF_cbc_encrypt((const unsigned char*)data(), szBuff, uLen, &bKey, szIvec, ((bEncrypt) ? BF_ENCRYPT : BF_DECRYPT)); clear(); append((const char*)szBuff, uLen); free(szBuff); } #endif // HAVE_LIBSSL CString CString::ToPercent(double d) { char szRet[32]; snprintf(szRet, 32, "%.02f%%", d); return szRet; } CString CString::ToByteStr(unsigned long long d) { const unsigned long long KiB = 1024; const unsigned long long MiB = KiB * 1024; const unsigned long long GiB = MiB * 1024; const unsigned long long TiB = GiB * 1024; if (d > TiB) { return CString(d / TiB) + " TiB"; } else if (d > GiB) { return CString(d / GiB) + " GiB"; } else if (d > MiB) { return CString(d / MiB) + " MiB"; } else if (d > KiB) { return CString(d / KiB) + " KiB"; } return CString(d) + " B"; } CString CString::ToTimeStr(unsigned long s) { const unsigned long m = 60; const unsigned long h = m * 60; const unsigned long d = h * 24; const unsigned long w = d * 7; const unsigned long y = d * 365; CString sRet; #define TIMESPAN(time, str) \ if (s >= time) { \ sRet += CString(s / time) + str " "; \ s = s % time; \ } TIMESPAN(y, "y"); TIMESPAN(w, "w"); TIMESPAN(d, "d"); TIMESPAN(h, "h"); TIMESPAN(m, "m"); TIMESPAN(1, "s"); if (sRet.empty()) return "0s"; return sRet.RightChomp_n(); } bool CString::ToBool() const { CString sTrimmed = Trim_n(); return (!sTrimmed.Trim_n("0").empty() && !sTrimmed.Equals("false") && !sTrimmed.Equals("off") && !sTrimmed.Equals("no") && !sTrimmed.Equals("n")); } short CString::ToShort() const { return (short int)strtol(this->c_str(), (char**)nullptr, 10); } unsigned short CString::ToUShort() const { return (unsigned short int)strtoul(this->c_str(), (char**)nullptr, 10); } unsigned int CString::ToUInt() const { return (unsigned int)strtoul(this->c_str(), (char**)nullptr, 10); } int CString::ToInt() const { return (int)strtol(this->c_str(), (char**)nullptr, 10); } long CString::ToLong() const { return strtol(this->c_str(), (char**)nullptr, 10); } unsigned long CString::ToULong() const { return strtoul(c_str(), nullptr, 10); } unsigned long long CString::ToULongLong() const { return strtoull(c_str(), nullptr, 10); } long long CString::ToLongLong() const { return strtoll(c_str(), nullptr, 10); } double CString::ToDouble() const { return strtod(c_str(), nullptr); } bool CString::Trim(const CString& s) { bool bLeft = TrimLeft(s); return (TrimRight(s) || bLeft); } bool CString::TrimLeft(const CString& s) { size_type i = find_first_not_of(s); if (i == 0) return false; if (i != npos) this->erase(0, i); else this->clear(); return true; } bool CString::TrimRight(const CString& s) { size_type i = find_last_not_of(s); if (i + 1 == length()) return false; if (i != npos) this->erase(i + 1, npos); else this->clear(); return true; } CString CString::Trim_n(const CString& s) const { CString sRet = *this; sRet.Trim(s); return sRet; } CString CString::TrimLeft_n(const CString& s) const { CString sRet = *this; sRet.TrimLeft(s); return sRet; } CString CString::TrimRight_n(const CString& s) const { CString sRet = *this; sRet.TrimRight(s); return sRet; } bool CString::TrimPrefix(const CString& sPrefix) { if (StartsWith(sPrefix)) { LeftChomp(sPrefix.length()); return true; } else { return false; } } bool CString::TrimSuffix(const CString& sSuffix) { if (Right(sSuffix.length()).Equals(sSuffix)) { RightChomp(sSuffix.length()); return true; } else { return false; } } size_t CString::Find(const CString& s, CaseSensitivity cs) const { if (cs == CaseSensitive) { return find(s); } else { return AsLower().find(s.AsLower()); } } bool CString::StartsWith(const CString& sPrefix, CaseSensitivity cs) const { return Left(sPrefix.length()).Equals(sPrefix, cs); } bool CString::EndsWith(const CString& sSuffix, CaseSensitivity cs) const { return Right(sSuffix.length()).Equals(sSuffix, cs); } bool CString::Contains(const CString& s, CaseSensitivity cs) const { return Find(s, cs) != npos; } CString CString::TrimPrefix_n(const CString& sPrefix) const { CString sRet = *this; sRet.TrimPrefix(sPrefix); return sRet; } CString CString::TrimSuffix_n(const CString& sSuffix) const { CString sRet = *this; sRet.TrimSuffix(sSuffix); return sRet; } CString CString::LeftChomp_n(size_type uLen) const { CString sRet = *this; sRet.LeftChomp(uLen); return sRet; } CString CString::RightChomp_n(size_type uLen) const { CString sRet = *this; sRet.RightChomp(uLen); return sRet; } bool CString::LeftChomp(size_type uLen) { bool bRet = false; while ((uLen--) && (length())) { erase(0, 1); bRet = true; } return bRet; } bool CString::RightChomp(size_type uLen) { bool bRet = false; while ((uLen--) && (length())) { erase(length() - 1); bRet = true; } return bRet; } CString CString::StripControls_n() const { CString sRet; const unsigned char* pStart = (const unsigned char*)data(); unsigned char ch = *pStart; size_type iLength = length(); sRet.reserve(iLength); bool colorCode = false; unsigned int digits = 0; bool comma = false; for (unsigned int a = 0; a < iLength; a++, ch = pStart[a]) { // Color code. Format: \x03([0-9]{1,2}(,[0-9]{1,2})?)? if (ch == 0x03) { colorCode = true; digits = 0; comma = false; continue; } if (colorCode) { if (isdigit(ch) && digits < 2) { digits++; continue; } if (ch == ',' && !comma) { comma = true; digits = 0; continue; } colorCode = false; if (digits == 0 && comma) { // There was a ',' which wasn't followed by digits, we should // print it. sRet += ','; } } // CO controls codes if (ch < 0x20 || ch == 0x7F) continue; sRet += ch; } if (colorCode && digits == 0 && comma) { sRet += ','; } sRet.reserve(0); return sRet; } CString& CString::StripControls() { return (*this = StripControls_n()); } //////////////// MCString //////////////// const MCString MCString::EmptyMap; MCString::status_t MCString::WriteToDisk(const CString& sPath, mode_t iMode) const { CFile cFile(sPath); if (this->empty()) { if (!cFile.Exists()) return MCS_SUCCESS; if (cFile.Delete()) return MCS_SUCCESS; } if (!cFile.Open(O_WRONLY | O_CREAT | O_TRUNC, iMode)) { return MCS_EOPEN; } for (const auto& it : *this) { CString sKey = it.first; CString sValue = it.second; if (!WriteFilter(sKey, sValue)) { return MCS_EWRITEFIL; } if (sKey.empty()) { continue; } if (cFile.Write(Encode(sKey) + " " + Encode(sValue) + "\n") <= 0) { return MCS_EWRITE; } } cFile.Close(); return MCS_SUCCESS; } MCString::status_t MCString::ReadFromDisk(const CString& sPath) { clear(); CFile cFile(sPath); if (!cFile.Open(O_RDONLY)) { return MCS_EOPEN; } CString sBuffer; while (cFile.ReadLine(sBuffer)) { sBuffer.Trim(); CString sKey = sBuffer.Token(0); CString sValue = sBuffer.Token(1); Decode(sKey); Decode(sValue); if (!ReadFilter(sKey, sValue)) return MCS_EREADFIL; (*this)[sKey] = sValue; } cFile.Close(); return MCS_SUCCESS; } static const char hexdigits[] = "0123456789abcdef"; CString& MCString::Encode(CString& sValue) const { CString sTmp; for (unsigned char c : sValue) { // isalnum() needs unsigned char as argument and this code // assumes unsigned, too. if (isalnum(c)) { sTmp += c; } else { sTmp += "%"; sTmp += hexdigits[c >> 4]; sTmp += hexdigits[c & 0xf]; sTmp += ";"; } } sValue = sTmp; return sValue; } CString& MCString::Decode(CString& sValue) const { const char* pTmp = sValue.c_str(); char* endptr; CString sTmp; while (*pTmp) { if (*pTmp != '%') { sTmp += *pTmp++; } else { char ch = (char)strtol(pTmp + 1, &endptr, 16); if (*endptr == ';') { sTmp += ch; pTmp = ++endptr; } else { sTmp += *pTmp++; } } } sValue = sTmp; return sValue; }