Files
znc/src/ZNCString.cpp
2025-05-09 21:54:36 +01:00

1566 lines
42 KiB
C++

/*
* 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 <znc/ZNCString.h>
#include <znc/FileUtils.h>
#include <znc/Utils.h>
#include <znc/MD5.h>
#include <znc/SHA256.h>
#include <sstream>
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 &#97; &#x3c
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 &#7777777777;
ch = (unsigned char)b;
a += iCounted;
break;
}
}
if (ch == 0) {
if (!strncasecmp((const char*)&pTmp, "&lt;", 4))
ch = '<';
else if (!strncasecmp((const char*)&pTmp, "&gt;", 4))
ch = '>';
else if (!strncasecmp((const char*)&pTmp, "&quot;", 6))
ch = '"';
else if (!strncasecmp((const char*)&pTmp, "&amp;", 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 += "&lt;";
else if (ch == '>')
sRet += "&gt;";
else if (ch == '"')
sRet += "&quot;";
else if (ch == '&')
sRet += "&amp;";
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;
}