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