Coverage Report

Created: 2023-09-25 06:17

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