anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
misc.cpp (17220B)
1 /* Miscellaneous routines. 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 #include "services.h" 13 #include "build.h" 14 #include "modules.h" 15 #include "lists.h" 16 #include "config.h" 17 #include "bots.h" 18 #include "language.h" 19 #include "regexpr.h" 20 #include "sockets.h" 21 22 #include <errno.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #ifndef _WIN32 26 #include <sys/socket.h> 27 #include <netdb.h> 28 #endif 29 30 NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending) 31 { 32 Anope::string error; 33 commasepstream sep(list); 34 Anope::string token; 35 36 sep.GetToken(token); 37 if (token.empty()) 38 token = list; 39 do 40 { 41 size_t t = token.find('-'); 42 43 if (t == Anope::string::npos) 44 { 45 try 46 { 47 unsigned num = convertTo<unsigned>(token, error, false); 48 if (error.empty()) 49 numbers.insert(num); 50 } 51 catch (const ConvertException &) 52 { 53 error = "1"; 54 } 55 56 if (!error.empty()) 57 { 58 if (!this->InvalidRange(list)) 59 { 60 is_valid = false; 61 return; 62 } 63 } 64 } 65 else 66 { 67 Anope::string error2; 68 try 69 { 70 unsigned num1 = convertTo<unsigned>(token.substr(0, t), error, false); 71 unsigned num2 = convertTo<unsigned>(token.substr(t + 1), error2, false); 72 if (error.empty() && error2.empty()) 73 for (unsigned i = num1; i <= num2; ++i) 74 numbers.insert(i); 75 } 76 catch (const ConvertException &) 77 { 78 error = "1"; 79 } 80 81 if (!error.empty() || !error2.empty()) 82 { 83 if (!this->InvalidRange(list)) 84 { 85 is_valid = false; 86 return; 87 } 88 } 89 } 90 } while (sep.GetToken(token)); 91 } 92 93 NumberList::~NumberList() 94 { 95 } 96 97 void NumberList::Process() 98 { 99 if (!is_valid) 100 return; 101 102 if (this->desc) 103 { 104 for (std::set<unsigned>::reverse_iterator it = numbers.rbegin(), it_end = numbers.rend(); it != it_end; ++it) 105 this->HandleNumber(*it); 106 } 107 else 108 { 109 for (std::set<unsigned>::iterator it = numbers.begin(), it_end = numbers.end(); it != it_end; ++it) 110 this->HandleNumber(*it); 111 } 112 } 113 114 void NumberList::HandleNumber(unsigned) 115 { 116 } 117 118 bool NumberList::InvalidRange(const Anope::string &) 119 { 120 return true; 121 } 122 123 ListFormatter::ListFormatter(NickCore *acc) : nc(acc) 124 { 125 } 126 127 ListFormatter &ListFormatter::AddColumn(const Anope::string &name) 128 { 129 this->columns.push_back(name); 130 return *this; 131 } 132 133 void ListFormatter::AddEntry(const ListEntry &entry) 134 { 135 this->entries.push_back(entry); 136 } 137 138 bool ListFormatter::IsEmpty() const 139 { 140 return this->entries.empty(); 141 } 142 143 void ListFormatter::Process(std::vector<Anope::string> &buffer) 144 { 145 std::vector<Anope::string> tcolumns; 146 std::map<Anope::string, size_t> lengths; 147 std::set<Anope::string> breaks; 148 for (unsigned i = 0; i < this->columns.size(); ++i) 149 { 150 tcolumns.push_back(Language::Translate(this->nc, this->columns[i].c_str())); 151 lengths[this->columns[i]] = tcolumns[i].length(); 152 } 153 for (unsigned i = 0; i < this->entries.size(); ++i) 154 { 155 ListEntry &e = this->entries[i]; 156 for (unsigned j = 0; j < this->columns.size(); ++j) 157 if (e[this->columns[j]].length() > lengths[this->columns[j]]) 158 lengths[this->columns[j]] = e[this->columns[j]].length(); 159 } 160 unsigned length = 0; 161 for (std::map<Anope::string, size_t>::iterator it = lengths.begin(), it_end = lengths.end(); it != it_end; ++it) 162 { 163 /* Break lines at 80 chars */ 164 if (length > 80) 165 { 166 breaks.insert(it->first); 167 length = 0; 168 } 169 else 170 length += it->second; 171 } 172 173 /* Only put a list header if more than 1 column */ 174 if (this->columns.size() > 1) 175 { 176 Anope::string s; 177 for (unsigned i = 0; i < this->columns.size(); ++i) 178 { 179 if (breaks.count(this->columns[i])) 180 { 181 buffer.push_back(s); 182 s = " "; 183 } 184 else if (!s.empty()) 185 s += " "; 186 s += tcolumns[i]; 187 if (i + 1 != this->columns.size()) 188 for (unsigned j = tcolumns[i].length(); j < lengths[this->columns[i]]; ++j) 189 s += " "; 190 } 191 buffer.push_back(s); 192 } 193 194 for (unsigned i = 0; i < this->entries.size(); ++i) 195 { 196 ListEntry &e = this->entries[i]; 197 198 Anope::string s; 199 for (unsigned j = 0; j < this->columns.size(); ++j) 200 { 201 if (breaks.count(this->columns[j])) 202 { 203 buffer.push_back(s); 204 s = " "; 205 } 206 else if (!s.empty()) 207 s += " "; 208 s += e[this->columns[j]]; 209 if (j + 1 != this->columns.size()) 210 for (unsigned k = e[this->columns[j]].length(); k < lengths[this->columns[j]]; ++k) 211 s += " "; 212 } 213 buffer.push_back(s); 214 } 215 } 216 217 InfoFormatter::InfoFormatter(NickCore *acc) : nc(acc), longest(0) 218 { 219 } 220 221 void InfoFormatter::Process(std::vector<Anope::string> &buffer) 222 { 223 buffer.clear(); 224 225 for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it) 226 { 227 Anope::string s; 228 for (unsigned i = it->first.length(); i < this->longest; ++i) 229 s += " "; 230 s += it->first + ": " + Language::Translate(this->nc, it->second.c_str()); 231 232 buffer.push_back(s); 233 } 234 } 235 236 Anope::string& InfoFormatter::operator[](const Anope::string &key) 237 { 238 Anope::string tkey = Language::Translate(this->nc, key.c_str()); 239 if (tkey.length() > this->longest) 240 this->longest = tkey.length(); 241 this->replies.push_back(std::make_pair(tkey, "")); 242 return this->replies.back().second; 243 } 244 245 void InfoFormatter::AddOption(const Anope::string &opt) 246 { 247 Anope::string options = Language::Translate(this->nc, "Options"); 248 Anope::string *optstr = NULL; 249 for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it) 250 { 251 if (it->first == options) 252 { 253 optstr = &it->second; 254 break; 255 } 256 } 257 if (!optstr) 258 optstr = &(*this)[_("Options")]; 259 260 if (!optstr->empty()) 261 *optstr += ", "; 262 *optstr += Language::Translate(nc, opt.c_str()); 263 } 264 265 bool Anope::IsFile(const Anope::string &filename) 266 { 267 struct stat fileinfo; 268 if (!stat(filename.c_str(), &fileinfo)) 269 return true; 270 271 return false; 272 } 273 274 time_t Anope::DoTime(const Anope::string &s) 275 { 276 if (s.empty()) 277 return 0; 278 279 int amount = 0; 280 Anope::string end; 281 282 try 283 { 284 amount = convertTo<int>(s, end, false); 285 if (!end.empty()) 286 { 287 switch (end[0]) 288 { 289 case 's': 290 return amount; 291 case 'm': 292 return amount * 60; 293 case 'h': 294 return amount * 3600; 295 case 'd': 296 return amount * 86400; 297 case 'w': 298 return amount * 86400 * 7; 299 case 'y': 300 return amount * 86400 * 365; 301 default: 302 break; 303 } 304 } 305 } 306 catch (const ConvertException &) 307 { 308 amount = -1; 309 } 310 311 return amount; 312 } 313 314 Anope::string Anope::Duration(time_t t, const NickCore *nc) 315 { 316 /* We first calculate everything */ 317 time_t years = t / 31536000; 318 time_t days = (t / 86400) % 365; 319 time_t hours = (t / 3600) % 24; 320 time_t minutes = (t / 60) % 60; 321 time_t seconds = (t) % 60; 322 323 if (!years && !days && !hours && !minutes) 324 return stringify(seconds) + " " + (seconds != 1 ? Language::Translate(nc, _("seconds")) : Language::Translate(nc, _("second"))); 325 else 326 { 327 bool need_comma = false; 328 Anope::string buffer; 329 if (years) 330 { 331 buffer = stringify(years) + " " + (years != 1 ? Language::Translate(nc, _("years")) : Language::Translate(nc, _("year"))); 332 need_comma = true; 333 } 334 if (days) 335 { 336 buffer += need_comma ? ", " : ""; 337 buffer += stringify(days) + " " + (days != 1 ? Language::Translate(nc, _("days")) : Language::Translate(nc, _("day"))); 338 need_comma = true; 339 } 340 if (hours) 341 { 342 buffer += need_comma ? ", " : ""; 343 buffer += stringify(hours) + " " + (hours != 1 ? Language::Translate(nc, _("hours")) : Language::Translate(nc, _("hour"))); 344 need_comma = true; 345 } 346 if (minutes) 347 { 348 buffer += need_comma ? ", " : ""; 349 buffer += stringify(minutes) + " " + (minutes != 1 ? Language::Translate(nc, _("minutes")) : Language::Translate(nc, _("minute"))); 350 } 351 return buffer; 352 } 353 } 354 355 Anope::string Anope::strftime(time_t t, const NickCore *nc, bool short_output) 356 { 357 tm tm = *localtime(&t); 358 char buf[BUFSIZE]; 359 strftime(buf, sizeof(buf), Language::Translate(nc, _("%b %d %H:%M:%S %Y %Z")), &tm); 360 if (short_output) 361 return buf; 362 if (t < Anope::CurTime) 363 return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s ago)")), Duration(Anope::CurTime - t, nc).c_str(), nc); 364 else if (t > Anope::CurTime) 365 return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s from now)")), Duration(t - Anope::CurTime, nc).c_str(), nc); 366 else 367 return Anope::string(buf) + " " + Language::Translate(nc, _("(now)")); 368 } 369 370 Anope::string Anope::Expires(time_t expires, const NickCore *nc) 371 { 372 if (!expires) 373 return Language::Translate(nc, NO_EXPIRE); 374 else if (expires <= Anope::CurTime) 375 return Language::Translate(nc, _("expires momentarily")); 376 else 377 { 378 char buf[256]; 379 time_t diff = expires - Anope::CurTime + 59; 380 381 if (diff >= 86400) 382 { 383 int days = diff / 86400; 384 snprintf(buf, sizeof(buf), Language::Translate(nc, days == 1 ? _("expires in %d day") : _("expires in %d days")), days); 385 } 386 else 387 { 388 if (diff <= 3600) 389 { 390 int minutes = diff / 60; 391 snprintf(buf, sizeof(buf), Language::Translate(nc, minutes == 1 ? _("expires in %d minute") : _("expires in %d minutes")), minutes); 392 } 393 else 394 { 395 int hours = diff / 3600, minutes; 396 diff -= hours * 3600; 397 minutes = diff / 60; 398 snprintf(buf, sizeof(buf), Language::Translate(nc, hours == 1 && minutes == 1 ? _("expires in %d hour, %d minute") : (hours == 1 && minutes != 1 ? _("expires in %d hour, %d minutes") : (hours != 1 && minutes == 1 ? _("expires in %d hours, %d minute") : _("expires in %d hours, %d minutes")))), hours, minutes); 399 } 400 } 401 402 return buf; 403 } 404 } 405 406 bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case_sensitive, bool use_regex) 407 { 408 size_t s = 0, m = 0, str_len = str.length(), mask_len = mask.length(); 409 410 if (use_regex && mask_len >= 2 && mask[0] == '/' && mask[mask.length() - 1] == '/') 411 { 412 Anope::string stripped_mask = mask.substr(1, mask_len - 2); 413 // This is often called with the same mask multiple times in a row, so cache it 414 static Regex *r = NULL; 415 416 if (r == NULL || r->GetExpression() != stripped_mask) 417 { 418 ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options")->Get<const Anope::string>("regexengine")); 419 if (provider) 420 { 421 try 422 { 423 delete r; 424 r = NULL; 425 // This may throw 426 r = provider->Compile(stripped_mask); 427 } 428 catch (const RegexException &ex) 429 { 430 Log(LOG_DEBUG) << ex.GetReason(); 431 } 432 } 433 else 434 { 435 delete r; 436 r = NULL; 437 } 438 } 439 440 if (r != NULL && r->Matches(str)) 441 return true; 442 443 // Fall through to non regex match 444 } 445 446 while (s < str_len && m < mask_len && mask[m] != '*') 447 { 448 char string = str[s], wild = mask[m]; 449 if (case_sensitive) 450 { 451 if (wild != string && wild != '?') 452 return false; 453 } 454 else 455 { 456 if (Anope::tolower(wild) != Anope::tolower(string) && wild != '?') 457 return false; 458 } 459 460 ++m; 461 ++s; 462 } 463 464 size_t sp = Anope::string::npos, mp = Anope::string::npos; 465 while (s < str_len) 466 { 467 char string = str[s], wild = mask[m]; 468 if (wild == '*') 469 { 470 if (++m == mask_len) 471 return 1; 472 473 mp = m; 474 sp = s + 1; 475 } 476 else if (case_sensitive) 477 { 478 if (wild == string || wild == '?') 479 { 480 ++m; 481 ++s; 482 } 483 else 484 { 485 m = mp; 486 s = sp++; 487 } 488 } 489 else 490 { 491 if (Anope::tolower(wild) == Anope::tolower(string) || wild == '?') 492 { 493 ++m; 494 ++s; 495 } 496 else 497 { 498 m = mp; 499 s = sp++; 500 } 501 } 502 } 503 504 if (m < mask_len && mask[m] == '*') 505 ++m; 506 507 return m == mask_len; 508 } 509 510 void Anope::Encrypt(const Anope::string &src, Anope::string &dest) 511 { 512 EventReturn MOD_RESULT; 513 FOREACH_RESULT(OnEncrypt, MOD_RESULT, (src, dest)); 514 static_cast<void>(MOD_RESULT); 515 } 516 517 bool Anope::Decrypt(const Anope::string &src, Anope::string &dest) 518 { 519 size_t pos = src.find(':'); 520 if (pos == Anope::string::npos) 521 { 522 Log() << "Error: Anope::Decrypt() called with invalid password string (" << src << ")"; 523 return false; 524 } 525 Anope::string hashm(src.begin(), src.begin() + pos); 526 527 EventReturn MOD_RESULT; 528 FOREACH_RESULT(OnDecrypt, MOD_RESULT, (hashm, src, dest)); 529 if (MOD_RESULT == EVENT_ALLOW) 530 return true; 531 532 return false; 533 } 534 535 Anope::string Anope::printf(const char *fmt, ...) 536 { 537 va_list args; 538 char buf[1024]; 539 va_start(args, fmt); 540 vsnprintf(buf, sizeof(buf), fmt, args); 541 va_end(args); 542 return buf; 543 } 544 545 Anope::string Anope::Hex(const Anope::string &data) 546 { 547 const char hextable[] = "0123456789abcdef"; 548 549 size_t l = data.length(); 550 Anope::string rv; 551 for (size_t i = 0; i < l; ++i) 552 { 553 unsigned char c = data[i]; 554 rv += hextable[c >> 4]; 555 rv += hextable[c & 0xF]; 556 } 557 return rv; 558 } 559 560 Anope::string Anope::Hex(const char *data, unsigned len) 561 { 562 const char hextable[] = "0123456789abcdef"; 563 564 Anope::string rv; 565 for (size_t i = 0; i < len; ++i) 566 { 567 unsigned char c = data[i]; 568 rv += hextable[c >> 4]; 569 rv += hextable[c & 0xF]; 570 } 571 return rv; 572 } 573 574 void Anope::Unhex(const Anope::string &src, Anope::string &dest) 575 { 576 size_t len = src.length(); 577 Anope::string rv; 578 for (size_t i = 0; i + 1 < len; i += 2) 579 { 580 char h = Anope::tolower(src[i]), l = Anope::tolower(src[i + 1]); 581 unsigned char byte = (h >= 'a' ? h - 'a' + 10 : h - '0') << 4; 582 byte += (l >= 'a' ? l - 'a' + 10 : l - '0'); 583 rv += byte; 584 } 585 dest = rv; 586 } 587 588 void Anope::Unhex(const Anope::string &src, char *dest, size_t sz) 589 { 590 Anope::string d; 591 Anope::Unhex(src, d); 592 593 memcpy(dest, d.c_str(), std::min(d.length() + 1, sz)); 594 } 595 596 int Anope::LastErrorCode() 597 { 598 #ifndef _WIN32 599 return errno; 600 #else 601 return GetLastError(); 602 #endif 603 } 604 605 const Anope::string Anope::LastError() 606 { 607 #ifndef _WIN32 608 return strerror(errno); 609 #else 610 char errbuf[513]; 611 DWORD err = GetLastError(); 612 if (!err) 613 return ""; 614 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, errbuf, 512, NULL); 615 return errbuf; 616 #endif 617 } 618 619 Anope::string Anope::Version() 620 { 621 #ifdef VERSION_GIT 622 return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA + " (" + VERSION_GIT + ")"; 623 #else 624 return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA; 625 #endif 626 } 627 628 Anope::string Anope::VersionShort() 629 { 630 return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH); 631 } 632 633 Anope::string Anope::VersionBuildString() 634 { 635 #ifdef REPRODUCIBLE_BUILD 636 Anope::string s = "build #" + stringify(BUILD); 637 #else 638 Anope::string s = "build #" + stringify(BUILD) + ", compiled " + Anope::compiled; 639 #endif 640 Anope::string flags; 641 642 #ifdef DEBUG_BUILD 643 flags += "D"; 644 #endif 645 #ifdef VERSION_GIT 646 flags += "G"; 647 #endif 648 #ifdef _WIN32 649 flags += "W"; 650 #endif 651 652 if (!flags.empty()) 653 s += ", flags " + flags; 654 655 return s; 656 } 657 658 int Anope::VersionMajor() { return VERSION_MAJOR; } 659 int Anope::VersionMinor() { return VERSION_MINOR; } 660 int Anope::VersionPatch() { return VERSION_PATCH; } 661 662 Anope::string Anope::NormalizeBuffer(const Anope::string &buf) 663 { 664 Anope::string newbuf; 665 666 for (unsigned i = 0, end = buf.length(); i < end; ++i) 667 { 668 switch (buf[i]) 669 { 670 /* ctrl char */ 671 case 1: 672 /* Bold ctrl char */ 673 case 2: 674 break; 675 /* Color ctrl char */ 676 case 3: 677 /* If the next character is a digit, its also removed */ 678 if (isdigit(buf[i + 1])) 679 { 680 ++i; 681 682 /* not the best way to remove colors 683 * which are two digit but no worse then 684 * how the Unreal does with +S - TSL 685 */ 686 if (isdigit(buf[i + 1])) 687 ++i; 688 689 /* Check for background color code 690 * and remove it as well 691 */ 692 if (buf[i + 1] == ',') 693 { 694 ++i; 695 696 if (isdigit(buf[i + 1])) 697 ++i; 698 /* not the best way to remove colors 699 * which are two digit but no worse then 700 * how the Unreal does with +S - TSL 701 */ 702 if (isdigit(buf[i + 1])) 703 ++i; 704 } 705 } 706 707 break; 708 /* line feed char */ 709 case 10: 710 /* carriage returns char */ 711 case 13: 712 /* Reverse ctrl char */ 713 case 22: 714 /* Italic ctrl char */ 715 case 29: 716 /* Underline ctrl char */ 717 case 31: 718 break; 719 /* A valid char gets copied into the new buffer */ 720 default: 721 newbuf += buf[i]; 722 } 723 } 724 725 return newbuf; 726 } 727 728 Anope::string Anope::Resolve(const Anope::string &host, int type) 729 { 730 Anope::string result = host; 731 732 addrinfo hints; 733 memset(&hints, 0, sizeof(hints)); 734 hints.ai_family = type; 735 736 Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << host; 737 738 addrinfo *addrresult = NULL; 739 if (getaddrinfo(host.c_str(), NULL, &hints, &addrresult) == 0) 740 { 741 sockaddrs addr; 742 memcpy(static_cast<void*>(&addr), addrresult->ai_addr, addrresult->ai_addrlen); 743 result = addr.addr(); 744 Log(LOG_DEBUG_2) << "Resolver: " << host << " -> " << result; 745 746 freeaddrinfo(addrresult); 747 } 748 749 return result; 750 } 751 752 Anope::string Anope::Random(size_t len) 753 { 754 char chars[] = { 755 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 756 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 757 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 758 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 759 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 760 }; 761 Anope::string buf; 762 for (size_t i = 0; i < len; ++i) 763 buf.append(chars[rand() % sizeof(chars)]); 764 return buf; 765 }