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 #ifndef ZNCSTRING_H
18 #define ZNCSTRING_H
19 
20 #include <znc/zncconfig.h>
21 #include <map>
22 #include <set>
23 #include <string>
24 #include <vector>
25 #include <sstream>
26 #include <sys/types.h>
27 #include <initializer_list>
28 
29 #define _SQL(s) CString("'" + CString(s).Escape_n(CString::ESQL) + "'")
30 #define _URL(s) CString(s).Escape_n(CString::EURL)
31 #define _HTML(s) CString(s).Escape_n(CString::EHTML)
32 #define _NAMEDFMT(s) CString(s).Escape_n(CString::ENAMEDFMT)
33 
34 class CString;
35 class MCString;
36 
37 typedef std::set<CString> SCString;
38 typedef std::vector<CString> VCString;
39 typedef std::vector<std::pair<CString, CString>> VPair;
40 
41 static const unsigned char XX = 0xff;
42 static const unsigned char base64_table[256] = {
43     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
44     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
45     XX, XX, XX, XX, XX, 62, XX, XX, XX, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
46     61, XX, XX, XX, XX, XX, XX, XX, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
47     11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, XX, XX, XX, XX,
48     XX, XX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
49     43, 44, 45, 46, 47, 48, 49, 50, 51, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
50     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
51     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
52     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
53     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
54     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
55     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
56     XX, XX, XX, XX, XX, XX, XX, XX, XX,
57 };
58 
59 enum class CaseSensitivity { CaseInsensitive, CaseSensitive };
60 
61 /**
62  * @brief String class that is used inside ZNC.
63  *
64  * All strings that are used in ZNC and its modules should use instances of this
65  * class. It provides helpful functions for parsing input like Token() and
66  * Split().
67  */
68 class CString : public std::string {
69   public:
70     typedef enum {
71         EASCII,
72         EURL,
73         EHTML,
74         ESQL,
75         ENAMEDFMT,
76         EDEBUG,
77         EMSGTAG,
78         EHEXCOLON,
79     } EEscape;
80 
81     static const CaseSensitivity CaseSensitive = CaseSensitivity::CaseSensitive;
82     static const CaseSensitivity CaseInsensitive =
83         CaseSensitivity::CaseInsensitive;
84 
CString(bool b)85     explicit CString(bool b) : std::string(b ? "true" : "false") {}
86     explicit CString(char c);
87     explicit CString(unsigned char c);
88     explicit CString(short i);
89     explicit CString(unsigned short i);
90     explicit CString(int i);
91     explicit CString(unsigned int i);
92     explicit CString(long i);
93     explicit CString(unsigned long i);
94     explicit CString(long long i);
95     explicit CString(unsigned long long i);
96     explicit CString(double i, int precision = 2);
97     explicit CString(float i, int precision = 2);
98 
CString()99     CString() : std::string() {}
CString(const char * c)100     CString(const char* c) : std::string(c) {}
CString(const char * c,size_t l)101     CString(const char* c, size_t l) : std::string(c, l) {}
CString(const std::string & s)102     CString(const std::string& s) : std::string(s) {}
CString(size_t n,char c)103     CString(size_t n, char c) : std::string(n, c) {}
CString(std::initializer_list<char> list)104     CString(std::initializer_list<char> list) : std::string(list) {}
~CString()105     ~CString() {}
106 
107     /**
108      * Casts a CString to another type.  Implemented via std::stringstream, you use this
109      * for any class that has an operator<<(std::ostream, YourClass).
110      * @param target The object to cast into. If the cast fails, its state is unspecified.
111      * @return True if the cast succeeds, and false if it fails.
112      */
113     template <typename T>
Convert(T * target)114     bool Convert(T* target) const {
115         std::stringstream ss(*this);
116         ss >> *target;
117         return (bool)ss;  // we don't care why it failed, only whether it failed
118     }
119 
120     /**
121      * Joins a collection of objects together, using 'this' as a delimiter.
122      * You can pass either pointers to arrays, or iterators to collections.
123      * @param i_begin An iterator pointing to the beginning of a group of objects.
124      * @param i_end An iterator pointing past the end of a group of objects.
125      * @return The joined string
126      */
127     template <typename Iterator>
Join(Iterator i_start,const Iterator & i_end)128     CString Join(Iterator i_start, const Iterator& i_end) const {
129         if (i_start == i_end) return CString("");
130         std::ostringstream output;
131         output << *i_start;
132         while (true) {
133             ++i_start;
134             if (i_start == i_end) return CString(output.str());
135             output << *this;
136             output << *i_start;
137         }
138     }
139 
140     /**
141      * Compare this string caselessly to some other string.
142      * @param s The string to compare to.
143      * @param uLen The number of characters to compare.
144      * @return An integer less than, equal to, or greater than zero if this
145      *         string smaller, equal.... to the given string.
146      */
147     int CaseCmp(const CString& s,
148                 CString::size_type uLen = CString::npos) const;
149     /**
150      * Compare this string case sensitively to some other string.
151      * @param s The string to compare to.
152      * @param uLen The number of characters to compare.
153      * @return An integer less than, equal to, or greater than zero if this
154      *         string smaller, equal.... to the given string.
155      */
156     int StrCmp(const CString& s, CString::size_type uLen = CString::npos) const;
157     /**
158      * Check if this string is equal to some other string.
159      * @param s The string to compare to.
160      * @param cs CaseSensitive if you want the comparison to be case
161      *                       sensitive, CaseInsensitive (default) otherwise.
162      * @return True if the strings are equal.
163      */
164     bool Equals(const CString& s, CaseSensitivity cs = CaseInsensitive) const;
165     /**
166      * @deprecated
167      */
168     bool Equals(const CString& s, bool bCaseSensitive,
169                 CString::size_type uLen = CString::npos) const;
170     /**
171      * Do a wildcard comparison between two strings.
172      * For example, the following returns true:
173      * <code>WildCmp("*!?bar@foo", "I_am!~bar@foo");</code>
174      * @param sWild The wildcards used for the comparison.
175      * @param sString The string that is used for comparing.
176      * @param cs CaseSensitive (default) if you want the comparison
177      *           to be case sensitive, CaseInsensitive otherwise.
178      * @todo Make cs CaseInsensitive by default.
179      * @return true if the wildcard matches.
180      */
181     static bool WildCmp(const CString& sWild, const CString& sString,
182                         CaseSensitivity cs = CaseSensitive);
183     /**
184      * Do a wild compare on this string.
185      * @param sWild The wildcards used to for the comparison.
186      * @param cs CaseSensitive (default) if you want the comparison
187      *           to be case sensitive, CaseInsensitive otherwise.
188      * @todo Make cs CaseInsensitive by default.
189      * @return The result of <code>this->WildCmp(sWild, *this);</code>.
190      */
191     bool WildCmp(const CString& sWild,
192                  CaseSensitivity cs = CaseSensitive) const;
193 
194     /**
195      * Turn all characters in this string into their upper-case equivalent.
196      * @returns A reference to *this.
197      */
198     CString& MakeUpper();
199     /**
200      * Turn all characters in this string into their lower-case equivalent.
201      * @returns A reference to *this.
202      */
203     CString& MakeLower();
204     /**
205      * Return a copy of this string with all characters turned into
206      * upper-case.
207      * @return The new string.
208      */
209     CString AsUpper() const;
210     /**
211      * Return a copy of this string with all characters turned into
212      * lower-case.
213      * @return The new string.
214      */
215     CString AsLower() const;
216 
217     static EEscape ToEscape(const CString& sEsc);
218     CString Escape_n(EEscape eFrom, EEscape eTo) const;
219     CString Escape_n(EEscape eTo) const;
220     CString& Escape(EEscape eFrom, EEscape eTo);
221     CString& Escape(EEscape eTo);
222 
223     /** Replace all occurrences in a string.
224      *
225      * You can specify a "safe zone" via sLeft and sRight. Anything inside
226      * of such a zone will not be replaced. This does not do recursion, so
227      * e.g. with <code>Replace("(a()a)", "a", "b", "(", ")", true)</code>
228      * you would get "a(b)" as result. The second opening brace and the
229      * second closing brace would not be seen as a delimitered and thus
230      * wouldn't be removed. The first a is inside a "safe zone" and thus is
231      * left alone, too.
232      *
233      * @param sStr The string to do the replacing on. This will also contain
234      *             the result when this function returns.
235      * @param sReplace The string that should be replaced.
236      * @param sWith The replacement to use.
237      * @param sLeft The string that marks the begin of the "safe zone".
238      * @param sRight The string that marks the end of the "safe zone".
239      * @param bRemoveDelims If this is true, all matches for sLeft and
240      *                      sRight are removed.
241      * @returns The number of replacements done.
242      */
243     static unsigned int Replace(CString& sStr, const CString& sReplace,
244                                 const CString& sWith, const CString& sLeft = "",
245                                 const CString& sRight = "",
246                                 bool bRemoveDelims = false);
247 
248     /** Replace all occurrences in the current string.
249      * @see CString::Replace
250      * @param sReplace The string to look for.
251      * @param sWith The replacement to use.
252      * @param sLeft The delimiter at the beginning of a safe zone.
253      * @param sRight The delimiter at the end of a safe zone.
254      * @param bRemoveDelims If true, all matching delimiters are removed.
255      * @return The result of the replacing. The current string is left
256      *         unchanged.
257      */
258     CString Replace_n(const CString& sReplace, const CString& sWith,
259                       const CString& sLeft = "", const CString& sRight = "",
260                       bool bRemoveDelims = false) const;
261     /** Replace all occurrences in the current string.
262      * @see CString::Replace
263      * @param sReplace The string to look for.
264      * @param sWith The replacement to use.
265      * @param sLeft The delimiter at the beginning of a safe zone.
266      * @param sRight The delimiter at the end of a safe zone.
267      * @param bRemoveDelims If true, all matching delimiters are removed.
268      * @returns The number of replacements done.
269      */
270     unsigned int Replace(const CString& sReplace, const CString& sWith,
271                          const CString& sLeft = "", const CString& sRight = "",
272                          bool bRemoveDelims = false);
273     /** Ellipsize the current string.
274      * For example, ellipsizing "Hello, I'm Bob" to the length 9 would
275      * result in "Hello,...".
276      * @param uLen The length to ellipsize to.
277      * @return The ellipsized string.
278      */
279     CString Ellipsize(unsigned int uLen) const;
280     /** Return the left part of the string.
281      * @param uCount The number of characters to keep.
282      * @return The resulting string.
283      */
284     CString Left(size_type uCount) const;
285     /** Return the right part of the string.
286      * @param uCount The number of characters to keep.
287      * @return The resulting string.
288      */
289     CString Right(size_type uCount) const;
290 
291     /** Get the first line of this string.
292      * @return The first line of text.
293      */
FirstLine()294     CString FirstLine() const { return Token(0, false, "\n"); }
295 
296     /** Get a token out of this string. For example in the string "a bc d  e",
297      *  each of "a", "bc", "d" and "e" are tokens.
298      * @param uPos The number of the token you are interested. The first
299      *             token has a position of 0.
300      * @param bRest If false, only the token you asked for is returned. Else
301      *              you get the substring starting from the beginning of
302      *              your token.
303      * @param sSep Seperator between tokens.
304      * @param bAllowEmpty If this is true, empty tokens are allowed. In the
305      *                    example from above this means that there is a
306      *                    token "" before the "e" token.
307      * @return The token you asked for and, if bRest is true, everything
308      *         after it.
309      * @see Split() if you need a string split into all of its tokens.
310      */
311     CString Token(size_t uPos, bool bRest = false, const CString& sSep = " ",
312                   bool bAllowEmpty = false) const;
313 
314     /** Get a token out of this string. This function behaves much like the
315      *  other Token() function in this class. The extra arguments are
316      *  handled similarly to Split().
317      */
318     CString Token(size_t uPos, bool bRest, const CString& sSep,
319                   bool bAllowEmpty, const CString& sLeft, const CString& sRight,
320                   bool bTrimQuotes = true) const;
321 
322     size_type URLSplit(MCString& msRet) const;
323     size_type OptionSplit(MCString& msRet, bool bUpperKeys = false) const;
324     size_type QuoteSplit(VCString& vsRet) const;
325 
326     /** Split up this string into tokens.
327      * Via sLeft and sRight you can define "markers" like with Replace().
328      * Anything in such a marked section is treated as a single token. All
329      * occurrences of sDelim in such a block are ignored.
330      * @param sDelim Delimiter between tokens.
331      * @param vsRet Vector for returning the result.
332      * @param bAllowEmpty Do empty tokens count as a valid token?
333      * @param sLeft Left delimiter like with Replace().
334      * @param sRight Right delimiter like with Replace().
335      * @param bTrimQuotes Should sLeft and sRight be removed from the token
336      *                    they mark?
337      * @param bTrimWhiteSpace If this is true, CString::Trim() is called on
338      *                        each token.
339      * @return The number of tokens found.
340      */
341     size_type Split(const CString& sDelim, VCString& vsRet,
342                     bool bAllowEmpty = true, const CString& sLeft = "",
343                     const CString& sRight = "", bool bTrimQuotes = true,
344                     bool bTrimWhiteSpace = false) const;
345 
346     /** Split up this string into tokens.
347      * This function is identical to the other CString::Split(), except that
348      * the result is returned as a SCString instead of a VCString.
349      */
350     size_type Split(const CString& sDelim, SCString& ssRet,
351                     bool bAllowEmpty = true, const CString& sLeft = "",
352                     const CString& sRight = "", bool bTrimQuotes = true,
353                     bool bTrimWhiteSpace = false) const;
354 
355     /** Build a string from a format string, replacing values from a map.
356      * The format specification can contain simple named parameters that match
357      * keys in the given map. For example in the string "a {b} c", the key "b"
358      * is looked up in the map, and inserted for "{b}".
359      * @param sFormat The format specification.
360      * @param msValues A map of named parameters to their values.
361      * @return The string with named parameters replaced.
362      */
363     static CString NamedFormat(const CString& sFormat,
364                                const MCString& msValues);
365 
366     /** Produces a random string.
367      * @param uLength The length of the resulting string.
368      * @return A random string.
369      */
370     static CString RandomString(unsigned int uLength);
371 
372     /** @return The MD5 hash of this string. */
373     CString MD5() const;
374     /** @return The SHA256 hash of this string. */
375     CString SHA256() const;
376 
377     /** Treat this string as base64-encoded data and decode it.
378      * @param sRet String to which the result of the decode is safed.
379      * @return The length of the resulting string.
380      */
381     unsigned long Base64Decode(CString& sRet) const;
382     /** Treat this string as base64-encoded data and decode it.
383      *  The result is saved in this CString instance.
384      * @return The length of the resulting string.
385      */
386     unsigned long Base64Decode();
387     /** Treat this string as base64-encoded data and decode it.
388      * @return The decoded string.
389      */
390     CString Base64Decode_n() const;
391     /** Base64-encode the current string.
392      * @param sRet String where the result is saved.
393      * @param uWrap A boolean(!?!) that decides if the result should be
394      *              wrapped after everywhere 57 characters.
395      * @return true unless this code is buggy.
396      * @todo WTF @ uWrap.
397      * @todo This only returns false if some formula we use was wrong?!
398      */
399     bool Base64Encode(CString& sRet, unsigned int uWrap = 0) const;
400     /** Base64-encode the current string.
401      *  This string is overwritten with the result of the encode.
402      *  @todo return value and param are as with Base64Encode() from above.
403      */
404     bool Base64Encode(unsigned int uWrap = 0);
405     /** Base64-encode the current string
406      * @todo uWrap is as broken as Base64Encode()'s uWrap.
407      * @return The encoded string.
408      */
409     CString Base64Encode_n(unsigned int uWrap = 0) const;
410 
411 #ifdef HAVE_LIBSSL
412     CString Encrypt_n(const CString& sPass, const CString& sIvec = "") const;
413     CString Decrypt_n(const CString& sPass, const CString& sIvec = "") const;
414     void Encrypt(const CString& sPass, const CString& sIvec = "");
415     void Decrypt(const CString& sPass, const CString& sIvec = "");
416     void Crypt(const CString& sPass, bool bEncrypt, const CString& sIvec = "");
417 #endif
418 
419     /** Pretty-print a percent value.
420      * @param d The percent value. This should be in range 0-100.
421      * @return The "pretty" string.
422      */
423     static CString ToPercent(double d);
424     /** Pretty-print a number of bytes.
425      * @param d The number of bytes.
426      * @return A string describing the number of bytes.
427      */
428     static CString ToByteStr(unsigned long long d);
429     /** Pretty-print a time span.
430      * @param s Number of seconds to print.
431      * @return A string like "4w 6d 4h 3m 58s".
432      */
433     static CString ToTimeStr(unsigned long s);
434 
435     /** @return True if this string is not "false". */
436     bool ToBool() const;
437     /** @return The numerical value of this string similar to atoi(). */
438     short ToShort() const;
439     /** @return The numerical value of this string similar to atoi(). */
440     unsigned short ToUShort() const;
441     /** @return The numerical value of this string similar to atoi(). */
442     int ToInt() const;
443     /** @return The numerical value of this string similar to atoi(). */
444     long ToLong() const;
445     /** @return The numerical value of this string similar to atoi(). */
446     unsigned int ToUInt() const;
447     /** @return The numerical value of this string similar to atoi(). */
448     unsigned long ToULong() const;
449     /** @return The numerical value of this string similar to atoi(). */
450     unsigned long long ToULongLong() const;
451     /** @return The numerical value of this string similar to atoi(). */
452     long long ToLongLong() const;
453     /** @return The numerical value of this string similar to atoi(). */
454     double ToDouble() const;
455 
456     /** Trim this string. All leading/trailing occurrences of characters from
457      *  s are removed.
458      * @param s A list of characters that should be trimmed.
459      * @return true if this string was modified.
460      */
461     bool Trim(const CString& s = " \t\r\n");
462     /** Trim this string. All leading occurrences of characters from s are
463      *  removed.
464      * @param s A list of characters that should be trimmed.
465      * @return true if this string was modified.
466      */
467     bool TrimLeft(const CString& s = " \t\r\n");
468     /** Trim this string. All trailing occurrences of characters from s are
469      *  removed.
470      * @param s A list of characters that should be trimmed.
471      * @return true if this string was modified.
472      */
473     bool TrimRight(const CString& s = " \t\r\n");
474     /** Trim this string. All leading/trailing occurrences of characters from
475      *  s are removed. This CString instance is not modified.
476      * @param s A list of characters that should be trimmed.
477      * @return The trimmed string.
478      */
479     CString Trim_n(const CString& s = " \t\r\n") const;
480     /** Trim this string. All leading occurrences of characters from s are
481      *  removed. This CString instance is not modified.
482      * @param s A list of characters that should be trimmed.
483      * @return The trimmed string.
484      */
485     CString TrimLeft_n(const CString& s = " \t\r\n") const;
486     /** Trim this string. All trailing occurrences of characters from s are
487      *  removed. This CString instance is not modified.
488      * @param s A list of characters that should be trimmed.
489      * @return The trimmed string.
490      */
491     CString TrimRight_n(const CString& s = " \t\r\n") const;
492 
493     /** Trim a given prefix.
494      * @param sPrefix The prefix that should be removed.
495      * @return True if this string was modified.
496      */
497     bool TrimPrefix(const CString& sPrefix = ":");
498     /** Trim a given suffix.
499      * @param sSuffix The suffix that should be removed.
500      * @return True if this string was modified.
501      */
502     bool TrimSuffix(const CString& sSuffix);
503     /** Trim a given prefix.
504      * @param sPrefix The prefix that should be removed.
505      * @return A copy of this string without the prefix.
506      */
507     CString TrimPrefix_n(const CString& sPrefix = ":") const;
508     /** Trim a given suffix.
509      * @param sSuffix The suffix that should be removed.
510      * @return A copy of this string without the prefix.
511      */
512     CString TrimSuffix_n(const CString& sSuffix) const;
513 
514     /** Find the position of the given substring.
515      * @param s The substring to search for.
516      * @param cs CaseSensitive if you want the comparison to be case
517      *                       sensitive, CaseInsensitive (default) otherwise.
518      * @return The position of the substring if found, CString::npos otherwise.
519      */
520     size_t Find(const CString& s, CaseSensitivity cs = CaseInsensitive) const;
521     /** Check whether the string starts with a given prefix.
522      * @param sPrefix The prefix.
523      * @param cs CaseSensitive if you want the comparison to be case
524      *                       sensitive, CaseInsensitive (default) otherwise.
525      * @return True if the string starts with prefix, false otherwise.
526      */
527     bool StartsWith(const CString& sPrefix,
528                     CaseSensitivity cs = CaseInsensitive) const;
529     /** Check whether the string ends with a given suffix.
530      * @param sSuffix The suffix.
531      * @param cs CaseSensitive if you want the comparison to be case
532      *                       sensitive, CaseInsensitive (default) otherwise.
533      * @return True if the string ends with suffix, false otherwise.
534      */
535     bool EndsWith(const CString& sSuffix,
536                   CaseSensitivity cs = CaseInsensitive) const;
537     /**
538      * Check whether the string contains a given string.
539      * @param s The string to search.
540      * @param bCaseSensitive Whether the search is case sensitive.
541      * @return True if this string contains the other string, falser otherwise.
542      */
543     bool Contains(const CString& s, CaseSensitivity cs = CaseInsensitive) const;
544 
545     /** Remove characters from the beginning of this string.
546      * @param uLen The number of characters to remove.
547      * @return true if this string was modified.
548      */
549     bool LeftChomp(size_type uLen = 1);
550     /** Remove characters from the end of this string.
551      * @param uLen The number of characters to remove.
552      * @return true if this string was modified.
553      */
554     bool RightChomp(size_type uLen = 1);
555     /** Remove characters from the beginning of this string.
556      * This string object isn't modified.
557      * @param uLen The number of characters to remove.
558      * @return The result of the conversion.
559      */
560     CString LeftChomp_n(size_type uLen = 1) const;
561     /** Remove characters from the end of this string.
562      * This string object isn't modified.
563      * @param uLen The number of characters to remove.
564      * @return The result of the conversion.
565      */
566     CString RightChomp_n(size_type uLen = 1) const;
567     /** Remove controls characters from this string.
568      * Controls characters are color codes, and those in C0 set
569      * See https://en.wikipedia.org/wiki/C0_and_C1_control_codes
570      * @return The result of the conversion.
571      */
572     CString& StripControls();
573     /** Remove controls characters from this string.
574      * Controls characters are color codes, and those in C0 set
575      * See https://en.wikipedia.org/wiki/C0_and_C1_control_codes
576      * This string object isn't modified.
577      * @return The result of the conversion.
578      */
579     CString StripControls_n() const;
580 
581   private:
582   protected:
583     unsigned char* strnchr(const unsigned char* src, unsigned char c,
584                            unsigned int iMaxBytes,
585                            unsigned char* pFill = nullptr,
586                            unsigned int* piCount = nullptr) const;
587 };
588 
589 /**
590  * @brief A dictionary for strings.
591  * @todo Replace with "using MCString = std::map<CString, CString>;" in ZNC 2.0
592  *
593  * This class maps strings to other strings.
594  */
595 class MCString : public std::map<CString, CString> {
596   public:
597     /** Construct an empty MCString. */
MCString()598     MCString() : std::map<CString, CString>() {}
599     /** Construct a MCString using an initializer list eg. MCString m = { {"key1", "val1"}, {"key2", "val2"} }; */
MCString(std::initializer_list<std::pair<const CString,CString>> list)600     MCString(std::initializer_list<std::pair<const CString, CString>> list)
601         : std::map<CString, CString>(list) {}
602     /** Destruct this MCString. */
~MCString()603     virtual ~MCString() { clear(); }
604 
605     /** A static instance of an empty map. */
606     static const MCString EmptyMap;
607 
608     /** Status codes that can be returned by WriteToDisk() and
609      * ReadFromDisk(). */
610     enum status_t {
611         /// No errors.
612         MCS_SUCCESS = 0,
613         /// Opening the file failed.
614         MCS_EOPEN = 1,
615         /// Writing to the file failed.
616         MCS_EWRITE = 2,
617         /// WriteFilter() failed.
618         MCS_EWRITEFIL = 3,
619         /// ReadFilter() failed.
620         MCS_EREADFIL = 4
621     };
622 
623     /** Write this map to a file.
624      * @param sPath The file name to write to.
625      * @param iMode The mode for the file.
626      * @return The result of the operation.
627      * @see WriteFilter.
628      */
629     enum status_t WriteToDisk(const CString& sPath, mode_t iMode = 0644) const;
630     /** Read a map from a file.
631      * @param sPath The file name to read from.
632      * @return The result of the operation.
633      * @see ReadFilter.
634      */
635     enum status_t ReadFromDisk(const CString& sPath);
636 
637     /** Filter used while writing this map. This function is called by
638      * WriteToDisk() for each pair that is going to be written. This
639      * function has the chance to modify the data that will be written.
640      * @param sKey The key that will be written. Can be modified.
641      * @param sValue The value that will be written. Can be modified.
642      * @return true unless WriteToDisk() should fail with MCS_EWRITEFIL.
643      */
WriteFilter(CString & sKey,CString & sValue)644     virtual bool WriteFilter(CString& sKey, CString& sValue) const {
645         return true;
646     }
647     /** Filter used while reading this map. This function is called by
648      * ReadFromDisk() for each pair that is beging read. This function has
649      * the chance to modify the data that is being read.
650      * @param sKey The key that was read. Can be modified.
651      * @param sValue The value that was read. Can be modified.
652      * @return true unless ReadFromDisk() should fail with MCS_EWRITEFIL.
653      */
ReadFilter(CString & sKey,CString & sValue)654     virtual bool ReadFilter(CString& sKey, CString& sValue) const {
655         return true;
656     }
657 
658     /** Encode a value so that it can safely be parsed by ReadFromDisk().
659      * This is an internal function.
660      */
661     virtual CString& Encode(CString& sValue) const;
662     /** Undo the effects of Encode(). This is an internal function. */
663     virtual CString& Decode(CString& sValue) const;
664 };
665 
666 namespace std {
667 template <>
668 struct hash<CString> : hash<std::string> {};
669 }
670 
671 // Make translateable messages easy to write:
672 // t_f("Foo is {1}")(foo)
673 class CInlineFormatMessage {
674   public:
675     explicit CInlineFormatMessage(const CString& sFormat)
676         : m_sFormat(sFormat) {}
677     explicit CInlineFormatMessage(CString&& sFormat)
678         : m_sFormat(std::move(sFormat)) {}
679 
680     template <typename... Args>
681     CString operator()(const Args&... args) const {
682         MCString values;
683         apply(values, 1, args...);
684         return CString::NamedFormat(m_sFormat, values);
685     }
686 
687   private:
688     template <typename Arg, typename... Rest>
689     void apply(MCString& values, int index, const Arg& arg,
690                const Rest&... rest) const {
691         values[CString(index)] = CString(arg);
692         apply(values, index + 1, rest...);
693     }
694 
695     void apply(MCString& values, int index) const {}
696 
697     CString m_sFormat;
698 };
699 
700 // For gtest
701 #ifdef GTEST_FAIL
702 inline void PrintTo(const CString& s, std::ostream* os) {
703     *os << '"' << s.Escape_n(CString::EDEBUG) << '"';
704 }
705 #endif
706 
707 #endif  // !ZNCSTRING_H
708