1 /*
2  * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <znc/ZNCString.h>
18 #include <znc/FileUtils.h>
19 #include <znc/Utils.h>
20 #include <znc/MD5.h>
21 #include <znc/SHA256.h>
22 #include <sstream>
23 
24 using std::stringstream;
25 
CString(char c)26 CString::CString(char c) : string() {
27     stringstream s;
28     s << c;
29     *this = s.str();
30 }
CString(unsigned char c)31 CString::CString(unsigned char c) : string() {
32     stringstream s;
33     s << c;
34     *this = s.str();
35 }
CString(short i)36 CString::CString(short i) : string() {
37     stringstream s;
38     s << i;
39     *this = s.str();
40 }
CString(unsigned short i)41 CString::CString(unsigned short i) : string() {
42     stringstream s;
43     s << i;
44     *this = s.str();
45 }
CString(int i)46 CString::CString(int i) : string() {
47     stringstream s;
48     s << i;
49     *this = s.str();
50 }
CString(unsigned int i)51 CString::CString(unsigned int i) : string() {
52     stringstream s;
53     s << i;
54     *this = s.str();
55 }
CString(long i)56 CString::CString(long i) : string() {
57     stringstream s;
58     s << i;
59     *this = s.str();
60 }
CString(unsigned long i)61 CString::CString(unsigned long i) : string() {
62     stringstream s;
63     s << i;
64     *this = s.str();
65 }
CString(long long i)66 CString::CString(long long i) : string() {
67     stringstream s;
68     s << i;
69     *this = s.str();
70 }
CString(unsigned long long i)71 CString::CString(unsigned long long i) : string() {
72     stringstream s;
73     s << i;
74     *this = s.str();
75 }
CString(double i,int precision)76 CString::CString(double i, int precision) : string() {
77     stringstream s;
78     s.precision(precision);
79     s << std::fixed << i;
80     *this = s.str();
81 }
CString(float i,int precision)82 CString::CString(float i, int precision) : string() {
83     stringstream s;
84     s.precision(precision);
85     s << std::fixed << i;
86     *this = s.str();
87 }
88 
strnchr(const unsigned char * src,unsigned char c,unsigned int iMaxBytes,unsigned char * pFill,unsigned int * piCount) const89 unsigned char* CString::strnchr(const unsigned char* src, unsigned char c,
90                                 unsigned int iMaxBytes, unsigned char* pFill,
91                                 unsigned int* piCount) const {
92     for (unsigned int a = 0; a < iMaxBytes && *src; a++, src++) {
93         if (pFill) {
94             pFill[a] = *src;
95         }
96 
97         if (*src == c) {
98             if (pFill) {
99                 pFill[a + 1] = 0;
100             }
101 
102             if (piCount) {
103                 *piCount = a;
104             }
105 
106             return (unsigned char*)src;
107         }
108     }
109 
110     if (pFill) {
111         *pFill = 0;
112     }
113 
114     if (piCount) {
115         *piCount = 0;
116     }
117 
118     return nullptr;
119 }
120 
CaseCmp(const CString & s,CString::size_type uLen) const121 int CString::CaseCmp(const CString& s, CString::size_type uLen) const {
122     if (uLen != CString::npos) {
123         return strncasecmp(c_str(), s.c_str(), uLen);
124     }
125     return strcasecmp(c_str(), s.c_str());
126 }
127 
StrCmp(const CString & s,CString::size_type uLen) const128 int CString::StrCmp(const CString& s, CString::size_type uLen) const {
129     if (uLen != CString::npos) {
130         return strncmp(c_str(), s.c_str(), uLen);
131     }
132     return strcmp(c_str(), s.c_str());
133 }
134 
Equals(const CString & s,CaseSensitivity cs) const135 bool CString::Equals(const CString& s, CaseSensitivity cs) const {
136     if (cs == CaseSensitive) {
137         return (StrCmp(s) == 0);
138     } else {
139         return (CaseCmp(s) == 0);
140     }
141 }
142 
Equals(const CString & s,bool bCaseSensitive,CString::size_type uLen) const143 bool CString::Equals(const CString& s, bool bCaseSensitive,
144                      CString::size_type uLen) const {
145     if (bCaseSensitive) {
146         return (StrCmp(s, uLen) == 0);
147     } else {
148         return (CaseCmp(s, uLen) == 0);
149     }
150 }
151 
WildCmp(const CString & sWild,const CString & sString,CaseSensitivity cs)152 bool CString::WildCmp(const CString& sWild, const CString& sString,
153                       CaseSensitivity cs) {
154     // avoid a copy when cs == CaseSensitive (C++ deliberately specifies that
155     // binding a temporary object to a reference to const on the stack
156     // lengthens the lifetime of the temporary to the lifetime of the reference
157     // itself)
158     const CString& sWld = (cs == CaseSensitive ? sWild : sWild.AsLower());
159     const CString& sStr = (cs == CaseSensitive ? sString : sString.AsLower());
160 
161     // Written by Jack Handy - jakkhandy@hotmail.com
162     const char* wild = sWld.c_str(), *CString = sStr.c_str();
163     const char* cp = nullptr, *mp = nullptr;
164 
165     while ((*CString) && (*wild != '*')) {
166         if ((*wild != *CString) && (*wild != '?')) {
167             return false;
168         }
169 
170         wild++;
171         CString++;
172     }
173 
174     while (*CString) {
175         if (*wild == '*') {
176             if (!*++wild) {
177                 return true;
178             }
179 
180             mp = wild;
181             cp = CString + 1;
182         } else if ((*wild == *CString) || (*wild == '?')) {
183             wild++;
184             CString++;
185         } else {
186             wild = mp;
187             CString = cp++;
188         }
189     }
190 
191     while (*wild == '*') {
192         wild++;
193     }
194 
195     return (*wild == 0);
196 }
197 
WildCmp(const CString & sWild,CaseSensitivity cs) const198 bool CString::WildCmp(const CString& sWild, CaseSensitivity cs) const {
199     return CString::WildCmp(sWild, *this, cs);
200 }
201 
MakeUpper()202 CString& CString::MakeUpper() {
203     for (char& c : *this) {
204         // TODO use unicode
205         c = (char)toupper(c);
206     }
207 
208     return *this;
209 }
210 
MakeLower()211 CString& CString::MakeLower() {
212     for (char& c : *this) {
213         // TODO use unicode
214         c = (char)tolower(c);
215     }
216 
217     return *this;
218 }
219 
AsUpper() const220 CString CString::AsUpper() const {
221     CString sRet = *this;
222     sRet.MakeUpper();
223     return sRet;
224 }
225 
AsLower() const226 CString CString::AsLower() const {
227     CString sRet = *this;
228     sRet.MakeLower();
229     return sRet;
230 }
231 
ToEscape(const CString & sEsc)232 CString::EEscape CString::ToEscape(const CString& sEsc) {
233     if (sEsc.Equals("ASCII")) {
234         return EASCII;
235     } else if (sEsc.Equals("HTML")) {
236         return EHTML;
237     } else if (sEsc.Equals("URL")) {
238         return EURL;
239     } else if (sEsc.Equals("SQL")) {
240         return ESQL;
241     } else if (sEsc.Equals("NAMEDFMT")) {
242         return ENAMEDFMT;
243     } else if (sEsc.Equals("DEBUG")) {
244         return EDEBUG;
245     } else if (sEsc.Equals("MSGTAG")) {
246         return EMSGTAG;
247     } else if (sEsc.Equals("HEXCOLON")) {
248         return EHEXCOLON;
249     }
250 
251     return EASCII;
252 }
253 
Escape_n(EEscape eFrom,EEscape eTo) const254 CString CString::Escape_n(EEscape eFrom, EEscape eTo) const {
255     CString sRet;
256     const char szHex[] = "0123456789ABCDEF";
257     const unsigned char* pStart = (const unsigned char*)data();
258     const unsigned char* p = (const unsigned char*)data();
259     size_type iLength = length();
260     sRet.reserve(iLength * 3);
261     unsigned char pTmp[21] = {};
262     unsigned int iCounted = 0;
263 
264     for (unsigned int a = 0; a < iLength; a++, p = pStart + a) {
265         unsigned char ch = 0;
266 
267         switch (eFrom) {
268             case EHTML:
269                 if ((*p == '&') &&
270                     (strnchr((unsigned char*)p, ';', sizeof(pTmp) - 1, pTmp,
271                              &iCounted))) {
272                     // please note that we do not have any Unicode or UTF-8
273                     // support here at all.
274 
275                     if ((iCounted >= 3) &&
276                         (pTmp[1] == '#')) {  // do XML and HTML &#97; &#x3c
277                         int base = 10;
278 
279                         if ((pTmp[2] & 0xDF) == 'X') {
280                             base = 16;
281                         }
282 
283                         char* endptr = nullptr;
284                         unsigned long int b =
285                             strtol((const char*)(pTmp + 2 + (base == 16)),
286                                    &endptr, base);
287 
288                         if ((*endptr == ';') && (b <= 255)) {
289                             // incase they do something like &#7777777777;
290                             ch = (unsigned char)b;
291                             a += iCounted;
292                             break;
293                         }
294                     }
295 
296                     if (ch == 0) {
297                         if (!strncasecmp((const char*)&pTmp, "&lt;", 4))
298                             ch = '<';
299                         else if (!strncasecmp((const char*)&pTmp, "&gt;", 4))
300                             ch = '>';
301                         else if (!strncasecmp((const char*)&pTmp, "&quot;", 6))
302                             ch = '"';
303                         else if (!strncasecmp((const char*)&pTmp, "&amp;", 5))
304                             ch = '&';
305                     }
306 
307                     if (ch > 0) {
308                         a += iCounted;
309                     } else {
310                         ch = *p;  // Not a valid escape, just record the &
311                     }
312                 } else {
313                     ch = *p;
314                 }
315                 break;
316             case EASCII:
317                 ch = *p;
318                 break;
319             case EURL:
320                 if (*p == '%' && (a + 2) < iLength && isxdigit(*(p + 1)) &&
321                     isxdigit(*(p + 2))) {
322                     p++;
323                     if (isdigit(*p)) {
324                         ch = (unsigned char)((*p - '0') << 4);
325                     } else {
326                         ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
327                     }
328 
329                     p++;
330                     if (isdigit(*p)) {
331                         ch |= (unsigned char)(*p - '0');
332                     } else {
333                         ch |= (unsigned char)(tolower(*p) - 'a' + 10);
334                     }
335 
336                     a += 2;
337                 } else if (pStart[a] == '+') {
338                     ch = ' ';
339                 } else {
340                     ch = *p;
341                 }
342 
343                 break;
344             case ESQL:
345                 if (*p != '\\' || iLength < (a + 1)) {
346                     ch = *p;
347                 } else {
348                     a++;
349                     p++;
350 
351                     if (*p == 'n') {
352                         ch = '\n';
353                     } else if (*p == 'r') {
354                         ch = '\r';
355                     } else if (*p == '0') {
356                         ch = '\0';
357                     } else if (*p == 't') {
358                         ch = '\t';
359                     } else if (*p == 'b') {
360                         ch = '\b';
361                     } else {
362                         ch = *p;
363                     }
364                 }
365 
366                 break;
367             case ENAMEDFMT:
368                 if (*p != '\\' || iLength < (a + 1)) {
369                     ch = *p;
370                 } else {
371                     a++;
372                     p++;
373                     ch = *p;
374                 }
375 
376                 break;
377             case EDEBUG:
378                 if (*p == '\\' && (a + 3) < iLength && *(p + 1) == 'x' &&
379                     isxdigit(*(p + 2)) && isxdigit(*(p + 3))) {
380                     p += 2;
381                     if (isdigit(*p)) {
382                         ch = (unsigned char)((*p - '0') << 4);
383                     } else {
384                         ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
385                     }
386 
387                     p++;
388                     if (isdigit(*p)) {
389                         ch |= (unsigned char)(*p - '0');
390                     } else {
391                         ch |= (unsigned char)(tolower(*p) - 'a' + 10);
392                     }
393 
394                     a += 3;
395                 } else if (*p == '\\' && a + 1 < iLength && *(p + 1) == '.') {
396                     a++;
397                     p++;
398                     ch = '\\';
399                 } else {
400                     ch = *p;
401                 }
402 
403                 break;
404             case EMSGTAG:
405                 if (*p != '\\' || iLength < (a + 1)) {
406                     ch = *p;
407                 } else {
408                     a++;
409                     p++;
410 
411                     if (*p == ':') {
412                         ch = ';';
413                     } else if (*p == 's') {
414                         ch = ' ';
415                     } else if (*p == '0') {
416                         ch = '\0';
417                     } else if (*p == '\\') {
418                         ch = '\\';
419                     } else if (*p == 'r') {
420                         ch = '\r';
421                     } else if (*p == 'n') {
422                         ch = '\n';
423                     } else {
424                         ch = *p;
425                     }
426                 }
427 
428                 break;
429             case EHEXCOLON: {
430                 while (!isxdigit(*p) && a < iLength) {
431                     a++;
432                     p++;
433                 }
434                 if (a == iLength) {
435                     continue;
436                 }
437                 if (isdigit(*p)) {
438                     ch = (unsigned char)((*p - '0') << 4);
439                 } else {
440                     ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
441                 }
442                 a++;
443                 p++;
444                 while (!isxdigit(*p) && a < iLength) {
445                     a++;
446                     p++;
447                 }
448                 if (a == iLength) {
449                     continue;
450                 }
451                 if (isdigit(*p)) {
452                     ch |= (unsigned char)(*p - '0');
453                 } else {
454                     ch |= (unsigned char)(tolower(*p) - 'a' + 10);
455                 }
456             } break;
457         }
458 
459         switch (eTo) {
460             case EHTML:
461                 if (ch == '<')
462                     sRet += "&lt;";
463                 else if (ch == '>')
464                     sRet += "&gt;";
465                 else if (ch == '"')
466                     sRet += "&quot;";
467                 else if (ch == '&')
468                     sRet += "&amp;";
469                 else {
470                     sRet += ch;
471                 }
472 
473                 break;
474             case EASCII:
475                 sRet += ch;
476                 break;
477             case EURL:
478                 if (isalnum(ch) || ch == '_' || ch == '.' || ch == '-') {
479                     sRet += ch;
480                 } else if (ch == ' ') {
481                     sRet += '+';
482                 } else {
483                     sRet += '%';
484                     sRet += szHex[ch >> 4];
485                     sRet += szHex[ch & 0xf];
486                 }
487 
488                 break;
489             case ESQL:
490                 if (ch == '\0') {
491                     sRet += '\\';
492                     sRet += '0';
493                 } else if (ch == '\n') {
494                     sRet += '\\';
495                     sRet += 'n';
496                 } else if (ch == '\t') {
497                     sRet += '\\';
498                     sRet += 't';
499                 } else if (ch == '\r') {
500                     sRet += '\\';
501                     sRet += 'r';
502                 } else if (ch == '\b') {
503                     sRet += '\\';
504                     sRet += 'b';
505                 } else if (ch == '\"') {
506                     sRet += '\\';
507                     sRet += '\"';
508                 } else if (ch == '\'') {
509                     sRet += '\\';
510                     sRet += '\'';
511                 } else if (ch == '\\') {
512                     sRet += '\\';
513                     sRet += '\\';
514                 } else {
515                     sRet += ch;
516                 }
517 
518                 break;
519             case ENAMEDFMT:
520                 if (ch == '\\') {
521                     sRet += '\\';
522                     sRet += '\\';
523                 } else if (ch == '{') {
524                     sRet += '\\';
525                     sRet += '{';
526                 } else if (ch == '}') {
527                     sRet += '\\';
528                     sRet += '}';
529                 } else {
530                     sRet += ch;
531                 }
532 
533                 break;
534             case EDEBUG:
535                 if (ch < 0x20 || ch == 0x7F) {
536                     sRet += "\\x";
537                     sRet += szHex[ch >> 4];
538                     sRet += szHex[ch & 0xf];
539                 } else if (ch == '\\') {
540                     sRet += "\\.";
541                 } else {
542                     sRet += ch;
543                 }
544 
545                 break;
546             case EMSGTAG:
547                 if (ch == ';') {
548                     sRet += '\\';
549                     sRet += ':';
550                 } else if (ch == ' ') {
551                     sRet += '\\';
552                     sRet += 's';
553                 } else if (ch == '\0') {
554                     sRet += '\\';
555                     sRet += '0';
556                 } else if (ch == '\\') {
557                     sRet += '\\';
558                     sRet += '\\';
559                 } else if (ch == '\r') {
560                     sRet += '\\';
561                     sRet += 'r';
562                 } else if (ch == '\n') {
563                     sRet += '\\';
564                     sRet += 'n';
565                 } else {
566                     sRet += ch;
567                 }
568 
569                 break;
570             case EHEXCOLON: {
571                 sRet += tolower(szHex[ch >> 4]);
572                 sRet += tolower(szHex[ch & 0xf]);
573                 sRet += ":";
574             } break;
575         }
576     }
577 
578     if (eTo == EHEXCOLON) {
579         sRet.TrimRight(":");
580     }
581 
582     return sRet;
583 }
584 
Escape_n(EEscape eTo) const585 CString CString::Escape_n(EEscape eTo) const { return Escape_n(EASCII, eTo); }
586 
Escape(EEscape eFrom,EEscape eTo)587 CString& CString::Escape(EEscape eFrom, EEscape eTo) {
588     return (*this = Escape_n(eFrom, eTo));
589 }
590 
Escape(EEscape eTo)591 CString& CString::Escape(EEscape eTo) { return (*this = Escape_n(eTo)); }
592 
Replace_n(const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims) const593 CString CString::Replace_n(const CString& sReplace, const CString& sWith,
594                            const CString& sLeft, const CString& sRight,
595                            bool bRemoveDelims) const {
596     CString sRet = *this;
597     CString::Replace(sRet, sReplace, sWith, sLeft, sRight, bRemoveDelims);
598     return sRet;
599 }
600 
Replace(const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims)601 unsigned int CString::Replace(const CString& sReplace, const CString& sWith,
602                               const CString& sLeft, const CString& sRight,
603                               bool bRemoveDelims) {
604     return CString::Replace(*this, sReplace, sWith, sLeft, sRight,
605                             bRemoveDelims);
606 }
607 
Replace(CString & sStr,const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims)608 unsigned int CString::Replace(CString& sStr, const CString& sReplace,
609                               const CString& sWith, const CString& sLeft,
610                               const CString& sRight, bool bRemoveDelims) {
611     unsigned int uRet = 0;
612     CString sCopy = sStr;
613     sStr.clear();
614 
615     size_type uReplaceWidth = sReplace.length();
616     size_type uLeftWidth = sLeft.length();
617     size_type uRightWidth = sRight.length();
618     const char* p = sCopy.c_str();
619     bool bInside = false;
620 
621     while (*p) {
622         if (!bInside && uLeftWidth &&
623             strncmp(p, sLeft.c_str(), uLeftWidth) == 0) {
624             if (!bRemoveDelims) {
625                 sStr += sLeft;
626             }
627 
628             p += uLeftWidth - 1;
629             bInside = true;
630         } else if (bInside && uRightWidth &&
631                    strncmp(p, sRight.c_str(), uRightWidth) == 0) {
632             if (!bRemoveDelims) {
633                 sStr += sRight;
634             }
635 
636             p += uRightWidth - 1;
637             bInside = false;
638         } else if (!bInside &&
639                    strncmp(p, sReplace.c_str(), uReplaceWidth) == 0) {
640             sStr += sWith;
641             p += uReplaceWidth - 1;
642             uRet++;
643         } else {
644             sStr.append(p, 1);
645         }
646 
647         p++;
648     }
649 
650     return uRet;
651 }
652 
Token(size_t uPos,bool bRest,const CString & sSep,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes) const653 CString CString::Token(size_t uPos, bool bRest, const CString& sSep,
654                        bool bAllowEmpty, const CString& sLeft,
655                        const CString& sRight, bool bTrimQuotes) const {
656     VCString vsTokens;
657     if (Split(sSep, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes) > uPos) {
658         CString sRet;
659 
660         for (size_t a = uPos; a < vsTokens.size(); a++) {
661             if (a > uPos) {
662                 sRet += sSep;
663             }
664 
665             sRet += vsTokens[a];
666 
667             if (!bRest) {
668                 break;
669             }
670         }
671 
672         return sRet;
673     }
674 
675     return Token(uPos, bRest, sSep, bAllowEmpty);
676 }
677 
Token(size_t uPos,bool bRest,const CString & sSep,bool bAllowEmpty) const678 CString CString::Token(size_t uPos, bool bRest, const CString& sSep,
679                        bool bAllowEmpty) const {
680     const char* sep_str = sSep.c_str();
681     size_t sep_len = sSep.length();
682     const char* str = c_str();
683     size_t str_len = length();
684     size_t start_pos = 0;
685     size_t end_pos;
686 
687     if (!bAllowEmpty) {
688         while (strncmp(&str[start_pos], sep_str, sep_len) == 0) {
689             start_pos += sep_len;
690         }
691     }
692 
693     // First, find the start of our token
694     while (uPos != 0 && start_pos < str_len) {
695         bool bFoundSep = false;
696 
697         while (strncmp(&str[start_pos], sep_str, sep_len) == 0 &&
698                (!bFoundSep || !bAllowEmpty)) {
699             start_pos += sep_len;
700             bFoundSep = true;
701         }
702 
703         if (bFoundSep) {
704             uPos--;
705         } else {
706             start_pos++;
707         }
708     }
709 
710     // String is over?
711     if (start_pos >= str_len) return "";
712 
713     // If they want everything from here on, give it to them
714     if (bRest) {
715         return substr(start_pos);
716     }
717 
718     // Now look for the end of the token they want
719     end_pos = start_pos;
720     while (end_pos < str_len) {
721         if (strncmp(&str[end_pos], sep_str, sep_len) == 0)
722             return substr(start_pos, end_pos - start_pos);
723 
724         end_pos++;
725     }
726 
727     // They want the last token in the string, not something in between
728     return substr(start_pos);
729 }
730 
Ellipsize(unsigned int uLen) const731 CString CString::Ellipsize(unsigned int uLen) const {
732     if (uLen >= size()) {
733         return *this;
734     }
735 
736     string sRet;
737 
738     // @todo this looks suspect
739     if (uLen < 4) {
740         for (unsigned int a = 0; a < uLen; a++) {
741             sRet += ".";
742         }
743 
744         return sRet;
745     }
746 
747     sRet = substr(0, uLen - 3) + "...";
748 
749     return sRet;
750 }
751 
Left(size_type uCount) const752 CString CString::Left(size_type uCount) const {
753     uCount = (uCount > length()) ? length() : uCount;
754     return substr(0, uCount);
755 }
756 
Right(size_type uCount) const757 CString CString::Right(size_type uCount) const {
758     uCount = (uCount > length()) ? length() : uCount;
759     return substr(length() - uCount, uCount);
760 }
761 
URLSplit(MCString & msRet) const762 CString::size_type CString::URLSplit(MCString& msRet) const {
763     msRet.clear();
764 
765     VCString vsPairs;
766     Split("&", vsPairs);
767 
768     for (const CString& sPair : vsPairs) {
769         msRet[sPair.Token(0, false, "=")
770                   .Escape(CString::EURL, CString::EASCII)] =
771             sPair.Token(1, true, "=").Escape(CString::EURL, CString::EASCII);
772     }
773 
774     return msRet.size();
775 }
776 
OptionSplit(MCString & msRet,bool bUpperKeys) const777 CString::size_type CString::OptionSplit(MCString& msRet,
778                                         bool bUpperKeys) const {
779     CString sName;
780     CString sCopy(*this);
781     msRet.clear();
782 
783     while (!sCopy.empty()) {
784         sName = sCopy.Token(0, false, "=", false, "\"", "\"", false).Trim_n();
785         sCopy =
786             sCopy.Token(1, true, "=", false, "\"", "\"", false).TrimLeft_n();
787 
788         if (sName.empty()) {
789             continue;
790         }
791 
792         VCString vsNames;
793         sName.Split(" ", vsNames, false, "\"", "\"");
794 
795         for (unsigned int a = 0; a < vsNames.size(); a++) {
796             CString sKeyName = vsNames[a];
797 
798             if (bUpperKeys) {
799                 sKeyName.MakeUpper();
800             }
801 
802             if ((a + 1) == vsNames.size()) {
803                 msRet[sKeyName] = sCopy.Token(0, false, " ", false, "\"", "\"");
804                 sCopy = sCopy.Token(1, true, " ", false, "\"", "\"", false);
805             } else {
806                 msRet[sKeyName] = "";
807             }
808         }
809     }
810 
811     return msRet.size();
812 }
813 
QuoteSplit(VCString & vsRet) const814 CString::size_type CString::QuoteSplit(VCString& vsRet) const {
815     vsRet.clear();
816     return Split(" ", vsRet, false, "\"", "\"", true);
817 }
818 
Split(const CString & sDelim,VCString & vsRet,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes,bool bTrimWhiteSpace) const819 CString::size_type CString::Split(const CString& sDelim, VCString& vsRet,
820                                   bool bAllowEmpty, const CString& sLeft,
821                                   const CString& sRight, bool bTrimQuotes,
822                                   bool bTrimWhiteSpace) const {
823     vsRet.clear();
824 
825     if (empty()) {
826         return 0;
827     }
828 
829     CString sTmp;
830     bool bInside = false;
831     size_type uDelimLen = sDelim.length();
832     size_type uLeftLen = sLeft.length();
833     size_type uRightLen = sRight.length();
834     const char* p = c_str();
835 
836     if (!bAllowEmpty) {
837         while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
838             p += uDelimLen;
839         }
840     }
841 
842     while (*p) {
843         if (uLeftLen && uRightLen && !bInside &&
844             strncasecmp(p, sLeft.c_str(), uLeftLen) == 0) {
845             if (!bTrimQuotes) {
846                 sTmp += sLeft;
847             }
848 
849             p += uLeftLen;
850             bInside = true;
851             continue;
852         }
853 
854         if (uLeftLen && uRightLen && bInside &&
855             strncasecmp(p, sRight.c_str(), uRightLen) == 0) {
856             if (!bTrimQuotes) {
857                 sTmp += sRight;
858             }
859 
860             p += uRightLen;
861             bInside = false;
862             continue;
863         }
864 
865         if (uDelimLen && !bInside &&
866             strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
867             if (bTrimWhiteSpace) {
868                 sTmp.Trim();
869             }
870 
871             vsRet.push_back(sTmp);
872             sTmp.clear();
873             p += uDelimLen;
874 
875             if (!bAllowEmpty) {
876                 while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
877                     p += uDelimLen;
878                 }
879             }
880 
881             bInside = false;
882             continue;
883         } else {
884             sTmp += *p;
885         }
886 
887         p++;
888     }
889 
890     if (!sTmp.empty()) {
891         if (bTrimWhiteSpace) {
892             sTmp.Trim();
893         }
894 
895         vsRet.push_back(sTmp);
896     }
897 
898     return vsRet.size();
899 }
900 
Split(const CString & sDelim,SCString & ssRet,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes,bool bTrimWhiteSpace) const901 CString::size_type CString::Split(const CString& sDelim, SCString& ssRet,
902                                   bool bAllowEmpty, const CString& sLeft,
903                                   const CString& sRight, bool bTrimQuotes,
904                                   bool bTrimWhiteSpace) const {
905     VCString vsTokens;
906 
907     Split(sDelim, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes,
908           bTrimWhiteSpace);
909 
910     ssRet.clear();
911 
912     for (const CString& sToken : vsTokens) {
913         ssRet.insert(sToken);
914     }
915 
916     return ssRet.size();
917 }
918 
NamedFormat(const CString & sFormat,const MCString & msValues)919 CString CString::NamedFormat(const CString& sFormat, const MCString& msValues) {
920     CString sRet;
921 
922     CString sKey;
923     bool bEscape = false;
924     bool bParam = false;
925     const char* p = sFormat.c_str();
926 
927     while (*p) {
928         if (!bParam) {
929             if (bEscape) {
930                 sRet += *p;
931                 bEscape = false;
932             } else if (*p == '\\') {
933                 bEscape = true;
934             } else if (*p == '{') {
935                 bParam = true;
936                 sKey.clear();
937             } else {
938                 sRet += *p;
939             }
940 
941         } else {
942             if (bEscape) {
943                 sKey += *p;
944                 bEscape = false;
945             } else if (*p == '\\') {
946                 bEscape = true;
947             } else if (*p == '}') {
948                 bParam = false;
949                 MCString::const_iterator it = msValues.find(sKey);
950                 if (it != msValues.end()) {
951                     sRet += (*it).second;
952                 }
953             } else {
954                 sKey += *p;
955             }
956         }
957 
958         p++;
959     }
960 
961     return sRet;
962 }
963 
RandomString(unsigned int uLength)964 CString CString::RandomString(unsigned int uLength) {
965     const char chars[] =
966         "abcdefghijklmnopqrstuvwxyz"
967         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
968         "0123456789!?.,:;/*-+_()";
969     // -1 because sizeof() includes the trailing '\0' byte
970     const size_t len = sizeof(chars) / sizeof(chars[0]) - 1;
971     size_t p;
972     CString sRet;
973 
974     for (unsigned int a = 0; a < uLength; a++) {
975         p = (size_t)(len * (rand() / (RAND_MAX + 1.0)));
976         sRet += chars[p];
977     }
978 
979     return sRet;
980 }
981 
Base64Encode(unsigned int uWrap)982 bool CString::Base64Encode(unsigned int uWrap) {
983     CString sCopy(*this);
984     return sCopy.Base64Encode(*this, uWrap);
985 }
986 
Base64Decode()987 unsigned long CString::Base64Decode() {
988     CString sCopy(*this);
989     return sCopy.Base64Decode(*this);
990 }
991 
Base64Encode_n(unsigned int uWrap) const992 CString CString::Base64Encode_n(unsigned int uWrap) const {
993     CString sRet;
994     Base64Encode(sRet, uWrap);
995     return sRet;
996 }
997 
Base64Decode_n() const998 CString CString::Base64Decode_n() const {
999     CString sRet;
1000     Base64Decode(sRet);
1001     return sRet;
1002 }
1003 
Base64Encode(CString & sRet,unsigned int uWrap) const1004 bool CString::Base64Encode(CString& sRet, unsigned int uWrap) const {
1005     const char b64table[] =
1006         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1007     sRet.clear();
1008     size_t len = size();
1009     const unsigned char* input = (const unsigned char*)c_str();
1010     unsigned char* output, *p;
1011     size_t i = 0, mod = len % 3, toalloc;
1012     toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1 + 8;
1013 
1014     if (uWrap) {
1015         toalloc += len / 57;
1016         if (len % 57) {
1017             toalloc++;
1018         }
1019     }
1020 
1021     if (toalloc < len) {
1022         return 0;
1023     }
1024 
1025     p = output = new unsigned char[toalloc]{};
1026 
1027     while (i < len - mod) {
1028         *p++ = b64table[input[i++] >> 2];
1029         *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
1030         *p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f];
1031         *p++ = b64table[input[i + 1] & 0x3f];
1032         i += 2;
1033 
1034         if (uWrap && !(i % 57)) {
1035             *p++ = '\n';
1036         }
1037     }
1038 
1039     if (!mod) {
1040         if (uWrap && i % 57) {
1041             *p++ = '\n';
1042         }
1043     } else {
1044         *p++ = b64table[input[i++] >> 2];
1045         *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
1046         if (mod == 1) {
1047             *p++ = '=';
1048         } else {
1049             *p++ = b64table[(input[i] << 2) & 0x3f];
1050         }
1051 
1052         *p++ = '=';
1053 
1054         if (uWrap) {
1055             *p++ = '\n';
1056         }
1057     }
1058 
1059     *p = 0;
1060     sRet = (char*)output;
1061     delete[] output;
1062     return true;
1063 }
1064 
Base64Decode(CString & sRet) const1065 unsigned long CString::Base64Decode(CString& sRet) const {
1066     CString sTmp(*this);
1067     // remove new lines
1068     sTmp.Replace("\r", "");
1069     sTmp.Replace("\n", "");
1070 
1071     const char* in = sTmp.c_str();
1072     char c, c1, *p;
1073     unsigned long i;
1074     unsigned long uLen = sTmp.size();
1075     char* out = new char[uLen + 1]{};
1076 
1077     for (i = 0, p = out; i < uLen; i++) {
1078         c = (char)base64_table[(unsigned char)in[i++]];
1079         c1 = (char)base64_table[(unsigned char)in[i++]];
1080         *p++ = char((c << 2) | ((c1 >> 4) & 0x3));
1081 
1082         if (i < uLen) {
1083             if (in[i] == '=') {
1084                 break;
1085             }
1086             c = (char)base64_table[(unsigned char)in[i]];
1087             *p++ = char(((c1 << 4) & 0xf0) | ((c >> 2) & 0xf));
1088         }
1089 
1090         if (++i < uLen) {
1091             if (in[i] == '=') {
1092                 break;
1093             }
1094             *p++ = char(((c << 6) & 0xc0) |
1095                         (char)base64_table[(unsigned char)in[i]]);
1096         }
1097     }
1098 
1099     *p = '\0';
1100     unsigned long uRet = p - out;
1101     sRet.clear();
1102     sRet.append(out, uRet);
1103     delete[] out;
1104 
1105     return uRet;
1106 }
1107 
MD5() const1108 CString CString::MD5() const { return (const char*)CMD5(*this); }
1109 
SHA256() const1110 CString CString::SHA256() const {
1111     unsigned char digest[SHA256_DIGEST_SIZE];
1112     char digest_hex[SHA256_DIGEST_SIZE * 2 + 1];
1113     const unsigned char* message = (const unsigned char*)c_str();
1114 
1115     sha256(message, length(), digest);
1116 
1117     snprintf(digest_hex, sizeof(digest_hex),
1118              "%02x%02x%02x%02x%02x%02x%02x%02x"
1119              "%02x%02x%02x%02x%02x%02x%02x%02x"
1120              "%02x%02x%02x%02x%02x%02x%02x%02x"
1121              "%02x%02x%02x%02x%02x%02x%02x%02x",
1122              digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
1123              digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
1124              digest[12], digest[13], digest[14], digest[15], digest[16],
1125              digest[17], digest[18], digest[19], digest[20], digest[21],
1126              digest[22], digest[23], digest[24], digest[25], digest[26],
1127              digest[27], digest[28], digest[29], digest[30], digest[31]);
1128 
1129     return digest_hex;
1130 }
1131 
1132 #ifdef HAVE_LIBSSL
Encrypt_n(const CString & sPass,const CString & sIvec) const1133 CString CString::Encrypt_n(const CString& sPass, const CString& sIvec) const {
1134     CString sRet;
1135     sRet.Encrypt(sPass, sIvec);
1136     return sRet;
1137 }
1138 
Decrypt_n(const CString & sPass,const CString & sIvec) const1139 CString CString::Decrypt_n(const CString& sPass, const CString& sIvec) const {
1140     CString sRet;
1141     sRet.Decrypt(sPass, sIvec);
1142     return sRet;
1143 }
1144 
Encrypt(const CString & sPass,const CString & sIvec)1145 void CString::Encrypt(const CString& sPass, const CString& sIvec) {
1146     Crypt(sPass, true, sIvec);
1147 }
1148 
Decrypt(const CString & sPass,const CString & sIvec)1149 void CString::Decrypt(const CString& sPass, const CString& sIvec) {
1150     Crypt(sPass, false, sIvec);
1151 }
1152 
Crypt(const CString & sPass,bool bEncrypt,const CString & sIvec)1153 void CString::Crypt(const CString& sPass, bool bEncrypt, const CString& sIvec) {
1154     unsigned char szIvec[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1155     BF_KEY bKey;
1156 
1157     if (sIvec.length() >= 8) {
1158         memcpy(szIvec, sIvec.data(), 8);
1159     }
1160 
1161     BF_set_key(&bKey, (unsigned int)sPass.length(),
1162                (unsigned char*)sPass.data());
1163     unsigned int uPad = (length() % 8);
1164 
1165     if (uPad) {
1166         uPad = 8 - uPad;
1167         append(uPad, '\0');
1168     }
1169 
1170     size_t uLen = length();
1171     unsigned char* szBuff = (unsigned char*)malloc(uLen);
1172     BF_cbc_encrypt((const unsigned char*)data(), szBuff, uLen, &bKey, szIvec,
1173                    ((bEncrypt) ? BF_ENCRYPT : BF_DECRYPT));
1174 
1175     clear();
1176     append((const char*)szBuff, uLen);
1177     free(szBuff);
1178 }
1179 #endif  // HAVE_LIBSSL
1180 
ToPercent(double d)1181 CString CString::ToPercent(double d) {
1182     char szRet[32];
1183     snprintf(szRet, 32, "%.02f%%", d);
1184     return szRet;
1185 }
1186 
ToByteStr(unsigned long long d)1187 CString CString::ToByteStr(unsigned long long d) {
1188     const unsigned long long KiB = 1024;
1189     const unsigned long long MiB = KiB * 1024;
1190     const unsigned long long GiB = MiB * 1024;
1191     const unsigned long long TiB = GiB * 1024;
1192 
1193     if (d > TiB) {
1194         return CString(d / TiB) + " TiB";
1195     } else if (d > GiB) {
1196         return CString(d / GiB) + " GiB";
1197     } else if (d > MiB) {
1198         return CString(d / MiB) + " MiB";
1199     } else if (d > KiB) {
1200         return CString(d / KiB) + " KiB";
1201     }
1202 
1203     return CString(d) + " B";
1204 }
1205 
ToTimeStr(unsigned long s)1206 CString CString::ToTimeStr(unsigned long s) {
1207     const unsigned long m = 60;
1208     const unsigned long h = m * 60;
1209     const unsigned long d = h * 24;
1210     const unsigned long w = d * 7;
1211     const unsigned long y = d * 365;
1212     CString sRet;
1213 
1214 #define TIMESPAN(time, str)                  \
1215     if (s >= time) {                         \
1216         sRet += CString(s / time) + str " "; \
1217         s = s % time;                        \
1218     }
1219     TIMESPAN(y, "y");
1220     TIMESPAN(w, "w");
1221     TIMESPAN(d, "d");
1222     TIMESPAN(h, "h");
1223     TIMESPAN(m, "m");
1224     TIMESPAN(1, "s");
1225 
1226     if (sRet.empty()) return "0s";
1227 
1228     return sRet.RightChomp_n();
1229 }
1230 
ToBool() const1231 bool CString::ToBool() const {
1232     CString sTrimmed = Trim_n();
1233     return (!sTrimmed.Trim_n("0").empty() && !sTrimmed.Equals("false") &&
1234             !sTrimmed.Equals("off") && !sTrimmed.Equals("no") &&
1235             !sTrimmed.Equals("n"));
1236 }
1237 
ToShort() const1238 short CString::ToShort() const {
1239     return (short int)strtol(this->c_str(), (char**)nullptr, 10);
1240 }
ToUShort() const1241 unsigned short CString::ToUShort() const {
1242     return (unsigned short int)strtoul(this->c_str(), (char**)nullptr, 10);
1243 }
ToUInt() const1244 unsigned int CString::ToUInt() const {
1245     return (unsigned int)strtoul(this->c_str(), (char**)nullptr, 10);
1246 }
ToInt() const1247 int CString::ToInt() const {
1248     return (int)strtol(this->c_str(), (char**)nullptr, 10);
1249 }
ToLong() const1250 long CString::ToLong() const {
1251     return strtol(this->c_str(), (char**)nullptr, 10);
1252 }
ToULong() const1253 unsigned long CString::ToULong() const { return strtoul(c_str(), nullptr, 10); }
ToULongLong() const1254 unsigned long long CString::ToULongLong() const {
1255     return strtoull(c_str(), nullptr, 10);
1256 }
ToLongLong() const1257 long long CString::ToLongLong() const { return strtoll(c_str(), nullptr, 10); }
ToDouble() const1258 double CString::ToDouble() const { return strtod(c_str(), nullptr); }
1259 
Trim(const CString & s)1260 bool CString::Trim(const CString& s) {
1261     bool bLeft = TrimLeft(s);
1262     return (TrimRight(s) || bLeft);
1263 }
1264 
TrimLeft(const CString & s)1265 bool CString::TrimLeft(const CString& s) {
1266     size_type i = find_first_not_of(s);
1267 
1268     if (i == 0) return false;
1269 
1270     if (i != npos)
1271         this->erase(0, i);
1272     else
1273         this->clear();
1274 
1275     return true;
1276 }
1277 
TrimRight(const CString & s)1278 bool CString::TrimRight(const CString& s) {
1279     size_type i = find_last_not_of(s);
1280 
1281     if (i + 1 == length()) return false;
1282 
1283     if (i != npos)
1284         this->erase(i + 1, npos);
1285     else
1286         this->clear();
1287 
1288     return true;
1289 }
1290 
Trim_n(const CString & s) const1291 CString CString::Trim_n(const CString& s) const {
1292     CString sRet = *this;
1293     sRet.Trim(s);
1294     return sRet;
1295 }
1296 
TrimLeft_n(const CString & s) const1297 CString CString::TrimLeft_n(const CString& s) const {
1298     CString sRet = *this;
1299     sRet.TrimLeft(s);
1300     return sRet;
1301 }
1302 
TrimRight_n(const CString & s) const1303 CString CString::TrimRight_n(const CString& s) const {
1304     CString sRet = *this;
1305     sRet.TrimRight(s);
1306     return sRet;
1307 }
1308 
TrimPrefix(const CString & sPrefix)1309 bool CString::TrimPrefix(const CString& sPrefix) {
1310     if (StartsWith(sPrefix)) {
1311         LeftChomp(sPrefix.length());
1312         return true;
1313     } else {
1314         return false;
1315     }
1316 }
1317 
TrimSuffix(const CString & sSuffix)1318 bool CString::TrimSuffix(const CString& sSuffix) {
1319     if (Right(sSuffix.length()).Equals(sSuffix)) {
1320         RightChomp(sSuffix.length());
1321         return true;
1322     } else {
1323         return false;
1324     }
1325 }
1326 
Find(const CString & s,CaseSensitivity cs) const1327 size_t CString::Find(const CString& s, CaseSensitivity cs) const {
1328     if (cs == CaseSensitive) {
1329         return find(s);
1330     } else {
1331         return AsLower().find(s.AsLower());
1332     }
1333 }
1334 
StartsWith(const CString & sPrefix,CaseSensitivity cs) const1335 bool CString::StartsWith(const CString& sPrefix, CaseSensitivity cs) const {
1336     return Left(sPrefix.length()).Equals(sPrefix, cs);
1337 }
1338 
EndsWith(const CString & sSuffix,CaseSensitivity cs) const1339 bool CString::EndsWith(const CString& sSuffix, CaseSensitivity cs) const {
1340     return Right(sSuffix.length()).Equals(sSuffix, cs);
1341 }
1342 
Contains(const CString & s,CaseSensitivity cs) const1343 bool CString::Contains(const CString& s, CaseSensitivity cs) const {
1344     return Find(s, cs) != npos;
1345 }
1346 
TrimPrefix_n(const CString & sPrefix) const1347 CString CString::TrimPrefix_n(const CString& sPrefix) const {
1348     CString sRet = *this;
1349     sRet.TrimPrefix(sPrefix);
1350     return sRet;
1351 }
1352 
TrimSuffix_n(const CString & sSuffix) const1353 CString CString::TrimSuffix_n(const CString& sSuffix) const {
1354     CString sRet = *this;
1355     sRet.TrimSuffix(sSuffix);
1356     return sRet;
1357 }
1358 
LeftChomp_n(size_type uLen) const1359 CString CString::LeftChomp_n(size_type uLen) const {
1360     CString sRet = *this;
1361     sRet.LeftChomp(uLen);
1362     return sRet;
1363 }
1364 
RightChomp_n(size_type uLen) const1365 CString CString::RightChomp_n(size_type uLen) const {
1366     CString sRet = *this;
1367     sRet.RightChomp(uLen);
1368     return sRet;
1369 }
1370 
LeftChomp(size_type uLen)1371 bool CString::LeftChomp(size_type uLen) {
1372     bool bRet = false;
1373 
1374     while ((uLen--) && (length())) {
1375         erase(0, 1);
1376         bRet = true;
1377     }
1378 
1379     return bRet;
1380 }
1381 
RightChomp(size_type uLen)1382 bool CString::RightChomp(size_type uLen) {
1383     bool bRet = false;
1384 
1385     while ((uLen--) && (length())) {
1386         erase(length() - 1);
1387         bRet = true;
1388     }
1389 
1390     return bRet;
1391 }
1392 
StripControls_n() const1393 CString CString::StripControls_n() const {
1394     CString sRet;
1395     const unsigned char* pStart = (const unsigned char*)data();
1396     unsigned char ch = *pStart;
1397     size_type iLength = length();
1398     sRet.reserve(iLength);
1399     bool colorCode = false;
1400     unsigned int digits = 0;
1401     bool comma = false;
1402 
1403     for (unsigned int a = 0; a < iLength; a++, ch = pStart[a]) {
1404         // Color code. Format: \x03([0-9]{1,2}(,[0-9]{1,2})?)?
1405         if (ch == 0x03) {
1406             colorCode = true;
1407             digits = 0;
1408             comma = false;
1409             continue;
1410         }
1411         if (colorCode) {
1412             if (isdigit(ch) && digits < 2) {
1413                 digits++;
1414                 continue;
1415             }
1416             if (ch == ',' && !comma) {
1417                 comma = true;
1418                 digits = 0;
1419                 continue;
1420             }
1421 
1422             colorCode = false;
1423 
1424             if (digits == 0 && comma) {
1425                 // There was a ',' which wasn't followed by digits, we should
1426                 // print it.
1427                 sRet += ',';
1428             }
1429         }
1430         // CO controls codes
1431         if (ch < 0x20 || ch == 0x7F) continue;
1432         sRet += ch;
1433     }
1434     if (colorCode && digits == 0 && comma) {
1435         sRet += ',';
1436     }
1437 
1438     sRet.reserve(0);
1439     return sRet;
1440 }
1441 
StripControls()1442 CString& CString::StripControls() { return (*this = StripControls_n()); }
1443 
1444 //////////////// MCString ////////////////
1445 const MCString MCString::EmptyMap;
1446 
WriteToDisk(const CString & sPath,mode_t iMode) const1447 MCString::status_t MCString::WriteToDisk(const CString& sPath,
1448                                          mode_t iMode) const {
1449     CFile cFile(sPath);
1450 
1451     if (this->empty()) {
1452         if (!cFile.Exists()) return MCS_SUCCESS;
1453         if (cFile.Delete()) return MCS_SUCCESS;
1454     }
1455 
1456     if (!cFile.Open(O_WRONLY | O_CREAT | O_TRUNC, iMode)) {
1457         return MCS_EOPEN;
1458     }
1459 
1460     for (const auto& it : *this) {
1461         CString sKey = it.first;
1462         CString sValue = it.second;
1463         if (!WriteFilter(sKey, sValue)) {
1464             return MCS_EWRITEFIL;
1465         }
1466 
1467         if (sKey.empty()) {
1468             continue;
1469         }
1470 
1471         if (cFile.Write(Encode(sKey) + " " + Encode(sValue) + "\n") <= 0) {
1472             return MCS_EWRITE;
1473         }
1474     }
1475 
1476     cFile.Close();
1477 
1478     return MCS_SUCCESS;
1479 }
1480 
ReadFromDisk(const CString & sPath)1481 MCString::status_t MCString::ReadFromDisk(const CString& sPath) {
1482     clear();
1483     CFile cFile(sPath);
1484     if (!cFile.Open(O_RDONLY)) {
1485         return MCS_EOPEN;
1486     }
1487 
1488     CString sBuffer;
1489 
1490     while (cFile.ReadLine(sBuffer)) {
1491         sBuffer.Trim();
1492         CString sKey = sBuffer.Token(0);
1493         CString sValue = sBuffer.Token(1);
1494         Decode(sKey);
1495         Decode(sValue);
1496 
1497         if (!ReadFilter(sKey, sValue)) return MCS_EREADFIL;
1498 
1499         (*this)[sKey] = sValue;
1500     }
1501     cFile.Close();
1502 
1503     return MCS_SUCCESS;
1504 }
1505 
1506 static const char hexdigits[] = "0123456789abcdef";
1507 
Encode(CString & sValue) const1508 CString& MCString::Encode(CString& sValue) const {
1509     CString sTmp;
1510     for (unsigned char c : sValue) {
1511         // isalnum() needs unsigned char as argument and this code
1512         // assumes unsigned, too.
1513         if (isalnum(c)) {
1514             sTmp += c;
1515         } else {
1516             sTmp += "%";
1517             sTmp += hexdigits[c >> 4];
1518             sTmp += hexdigits[c & 0xf];
1519             sTmp += ";";
1520         }
1521     }
1522     sValue = sTmp;
1523     return sValue;
1524 }
1525 
Decode(CString & sValue) const1526 CString& MCString::Decode(CString& sValue) const {
1527     const char* pTmp = sValue.c_str();
1528     char* endptr;
1529     CString sTmp;
1530 
1531     while (*pTmp) {
1532         if (*pTmp != '%') {
1533             sTmp += *pTmp++;
1534         } else {
1535             char ch = (char)strtol(pTmp + 1, &endptr, 16);
1536             if (*endptr == ';') {
1537                 sTmp += ch;
1538                 pTmp = ++endptr;
1539             } else {
1540                 sTmp += *pTmp++;
1541             }
1542         }
1543     }
1544 
1545     sValue = sTmp;
1546     return sValue;
1547 }
1548