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 #ifdef __CYGWIN__
18 #ifndef _XOPEN_SOURCE
19 // strptime() wants this
20 #define _XOPEN_SOURCE 600
21 #endif
22 #endif
23 
24 #include <znc/Utils.h>
25 #include <znc/ZNCDebug.h>
26 #include <znc/FileUtils.h>
27 #include <znc/Message.h>
28 #ifdef HAVE_LIBSSL
29 #include <openssl/ssl.h>
30 #include <openssl/bn.h>
31 #include <openssl/rsa.h>
32 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20700000L))
33 #define X509_getm_notBefore X509_get_notBefore
34 #define X509_getm_notAfter X509_get_notAfter
35 #endif
36 #endif /* HAVE_LIBSSL */
37 #include <memory>
38 #include <unistd.h>
39 #include <time.h>
40 
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netdb.h>
44 #include <netinet/in.h>
45 
46 #ifdef HAVE_TCSETATTR
47 #include <termios.h>
48 #endif
49 
50 #ifdef HAVE_ICU
51 #include <unicode/ucnv.h>
52 #include <unicode/errorcode.h>
53 #endif
54 
55 // Required with GCC 4.3+ if openssl is disabled
56 #include <cstring>
57 #include <cstdlib>
58 #include <iomanip>
59 
60 using std::map;
61 using std::vector;
62 
CUtils()63 CUtils::CUtils() {}
~CUtils()64 CUtils::~CUtils() {}
65 
66 #ifdef HAVE_LIBSSL
67 // Generated by "openssl dhparam 2048"
68 constexpr const char* szDefaultDH2048 =
69     "-----BEGIN DH PARAMETERS-----\n"
70     "MIIBCAKCAQEAtS/K3TMY8IHzcCATQSjUF3rDidjDDQmT+mLxyxRORmzMPjFIFkKH\n"
71     "MOmxZvyCBArdaoCCEBBOzrldl/bBLn5TOeZb+MW7mpBLANTuQSOu97DDM7EzbnqC\n"
72     "b6z3QgixZ2+UqxdmQAu4nBPLFwym6W/XPFEHpz6iHISSvjzzo4cfI0xwWTcoAvFQ\n"
73     "r/ZU5BXSXp7XuDxSyyAqaaKUxquElf+x56QWrpNJypjzPpslg5ViAKwWQS0TnCrU\n"
74     "sVuhFtbNlZjqW1tMSBxiWFltS1HoEaaI79MEpf1Ps25OrQl8xqqCGKkZcHlNo4oF\n"
75     "cvUyzAEcCQYHmiYjp2hoZbSa8b690TQaAwIBAg==\n"
76     "-----END DH PARAMETERS-----\n";
77 
GenerateCert(FILE * pOut,const CString & sHost)78 void CUtils::GenerateCert(FILE* pOut, const CString& sHost) {
79     const int days = 365;
80     const int years = 10;
81 
82     unsigned int uSeed = (unsigned int)time(nullptr);
83     int serial = (rand_r(&uSeed) % 9999);
84 
85     std::unique_ptr<BIGNUM, void (*)(BIGNUM*)> pExponent(BN_new(), ::BN_free);
86     if (!pExponent || !BN_set_word(pExponent.get(), 0x10001)) return;
87 
88     std::unique_ptr<RSA, void (*)(RSA*)> pRSA(RSA_new(), ::RSA_free);
89     if (!pRSA ||
90         !RSA_generate_key_ex(pRSA.get(), 2048, pExponent.get(), nullptr))
91         return;
92 
93     std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)> pKey(EVP_PKEY_new(),
94                                                         ::EVP_PKEY_free);
95     if (!pKey || !EVP_PKEY_set1_RSA(pKey.get(), pRSA.get())) return;
96 
97     std::unique_ptr<X509, void (*)(X509*)> pCert(X509_new(), ::X509_free);
98     if (!pCert) return;
99 
100     X509_set_version(pCert.get(), 2);
101     ASN1_INTEGER_set(X509_get_serialNumber(pCert.get()), serial);
102     X509_gmtime_adj(X509_getm_notBefore(pCert.get()), 0);
103     X509_gmtime_adj(X509_getm_notAfter(pCert.get()),
104                     (long)60 * 60 * 24 * days * years);
105     X509_set_pubkey(pCert.get(), pKey.get());
106 
107     const char* pLogName = getenv("LOGNAME");
108     const char* pHostName = nullptr;
109 
110     if (!pLogName) pLogName = "Unknown";
111 
112     if (!sHost.empty()) pHostName = sHost.c_str();
113 
114     if (!pHostName) pHostName = getenv("HOSTNAME");
115 
116     if (!pHostName) pHostName = "host.unknown";
117 
118     CString sEmailAddr = pLogName;
119     sEmailAddr += "@";
120     sEmailAddr += pHostName;
121 
122     X509_NAME* pName = X509_get_subject_name(pCert.get());
123     X509_NAME_add_entry_by_txt(pName, "OU", MBSTRING_ASC,
124                                (unsigned char*)pLogName, -1, -1, 0);
125     X509_NAME_add_entry_by_txt(pName, "CN", MBSTRING_ASC,
126                                (unsigned char*)pHostName, -1, -1, 0);
127     X509_NAME_add_entry_by_txt(pName, "emailAddress", MBSTRING_ASC,
128                                (unsigned char*)sEmailAddr.c_str(), -1, -1, 0);
129 
130     X509_set_issuer_name(pCert.get(), pName);
131 
132     if (!X509_sign(pCert.get(), pKey.get(), EVP_sha256())) return;
133 
134     PEM_write_RSAPrivateKey(pOut, pRSA.get(), nullptr, nullptr, 0, nullptr,
135                             nullptr);
136     PEM_write_X509(pOut, pCert.get());
137 
138     fprintf(pOut, "%s", szDefaultDH2048);
139 }
140 #endif /* HAVE_LIBSSL */
141 
GetIP(unsigned long addr)142 CString CUtils::GetIP(unsigned long addr) {
143     char szBuf[16];
144     memset((char*)szBuf, 0, 16);
145 
146     if (addr >= (1 << 24)) {
147         unsigned long ip[4];
148         ip[0] = addr >> 24 & 255;
149         ip[1] = addr >> 16 & 255;
150         ip[2] = addr >> 8 & 255;
151         ip[3] = addr & 255;
152         sprintf(szBuf, "%lu.%lu.%lu.%lu", ip[0], ip[1], ip[2], ip[3]);
153     }
154 
155     return szBuf;
156 }
157 
GetLongIP(const CString & sIP)158 unsigned long CUtils::GetLongIP(const CString& sIP) {
159     unsigned long ret;
160     char ip[4][4];
161     unsigned int i;
162 
163     i = sscanf(sIP.c_str(), "%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip[0], ip[1],
164                ip[2], ip[3]);
165     if (i != 4) return 0;
166 
167     // Beware that atoi("200") << 24 would overflow and turn negative!
168     ret = atol(ip[0]) << 24;
169     ret += atol(ip[1]) << 16;
170     ret += atol(ip[2]) << 8;
171     ret += atol(ip[3]) << 0;
172 
173     return ret;
174 }
175 
176 // If you change this here and in GetSaltedHashPass(),
177 // don't forget CUser::HASH_DEFAULT!
178 // TODO refactor this
179 const CString CUtils::sDefaultHash = "sha256";
GetSaltedHashPass(CString & sSalt)180 CString CUtils::GetSaltedHashPass(CString& sSalt) {
181     sSalt = GetSalt();
182 
183     while (true) {
184         CString pass1;
185         do {
186             pass1 = CUtils::GetPass("Enter password");
187         } while (pass1.empty());
188 
189         CString pass2 = CUtils::GetPass("Confirm password");
190 
191         if (!pass1.Equals(pass2, CString::CaseSensitive)) {
192             CUtils::PrintError("The supplied passwords did not match");
193         } else {
194             // Construct the salted pass
195             return SaltedSHA256Hash(pass1, sSalt);
196         }
197     }
198 }
199 
GetSalt()200 CString CUtils::GetSalt() { return CString::RandomString(20); }
201 
SaltedMD5Hash(const CString & sPass,const CString & sSalt)202 CString CUtils::SaltedMD5Hash(const CString& sPass, const CString& sSalt) {
203     return CString(sPass + sSalt).MD5();
204 }
205 
SaltedSHA256Hash(const CString & sPass,const CString & sSalt)206 CString CUtils::SaltedSHA256Hash(const CString& sPass, const CString& sSalt) {
207     return CString(sPass + sSalt).SHA256();
208 }
209 
GetPass(const CString & sPrompt)210 CString CUtils::GetPass(const CString& sPrompt) {
211 #ifdef HAVE_TCSETATTR
212     // Disable echo
213     struct termios t;
214     tcgetattr(1, &t);
215     struct termios t2 = t;
216     t2.c_lflag &= ~ECHO;
217     tcsetattr(1, TCSANOW, &t2);
218     // Read pass
219     CString r;
220     GetInput(sPrompt, r);
221     // Restore echo and go to new line
222     tcsetattr(1, TCSANOW, &t);
223     fprintf(stdout, "\n");
224     fflush(stdout);
225     return r;
226 #else
227     PrintPrompt(sPrompt);
228 #ifdef HAVE_GETPASSPHRASE
229     return getpassphrase("");
230 #else
231     return getpass("");
232 #endif
233 #endif
234 }
235 
GetBoolInput(const CString & sPrompt,bool bDefault)236 bool CUtils::GetBoolInput(const CString& sPrompt, bool bDefault) {
237     return CUtils::GetBoolInput(sPrompt, &bDefault);
238 }
239 
GetBoolInput(const CString & sPrompt,bool * pbDefault)240 bool CUtils::GetBoolInput(const CString& sPrompt, bool* pbDefault) {
241     CString sRet, sDefault;
242 
243     if (pbDefault) {
244         sDefault = (*pbDefault) ? "yes" : "no";
245     }
246 
247     while (true) {
248         GetInput(sPrompt, sRet, sDefault, "yes/no");
249 
250         if (sRet.Equals("y") || sRet.Equals("yes")) {
251             return true;
252         } else if (sRet.Equals("n") || sRet.Equals("no")) {
253             return false;
254         }
255     }
256 }
257 
GetNumInput(const CString & sPrompt,unsigned int & uRet,unsigned int uMin,unsigned int uMax,unsigned int uDefault)258 bool CUtils::GetNumInput(const CString& sPrompt, unsigned int& uRet,
259                          unsigned int uMin, unsigned int uMax,
260                          unsigned int uDefault) {
261     if (uMin > uMax) {
262         return false;
263     }
264 
265     CString sDefault = (uDefault != (unsigned int)~0) ? CString(uDefault) : "";
266     CString sNum, sHint;
267 
268     if (uMax != (unsigned int)~0) {
269         sHint = CString(uMin) + " to " + CString(uMax);
270     } else if (uMin > 0) {
271         sHint = CString(uMin) + " and up";
272     }
273 
274     while (true) {
275         GetInput(sPrompt, sNum, sDefault, sHint);
276         if (sNum.empty()) {
277             return false;
278         }
279 
280         uRet = sNum.ToUInt();
281 
282         if ((uRet >= uMin && uRet <= uMax)) {
283             break;
284         }
285 
286         CUtils::PrintError("Number must be " + sHint);
287     }
288 
289     return true;
290 }
291 
GetInput(const CString & sPrompt,CString & sRet,const CString & sDefault,const CString & sHint)292 bool CUtils::GetInput(const CString& sPrompt, CString& sRet,
293                       const CString& sDefault, const CString& sHint) {
294     CString sExtra;
295     CString sInput;
296     sExtra += (!sHint.empty()) ? (" (" + sHint + ")") : "";
297     sExtra += (!sDefault.empty()) ? (" [" + sDefault + "]") : "";
298 
299     PrintPrompt(sPrompt + sExtra);
300     char szBuf[1024];
301     memset(szBuf, 0, 1024);
302     if (fgets(szBuf, 1024, stdin) == nullptr) {
303         // Reading failed (Error? EOF?)
304         PrintError("Error while reading from stdin. Exiting...");
305         exit(-1);
306     }
307     sInput = szBuf;
308 
309     sInput.TrimSuffix("\n");
310 
311     if (sInput.empty()) {
312         sRet = sDefault;
313     } else {
314         sRet = sInput;
315     }
316 
317     return !sRet.empty();
318 }
319 
320 #define BOLD "\033[1m"
321 #define NORM "\033[22m"
322 
323 #define RED "\033[31m"
324 #define GRN "\033[32m"
325 #define YEL "\033[33m"
326 #define BLU "\033[34m"
327 #define DFL "\033[39m"
328 
PrintError(const CString & sMessage)329 void CUtils::PrintError(const CString& sMessage) {
330     if (CDebug::StdoutIsTTY())
331         fprintf(stdout, BOLD BLU "[" RED " ** " BLU "]" DFL NORM " %s\n",
332                 sMessage.c_str());
333     else
334         fprintf(stdout, "%s\n", sMessage.c_str());
335     fflush(stdout);
336 }
337 
PrintPrompt(const CString & sMessage)338 void CUtils::PrintPrompt(const CString& sMessage) {
339     if (CDebug::StdoutIsTTY())
340         fprintf(stdout, BOLD BLU "[" YEL " ?? " BLU "]" DFL NORM " %s: ",
341                 sMessage.c_str());
342     else
343         fprintf(stdout, "[ ?? ] %s: ", sMessage.c_str());
344     fflush(stdout);
345 }
346 
PrintMessage(const CString & sMessage,bool bStrong)347 void CUtils::PrintMessage(const CString& sMessage, bool bStrong) {
348     if (CDebug::StdoutIsTTY()) {
349         if (bStrong)
350             fprintf(stdout,
351                     BOLD BLU "[" YEL " ** " BLU "]" DFL BOLD " %s" NORM "\n",
352                     sMessage.c_str());
353         else
354             fprintf(stdout, BOLD BLU "[" YEL " ** " BLU "]" DFL NORM " %s\n",
355                     sMessage.c_str());
356     } else
357         fprintf(stdout, "%s\n", sMessage.c_str());
358 
359     fflush(stdout);
360 }
361 
PrintAction(const CString & sMessage)362 void CUtils::PrintAction(const CString& sMessage) {
363     if (CDebug::StdoutIsTTY())
364         fprintf(stdout, BOLD BLU "[ .. " BLU "]" DFL NORM " %s...\n",
365                 sMessage.c_str());
366     else
367         fprintf(stdout, "%s... ", sMessage.c_str());
368     fflush(stdout);
369 }
370 
PrintStatus(bool bSuccess,const CString & sMessage)371 void CUtils::PrintStatus(bool bSuccess, const CString& sMessage) {
372     if (CDebug::StdoutIsTTY()) {
373         if (bSuccess) {
374             if (!sMessage.empty())
375                 fprintf(stdout,
376                         BOLD BLU "[" GRN " >> " BLU "]" DFL NORM " %s\n",
377                         sMessage.c_str());
378         } else {
379             fprintf(stdout, BOLD BLU "[" RED " !! " BLU "]" DFL NORM BOLD RED
380                                      " %s" DFL NORM "\n",
381                     sMessage.empty() ? "failed" : sMessage.c_str());
382         }
383     } else {
384         if (bSuccess) {
385             fprintf(stdout, "%s\n", sMessage.c_str());
386         } else {
387             if (!sMessage.empty()) {
388                 fprintf(stdout, "[ %s ]", sMessage.c_str());
389             }
390 
391             fprintf(stdout, "\n");
392         }
393     }
394 
395     fflush(stdout);
396 }
397 
398 namespace {
399 /* Switch GMT-X and GMT+X
400  *
401  * See https://en.wikipedia.org/wiki/Tz_database#Area
402  *
403  * "In order to conform with the POSIX style, those zone names beginning
404  * with "Etc/GMT" have their sign reversed from what most people expect.
405  * In this style, zones west of GMT have a positive sign and those east
406  * have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours
407  * ahead/east of GMT.)"
408  */
FixGMT(CString sTZ)409 inline CString FixGMT(CString sTZ) {
410     if (sTZ.length() >= 4 && sTZ.StartsWith("GMT")) {
411         if (sTZ[3] == '+') {
412             sTZ[3] = '-';
413         } else if (sTZ[3] == '-') {
414             sTZ[3] = '+';
415         }
416     }
417     return sTZ;
418 }
419 }  // namespace
420 
GetTime()421 timeval CUtils::GetTime() {
422 #ifdef HAVE_CLOCK_GETTIME
423     timespec ts;
424     if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
425         return { ts.tv_sec, static_cast<suseconds_t>(ts.tv_nsec / 1000) };
426     }
427 #endif
428 
429     struct timeval tv;
430     if (gettimeofday(&tv, nullptr) == 0) {
431         return tv;
432     }
433 
434     // Last resort, no microseconds
435     return { time(nullptr), 0 };
436 }
437 
GetMillTime()438 unsigned long long CUtils::GetMillTime() {
439     struct timeval tv = GetTime();
440     unsigned long long iTime = 0;
441     iTime = (unsigned long long)tv.tv_sec * 1000;
442     iTime += ((unsigned long long)tv.tv_usec / 1000);
443     return iTime;
444 }
445 
CTime(time_t t,const CString & sTimezone)446 CString CUtils::CTime(time_t t, const CString& sTimezone) {
447     char s[30] = {};  // should have at least 26 bytes
448     if (sTimezone.empty()) {
449         ctime_r(&t, s);
450         // ctime() adds a trailing newline
451         return CString(s).Trim_n();
452     }
453     CString sTZ = FixGMT(sTimezone);
454 
455     // backup old value
456     char* oldTZ = getenv("TZ");
457     if (oldTZ) oldTZ = strdup(oldTZ);
458     setenv("TZ", sTZ.c_str(), 1);
459     tzset();
460 
461     ctime_r(&t, s);
462 
463     // restore old value
464     if (oldTZ) {
465         setenv("TZ", oldTZ, 1);
466         free(oldTZ);
467     } else {
468         unsetenv("TZ");
469     }
470     tzset();
471 
472     return CString(s).Trim_n();
473 }
474 
FormatTime(time_t t,const CString & sFormat,const CString & sTimezone)475 CString CUtils::FormatTime(time_t t, const CString& sFormat,
476                            const CString& sTimezone) {
477     char s[1024] = {};
478     tm m;
479     if (sTimezone.empty()) {
480         localtime_r(&t, &m);
481         strftime(s, sizeof(s), sFormat.c_str(), &m);
482         return s;
483     }
484     CString sTZ = FixGMT(sTimezone);
485 
486     // backup old value
487     char* oldTZ = getenv("TZ");
488     if (oldTZ) oldTZ = strdup(oldTZ);
489     setenv("TZ", sTZ.c_str(), 1);
490     tzset();
491 
492     localtime_r(&t, &m);
493     strftime(s, sizeof(s), sFormat.c_str(), &m);
494 
495     // restore old value
496     if (oldTZ) {
497         setenv("TZ", oldTZ, 1);
498         free(oldTZ);
499     } else {
500         unsetenv("TZ");
501     }
502     tzset();
503 
504     return s;
505 }
506 
FormatTime(const timeval & tv,const CString & sFormat,const CString & sTimezone)507 CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
508                            const CString& sTimezone) {
509     // Parse additional format specifiers before passing them to
510     // strftime, since the way strftime treats unknown format
511     // specifiers is undefined.
512     CString sFormat2;
513 
514     // Make sure %% is parsed correctly, i.e. %%f is passed through to
515     // strftime to become %f, and not 123.
516     bool bInFormat = false;
517     int iDigits;
518     CString::size_type uLastCopied = 0, uFormatStart;
519 
520     for (CString::size_type i = 0; i < sFormat.length(); i++) {
521         if (!bInFormat) {
522             if (sFormat[i] == '%') {
523                 uFormatStart = i;
524                 bInFormat = true;
525                 iDigits = 3;
526             }
527         } else {
528             switch (sFormat[i]) {
529                 case '0': case '1': case '2': case '3': case '4':
530                 case '5': case '6': case '7': case '8': case '9':
531                     iDigits = sFormat[i] - '0';
532                     break;
533                 case 'f': {
534                     int iVal = tv.tv_usec;
535                     int iDigitDelta = iDigits - 6; // tv_user is in 10^-6 seconds
536                     for (; iDigitDelta > 0; iDigitDelta--)
537                         iVal *= 10;
538                     for (; iDigitDelta < 0; iDigitDelta++)
539                         iVal /= 10;
540                     sFormat2 += sFormat.substr(uLastCopied,
541                         uFormatStart - uLastCopied);
542                     CString sVal = CString(iVal);
543                     sFormat2 += CString(iDigits - sVal.length(), '0');
544                     sFormat2 += sVal;
545                     uLastCopied = i + 1;
546                     bInFormat = false;
547                     break;
548                 }
549                 default:
550                     bInFormat = false;
551             }
552         }
553     }
554 
555     if (uLastCopied) {
556         sFormat2 += sFormat.substr(uLastCopied);
557         return FormatTime(tv.tv_sec, sFormat2, sTimezone);
558     } else {
559         // If there are no extended format specifiers, avoid doing any
560         // memory allocations entirely.
561         return FormatTime(tv.tv_sec, sFormat, sTimezone);
562     }
563 }
564 
FormatServerTime(const timeval & tv)565 CString CUtils::FormatServerTime(const timeval& tv) {
566     CString s_msec(tv.tv_usec / 1000);
567     while (s_msec.length() < 3) {
568         s_msec = "0" + s_msec;
569     }
570     // TODO support leap seconds properly
571     // TODO support message-tags properly
572     struct tm stm;
573     memset(&stm, 0, sizeof(stm));
574     // OpenBSD has tv_sec as int, so explicitly convert it to time_t to make
575     // gmtime_r() happy
576     const time_t secs = tv.tv_sec;
577     gmtime_r(&secs, &stm);
578     char sTime[20] = {};
579     strftime(sTime, sizeof(sTime), "%Y-%m-%dT%H:%M:%S", &stm);
580     return CString(sTime) + "." + s_msec + "Z";
581 }
582 
ParseServerTime(const CString & sTime)583 timeval CUtils::ParseServerTime(const CString& sTime) {
584     struct tm stm;
585     memset(&stm, 0, sizeof(stm));
586     const char* cp = strptime(sTime.c_str(), "%Y-%m-%dT%H:%M:%S", &stm);
587     struct timeval tv;
588     memset(&tv, 0, sizeof(tv));
589     if (cp) {
590         tv.tv_sec = mktime(&stm);
591         CString s_usec(cp);
592         if (s_usec.TrimPrefix(".") && s_usec.TrimSuffix("Z")) {
593             tv.tv_usec = s_usec.ToULong() * 1000;
594         }
595     }
596     return tv;
597 }
598 
599 namespace {
FillTimezones(const CString & sPath,SCString & result,const CString & sPrefix)600 void FillTimezones(const CString& sPath, SCString& result,
601                    const CString& sPrefix) {
602     CDir Dir;
603     Dir.Fill(sPath);
604     for (CFile* pFile : Dir) {
605         CString sName = pFile->GetShortName();
606         CString sFile = pFile->GetLongName();
607         if (sName == "posix" || sName == "right")
608             continue;  // these 2 dirs contain the same filenames
609         if (sName.EndsWith(".tab") || sName == "posixrules" ||
610             sName == "localtime")
611             continue;
612         if (pFile->IsDir()) {
613             if (sName == "Etc") {
614                 FillTimezones(sFile, result, sPrefix);
615             } else {
616                 FillTimezones(sFile, result, sPrefix + sName + "/");
617             }
618         } else {
619             result.insert(sPrefix + sName);
620         }
621     }
622 }
623 }  // namespace
624 
GetTimezones()625 SCString CUtils::GetTimezones() {
626     static SCString result;
627     if (result.empty()) {
628         FillTimezones("/usr/share/zoneinfo", result, "");
629     }
630     return result;
631 }
632 
GetEncodings()633 SCString CUtils::GetEncodings() {
634     static SCString ssResult;
635 #ifdef HAVE_ICU
636     if (ssResult.empty()) {
637         for (int i = 0; i < ucnv_countAvailable(); ++i) {
638             const char* pConvName = ucnv_getAvailableName(i);
639             ssResult.insert(pConvName);
640             icu::ErrorCode e;
641             for (int st = 0; st < ucnv_countStandards(); ++st) {
642                 const char* pStdName = ucnv_getStandard(st, e);
643                 icu::LocalUEnumerationPointer ue(
644                     ucnv_openStandardNames(pConvName, pStdName, e));
645                 while (const char* pStdConvNameEnum =
646                            uenum_next(ue.getAlias(), nullptr, e)) {
647                     ssResult.insert(pStdConvNameEnum);
648                 }
649             }
650         }
651     }
652 #endif
653     return ssResult;
654 }
655 
CheckCIDR(const CString & sIP,const CString & sRange)656 bool CUtils::CheckCIDR(const CString& sIP, const CString& sRange) {
657     if (sIP.WildCmp(sRange)) {
658         return true;
659     }
660     auto deleter = [](addrinfo* ai) { freeaddrinfo(ai); };
661     // Try to split the string into an IP and routing prefix
662     VCString vsSplitCIDR;
663     if (sRange.Split("/", vsSplitCIDR, false) != 2) return false;
664     const CString sRoutingPrefix = vsSplitCIDR.back();
665     const int iRoutingPrefix = sRoutingPrefix.ToInt();
666     if (iRoutingPrefix < 0 || iRoutingPrefix > 128) return false;
667 
668     // If iRoutingPrefix is 0, it could be due to ToInt() failing, so
669     // sRoutingPrefix needs to be all zeroes
670     if (iRoutingPrefix == 0 && sRoutingPrefix != "0") return false;
671 
672     // Convert each IP from a numeric string to an addrinfo
673     addrinfo aiHints;
674     memset(&aiHints, 0, sizeof(addrinfo));
675     aiHints.ai_flags = AI_NUMERICHOST;
676 
677     addrinfo* aiHostC;
678     int iIsHostValid = getaddrinfo(sIP.c_str(), nullptr, &aiHints, &aiHostC);
679     if (iIsHostValid != 0) return false;
680     std::unique_ptr<addrinfo, decltype(deleter)> aiHost(aiHostC, deleter);
681 
682     aiHints.ai_family = aiHost->ai_family;  // Host and range must be in
683                                             // the same address family
684 
685     addrinfo* aiRangeC;
686     int iIsRangeValid =
687         getaddrinfo(vsSplitCIDR.front().c_str(), nullptr, &aiHints, &aiRangeC);
688     if (iIsRangeValid != 0) {
689         return false;
690     }
691     std::unique_ptr<addrinfo, decltype(deleter)> aiRange(aiRangeC, deleter);
692 
693     // "/0" allows all IPv[4|6] addresses
694     if (iRoutingPrefix == 0) {
695         return true;
696     }
697 
698     // If both IPs are valid and of the same type, make a bit field mask
699     // from the routing prefix, AND it to the host and range, and see if
700     // they match
701     if (aiHost->ai_family == AF_INET) {
702         if (iRoutingPrefix > 32) {
703             return false;
704         }
705 
706         const sockaddr_in* saHost =
707             reinterpret_cast<const sockaddr_in*>(aiHost->ai_addr);
708         const sockaddr_in* saRange =
709             reinterpret_cast<const sockaddr_in*>(aiRange->ai_addr);
710 
711         // Make IPv4 bitmask
712         const in_addr_t inBitmask = htonl((~0u) << (32 - iRoutingPrefix));
713 
714         // Compare masked IPv4s
715         return ((inBitmask & saHost->sin_addr.s_addr) ==
716                 (inBitmask & saRange->sin_addr.s_addr));
717     } else if (aiHost->ai_family == AF_INET6) {
718         // Make IPv6 bitmask
719         in6_addr in6aBitmask;
720         memset(&in6aBitmask, 0, sizeof(in6aBitmask));
721 
722         for (int i = 0, iBitsLeft = iRoutingPrefix; iBitsLeft > 0;
723              ++i, iBitsLeft -= 8) {
724             if (iBitsLeft >= 8) {
725                 in6aBitmask.s6_addr[i] = (uint8_t)(~0u);
726             } else {
727                 in6aBitmask.s6_addr[i] = (uint8_t)(~0u) << (8 - iBitsLeft);
728             }
729         }
730 
731         // Compare masked IPv6s
732         const sockaddr_in6* sa6Host =
733             reinterpret_cast<const sockaddr_in6*>(aiHost->ai_addr);
734         const sockaddr_in6* sa6Range =
735             reinterpret_cast<const sockaddr_in6*>(aiRange->ai_addr);
736 
737         for (int i = 0; i < 16; ++i) {
738             if ((in6aBitmask.s6_addr[i] & sa6Host->sin6_addr.s6_addr[i]) !=
739                 (in6aBitmask.s6_addr[i] & sa6Range->sin6_addr.s6_addr[i])) {
740                 return false;
741             }
742         }
743         return true;
744     } else {
745         return false;
746     }
747 }
748 
GetMessageTags(const CString & sLine)749 MCString CUtils::GetMessageTags(const CString& sLine) {
750     if (sLine.StartsWith("@")) {
751         return CMessage(sLine).GetTags();
752     }
753     return MCString::EmptyMap;
754 }
755 
SetMessageTags(CString & sLine,const MCString & mssTags)756 void CUtils::SetMessageTags(CString& sLine, const MCString& mssTags) {
757     CMessage Message(sLine);
758     Message.SetTags(mssTags);
759     sLine = Message.ToString();
760 }
761 
AddColumn(const CString & sName)762 bool CTable::AddColumn(const CString& sName) {
763     if (eStyle == ListStyle && m_vsHeaders.size() >= 2)
764         return false;
765     for (const CString& sHeader : m_vsHeaders) {
766         if (sHeader.Equals(sName)) {
767             return false;
768         }
769     }
770 
771     m_vsHeaders.push_back(sName);
772     m_msuWidths[sName] = sName.size();
773 
774     return true;
775 }
776 
SetStyle(EStyle eNewStyle)777 bool CTable::SetStyle(EStyle eNewStyle) {
778     switch (eNewStyle) {
779     case GridStyle:
780         break;
781     case ListStyle:
782         if (m_vsHeaders.size() > 2) return false;
783         break;
784     }
785 
786     eStyle = eNewStyle;
787     return true;
788 }
789 
AddRow()790 CTable::size_type CTable::AddRow() {
791     // Don't add a row if no headers are defined
792     if (m_vsHeaders.empty()) {
793         return (size_type)-1;
794     }
795 
796     // Add a vector with enough space for each column
797     push_back(vector<CString>(m_vsHeaders.size()));
798     return size() - 1;
799 }
800 
SetCell(const CString & sColumn,const CString & sValue,size_type uRowIdx)801 bool CTable::SetCell(const CString& sColumn, const CString& sValue,
802                      size_type uRowIdx) {
803     if (uRowIdx == (size_type)~0) {
804         if (empty()) {
805             return false;
806         }
807 
808         uRowIdx = size() - 1;
809     }
810 
811     unsigned int uColIdx = GetColumnIndex(sColumn);
812 
813     if (uColIdx == (unsigned int)-1) return false;
814 
815     (*this)[uRowIdx][uColIdx] = sValue;
816 
817     if (m_msuWidths[sColumn] < sValue.size())
818         m_msuWidths[sColumn] = sValue.size();
819 
820     return true;
821 }
822 
GetLine(unsigned int uIdx,CString & sLine) const823 bool CTable::GetLine(unsigned int uIdx, CString& sLine) const {
824     std::stringstream ssRet;
825 
826     if (empty()) {
827         return false;
828     }
829 
830     if (eStyle == ListStyle) {
831         if (m_vsHeaders.size() > 2) return false; // definition list mode can only do up to two columns
832         if (uIdx >= size()) return false;
833 
834         const std::vector<CString>& mRow = (*this)[uIdx];
835         ssRet << "\x02" << mRow[0] << "\x0f"; //bold first column
836         if (m_vsHeaders.size() >= 2 && mRow[1] != "") {
837             ssRet << ": " << mRow[1];
838         }
839 
840         sLine = ssRet.str();
841         return true;
842     }
843 
844     if (uIdx == 1) {
845         ssRet.fill(' ');
846         ssRet << "| ";
847 
848         for (unsigned int a = 0; a < m_vsHeaders.size(); a++) {
849             ssRet.width(GetColumnWidth(a));
850             ssRet << std::left << m_vsHeaders[a];
851             ssRet << ((a == m_vsHeaders.size() - 1) ? " |" : " | ");
852         }
853 
854         sLine = ssRet.str();
855         return true;
856     } else if ((uIdx == 0) || (uIdx == 2) || (uIdx == (size() + 3))) {
857         ssRet.fill('-');
858         ssRet << "+-";
859 
860         for (unsigned int a = 0; a < m_vsHeaders.size(); a++) {
861             ssRet.width(GetColumnWidth(a));
862             ssRet << std::left << "-";
863             ssRet << ((a == m_vsHeaders.size() - 1) ? "-+" : "-+-");
864         }
865 
866         sLine = ssRet.str();
867         return true;
868     } else {
869         uIdx -= 3;
870 
871         if (uIdx < size()) {
872             const std::vector<CString>& mRow = (*this)[uIdx];
873             ssRet.fill(' ');
874             ssRet << "| ";
875 
876             for (unsigned int c = 0; c < m_vsHeaders.size(); c++) {
877                 ssRet.width(GetColumnWidth(c));
878                 ssRet << std::left << mRow[c];
879                 ssRet << ((c == m_vsHeaders.size() - 1) ? " |" : " | ");
880             }
881 
882             sLine = ssRet.str();
883             return true;
884         }
885     }
886 
887     return false;
888 }
889 
GetColumnIndex(const CString & sName) const890 unsigned int CTable::GetColumnIndex(const CString& sName) const {
891     for (unsigned int i = 0; i < m_vsHeaders.size(); i++) {
892         if (m_vsHeaders[i] == sName) return i;
893     }
894 
895     DEBUG("CTable::GetColumnIndex(" + sName + ") failed");
896 
897     return (unsigned int)-1;
898 }
899 
GetColumnWidth(unsigned int uIdx) const900 CString::size_type CTable::GetColumnWidth(unsigned int uIdx) const {
901     if (uIdx >= m_vsHeaders.size()) {
902         return 0;
903     }
904 
905     const CString& sColName = m_vsHeaders[uIdx];
906     std::map<CString, CString::size_type>::const_iterator it =
907         m_msuWidths.find(sColName);
908 
909     if (it == m_msuWidths.end()) {
910         // AddColumn() and SetCell() should make sure that we get a value :/
911         return 0;
912     }
913     return it->second;
914 }
915 
Clear()916 void CTable::Clear() {
917     clear();
918     m_vsHeaders.clear();
919     m_msuWidths.clear();
920 }
921 
922 #ifdef HAVE_LIBSSL
CBlowfish(const CString & sPassword,int iEncrypt,const CString & sIvec)923 CBlowfish::CBlowfish(const CString& sPassword, int iEncrypt,
924                      const CString& sIvec)
925     : m_ivec((unsigned char*)calloc(sizeof(unsigned char), 8)),
926       m_bkey(),
927       m_iEncrypt(iEncrypt),
928       m_num(0) {
929     if (sIvec.length() >= 8) {
930         memcpy(m_ivec, sIvec.data(), 8);
931     }
932 
933     BF_set_key(&m_bkey, (unsigned int)sPassword.length(),
934                (unsigned char*)sPassword.data());
935 }
936 
~CBlowfish()937 CBlowfish::~CBlowfish() { free(m_ivec); }
938 
939 //! output must be freed
MD5(const unsigned char * input,unsigned int ilen)940 unsigned char* CBlowfish::MD5(const unsigned char* input, unsigned int ilen) {
941     unsigned char* output = (unsigned char*)malloc(MD5_DIGEST_LENGTH);
942     ::MD5(input, ilen, output);
943     return output;
944 }
945 
946 //! returns an md5 of the CString (not hex encoded)
MD5(const CString & sInput,bool bHexEncode)947 CString CBlowfish::MD5(const CString& sInput, bool bHexEncode) {
948     CString sRet;
949     unsigned char* data =
950         MD5((const unsigned char*)sInput.data(), (unsigned int)sInput.length());
951 
952     if (!bHexEncode) {
953         sRet.append((const char*)data, MD5_DIGEST_LENGTH);
954     } else {
955         for (int a = 0; a < MD5_DIGEST_LENGTH; a++) {
956             sRet += g_HexDigits[data[a] >> 4];
957             sRet += g_HexDigits[data[a] & 0xf];
958         }
959     }
960 
961     free(data);
962     return sRet;
963 }
964 
965 //! output must be the same size as input
Crypt(unsigned char * input,unsigned char * output,unsigned int uBytes)966 void CBlowfish::Crypt(unsigned char* input, unsigned char* output,
967                       unsigned int uBytes) {
968     BF_cfb64_encrypt(input, output, uBytes, &m_bkey, m_ivec, &m_num,
969                      m_iEncrypt);
970 }
971 
972 //! must free result
Crypt(unsigned char * input,unsigned int uBytes)973 unsigned char* CBlowfish::Crypt(unsigned char* input, unsigned int uBytes) {
974     unsigned char* buff = (unsigned char*)malloc(uBytes);
975     Crypt(input, buff, uBytes);
976     return buff;
977 }
978 
Crypt(const CString & sData)979 CString CBlowfish::Crypt(const CString& sData) {
980     unsigned char* buff =
981         Crypt((unsigned char*)sData.data(), (unsigned int)sData.length());
982     CString sOutput;
983     sOutput.append((const char*)buff, sData.length());
984     free(buff);
985     return sOutput;
986 }
987 
988 #endif  // HAVE_LIBSSL
989