unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
misc.c (69113B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/misc.c 3 * Copyright (C) 1990 Jarkko Oikarinen and 4 * University of Oulu, Computing Center 5 * Copyright (C) 1999-present UnrealIRCd team 6 * 7 * See file AUTHORS in IRC package for additional names of 8 * the programmers. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 1, or (at your option) 13 * any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25 /** @file 26 * @brief Miscellaneous functions that don't fit in other files. 27 * Generally these are either simple helper functions or larger 28 * functions that don't fit in either user.c, channel.c. 29 */ 30 31 #include "unrealircd.h" 32 33 static void exit_one_client(Client *, MessageTag *mtags_i, const char *); 34 35 static const char *months[] = { 36 "January", "February", "March", "April", 37 "May", "June", "July", "August", 38 "September", "October", "November", "December" 39 }; 40 41 static const char *weekdays[] = { 42 "Sunday", "Monday", "Tuesday", "Wednesday", 43 "Thursday", "Friday", "Saturday" 44 }; 45 46 static const char *short_months[12] = { 47 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 48 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 49 }; 50 51 static const char *short_weekdays[7] = { 52 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 53 }; 54 55 typedef struct { 56 int value; /** Unique integer value of item */ 57 char character; /** Unique character assigned to item */ 58 char *name; /** Name of item */ 59 } BanActTable; 60 61 static BanActTable banacttable[] = { 62 { BAN_ACT_KILL, 'K', "kill" }, 63 { BAN_ACT_SOFT_KILL, 'i', "soft-kill" }, 64 { BAN_ACT_TEMPSHUN, 'S', "tempshun" }, 65 { BAN_ACT_SOFT_TEMPSHUN,'T', "soft-tempshun" }, 66 { BAN_ACT_SHUN, 's', "shun" }, 67 { BAN_ACT_SOFT_SHUN, 'H', "soft-shun" }, 68 { BAN_ACT_KLINE, 'k', "kline" }, 69 { BAN_ACT_SOFT_KLINE, 'I', "soft-kline" }, 70 { BAN_ACT_ZLINE, 'z', "zline" }, 71 { BAN_ACT_GLINE, 'g', "gline" }, 72 { BAN_ACT_SOFT_GLINE, 'G', "soft-gline" }, 73 { BAN_ACT_GZLINE, 'Z', "gzline" }, 74 { BAN_ACT_BLOCK, 'b', "block" }, 75 { BAN_ACT_SOFT_BLOCK, 'B', "soft-block" }, 76 { BAN_ACT_DCCBLOCK, 'd', "dccblock" }, 77 { BAN_ACT_SOFT_DCCBLOCK,'D', "soft-dccblock" }, 78 { BAN_ACT_VIRUSCHAN, 'v', "viruschan" }, 79 { BAN_ACT_SOFT_VIRUSCHAN,'V', "soft-viruschan" }, 80 { BAN_ACT_WARN, 'w', "warn" }, 81 { BAN_ACT_SOFT_WARN, 'W', "soft-warn" }, 82 { 0, 0, 0 } 83 }; 84 85 typedef struct { 86 int value; /** Unique integer value of item */ 87 char character; /** Unique character assigned to item */ 88 char *name; /** Name of item */ 89 char *irccommand; /** Raw IRC command of item (not unique!) */ 90 } SpamfilterTargetTable; 91 92 SpamfilterTargetTable spamfiltertargettable[] = { 93 { SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" }, 94 { SPAMF_USERMSG, 'p', "private", "PRIVMSG" }, 95 { SPAMF_USERNOTICE, 'n', "private-notice", "NOTICE" }, 96 { SPAMF_CHANNOTICE, 'N', "channel-notice", "NOTICE" }, 97 { SPAMF_PART, 'P', "part", "PART" }, 98 { SPAMF_QUIT, 'q', "quit", "QUIT" }, 99 { SPAMF_DCC, 'd', "dcc", "PRIVMSG" }, 100 { SPAMF_USER, 'u', "user", "NICK" }, 101 { SPAMF_AWAY, 'a', "away", "AWAY" }, 102 { SPAMF_TOPIC, 't', "topic", "TOPIC" }, 103 { SPAMF_MTAG, 'T', "message-tag", "message-tag" }, 104 { 0, 0, 0, 0 } 105 }; 106 107 /** IRC Statistics (quite useless?) */ 108 struct IRCStatistics ircstats; 109 110 /** Returns the date in rather long string */ 111 const char *long_date(time_t clock) 112 { 113 static char buf[80], plus; 114 struct tm *lt, *gm; 115 struct tm gmbuf; 116 int minswest; 117 118 if (!clock) 119 time(&clock); 120 gm = gmtime(&clock); 121 memcpy(&gmbuf, gm, sizeof(gmbuf)); 122 gm = &gmbuf; 123 lt = localtime(&clock); 124 #ifndef _WIN32 125 if (lt->tm_yday == gm->tm_yday) 126 minswest = (gm->tm_hour - lt->tm_hour) * 60 + 127 (gm->tm_min - lt->tm_min); 128 else if (lt->tm_yday > gm->tm_yday) 129 minswest = (gm->tm_hour - (lt->tm_hour + 24)) * 60; 130 else 131 minswest = ((gm->tm_hour + 24) - lt->tm_hour) * 60; 132 #else 133 minswest = (_timezone / 60); 134 #endif 135 plus = (minswest > 0) ? '-' : '+'; 136 if (minswest < 0) 137 minswest = -minswest; 138 ircsnprintf(buf, sizeof(buf), "%s %s %d %d -- %02d:%02d %c%02d:%02d", 139 weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday, 140 1900 + lt->tm_year, 141 lt->tm_hour, lt->tm_min, plus, minswest / 60, minswest % 60); 142 143 return buf; 144 } 145 146 /** Convert timestamp to a short date, a la: Wed Jun 30 21:49:08 1993 147 * @returns A short date string, or NULL if the timestamp is invalid 148 * (out of range) 149 * @param ts The timestamp 150 * @param buf The buffer to store the string (minimum size: 128 bytes), 151 * or NULL to use temporary static storage. 152 */ 153 const char *short_date(time_t ts, char *buf) 154 { 155 struct tm *t = gmtime(&ts); 156 static char retbuf[128]; 157 158 if (!buf) 159 buf = retbuf; 160 161 *buf = '\0'; 162 if (!t) 163 return NULL; 164 165 if (!strftime(buf, 128, "%a %b %d %H:%M:%S %Y", t)) 166 return NULL; 167 168 return buf; 169 } 170 171 /** Return a string with the "pretty date" - yeah, another variant */ 172 const char *pretty_date(time_t t) 173 { 174 static char buf[128]; 175 struct tm *tm; 176 177 if (!t) 178 time(&t); 179 tm = gmtime(&t); 180 snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d GMT", 181 1900 + tm->tm_year, 182 tm->tm_mon + 1, 183 tm->tm_mday, 184 tm->tm_hour, 185 tm->tm_min, 186 tm->tm_sec); 187 188 return buf; 189 } 190 191 /** Helper function for make_user_host() and friends. 192 * Fixes a string so that the first white space found becomes an end of 193 * string marker (`\-`). returns the 'fixed' string or "*" if the string 194 * was NULL length or a NULL pointer. 195 */ 196 const char *check_string(const char *s) 197 { 198 static char buf[512]; 199 static char star[2] = "*"; 200 const char *str = s; 201 202 if (BadPtr(s)) 203 return star; 204 205 for (; *s; s++) 206 { 207 if (isspace(*s)) 208 { 209 /* Because this is an unlikely scenario, we have 210 * delayed the copy until here: 211 */ 212 strlncpy(buf, s, sizeof(buf), s - str); 213 str = buf; 214 break; 215 } 216 } 217 218 return (BadPtr(str)) ? star : str; 219 } 220 221 /** Create a user@host based on the provided name and host */ 222 char *make_user_host(const char *name, const char *host) 223 { 224 static char namebuf[USERLEN + HOSTLEN + 6]; 225 226 strlncpy(namebuf, check_string(name), sizeof(namebuf), USERLEN+1); 227 strlcat(namebuf, "@", sizeof(namebuf)); 228 strlncat(namebuf, check_string(host), sizeof(namebuf), HOSTLEN+1); 229 return namebuf; 230 } 231 232 /** Create a nick!user@host string based on the provided variables. 233 * If any of the variables are NULL, it becomes * (asterisk) 234 * This is the reentrant safe version. 235 */ 236 char *make_nick_user_host_r(char *namebuf, size_t namebuflen, const char *nick, const char *name, const char *host) 237 { 238 strlncpy(namebuf, check_string(nick), namebuflen, NICKLEN+1); 239 strlcat(namebuf, "!", namebuflen); 240 strlncat(namebuf, check_string(name), namebuflen, USERLEN+1); 241 strlcat(namebuf, "@", namebuflen); 242 strlncat(namebuf, check_string(host), namebuflen, HOSTLEN+1); 243 return namebuf; 244 } 245 246 /** Create a nick!user@host string based on the provided variables. 247 * If any of the variables are NULL, it becomes * (asterisk) 248 * This version uses static storage. 249 */ 250 char *make_nick_user_host(const char *nick, const char *name, const char *host) 251 { 252 static char namebuf[NICKLEN + USERLEN + HOSTLEN + 24]; 253 254 return make_nick_user_host_r(namebuf, sizeof(namebuf), nick, name, host); 255 } 256 257 258 /** Similar to ctime() but without a potential newline and 259 * also takes a time_t value rather than a pointer. 260 */ 261 const char *myctime(time_t value) 262 { 263 static char buf[28]; 264 char *p; 265 266 strlcpy(buf, ctime(&value), sizeof buf); 267 if ((p = strchr(buf, '\n')) != NULL) 268 *p = '\0'; 269 270 return buf; 271 } 272 273 /* 274 ** get_client_name 275 ** Return the name of the client for various tracking and 276 ** admin purposes. The main purpose of this function is to 277 ** return the "socket host" name of the client, if that 278 ** differs from the advertised name (other than case). 279 ** But, this can be used to any client structure. 280 ** 281 ** Returns: 282 ** "name[user@ip#.port]" if 'showip' is true; 283 ** "name[sockethost]", if name and sockhost are different and 284 ** showip is false; else 285 ** "name". 286 ** 287 ** NOTE 1: 288 ** Watch out the allocation of "nbuf", if either client->name 289 ** or client->local->sockhost gets changed into pointers instead of 290 ** directly allocated within the structure... 291 ** 292 ** NOTE 2: 293 ** Function return either a pointer to the structure (client) or 294 ** to internal buffer (nbuf). *NEVER* use the returned pointer 295 ** to modify what it points!!! 296 */ 297 const char *get_client_name(Client *client, int showip) 298 { 299 static char nbuf[HOSTLEN * 2 + USERLEN + 5]; 300 301 if (MyConnect(client)) 302 { 303 if (showip) 304 ircsnprintf(nbuf, sizeof(nbuf), "%s[%s@%s.%u]", 305 client->name, 306 IsIdentSuccess(client) ? client->ident : "", 307 client->ip ? client->ip : "???", 308 (unsigned int)client->local->port); 309 else 310 { 311 if (mycmp(client->name, client->local->sockhost)) 312 ircsnprintf(nbuf, sizeof(nbuf), "%s[%s]", 313 client->name, client->local->sockhost); 314 else 315 return client->name; 316 } 317 return nbuf; 318 } 319 return client->name; 320 } 321 322 const char *get_client_host(Client *client) 323 { 324 static char nbuf[HOSTLEN * 2 + USERLEN + 5]; 325 326 if (!MyConnect(client)) 327 return client->name; 328 if (!client->local->hostp) 329 return get_client_name(client, FALSE); 330 ircsnprintf(nbuf, sizeof(nbuf), "%s[%-.*s@%-.*s]", 331 client->name, USERLEN, 332 IsIdentSuccess(client) ? client->ident : "", 333 HOSTLEN, client->local->hostp->h_name); 334 return nbuf; 335 } 336 337 /* 338 * Set sockhost to 'host'. Skip the user@ part of 'host' if necessary. 339 */ 340 void set_sockhost(Client *client, const char *host) 341 { 342 const char *s; 343 if ((s = strchr(host, '@'))) 344 s++; 345 else 346 s = host; 347 strlcpy(client->local->sockhost, s, sizeof(client->local->sockhost)); 348 } 349 350 /** Returns 1 if 'from' is on the allow list of 'to' */ 351 int on_dccallow_list(Client *to, Client *from) 352 { 353 Link *lp; 354 355 for(lp = to->user->dccallow; lp; lp = lp->next) 356 if (lp->flags == DCC_LINK_ME && lp->value.client == from) 357 return 1; 358 return 0; 359 } 360 361 /** Delete all DCCALLOW references. 362 * Ultimately, this should be moved to modules/dccallow.c 363 */ 364 void remove_dcc_references(Client *client) 365 { 366 Client *acptr; 367 Link *lp, *nextlp; 368 Link **lpp, *tmp; 369 int found; 370 371 lp = client->user->dccallow; 372 while(lp) 373 { 374 nextlp = lp->next; 375 acptr = lp->value.client; 376 for(found = 0, lpp = &(acptr->user->dccallow); *lpp; lpp=&((*lpp)->next)) 377 { 378 if (lp->flags == (*lpp)->flags) 379 continue; /* match only opposite types for sanity */ 380 if ((*lpp)->value.client == client) 381 { 382 if ((*lpp)->flags == DCC_LINK_ME) 383 { 384 sendto_one(acptr, NULL, ":%s %d %s :%s has been removed from " 385 "your DCC allow list for signing off", 386 me.name, RPL_DCCINFO, acptr->name, client->name); 387 } 388 tmp = *lpp; 389 *lpp = tmp->next; 390 free_link(tmp); 391 found++; 392 break; 393 } 394 } 395 396 if (!found) 397 { 398 unreal_log(ULOG_WARNING, "main", "BUG_REMOVE_DCC_REFERENCES", acptr, 399 "[BUG] remove_dcc_references: $client was in dccallowme " 400 "list of $existing_client but not in dccallowrem list!", 401 log_data_client("existing_client", client)); 402 } 403 404 free_link(lp); 405 lp = nextlp; 406 } 407 } 408 409 /* 410 * Remove all clients that depend on source_p; assumes all (S)QUITs have 411 * already been sent. we make sure to exit a server's dependent clients 412 * and servers before the server itself; exit_one_client takes care of 413 * actually removing things off llists. tweaked from +CSr31 -orabidoo 414 */ 415 static void recurse_remove_clients(Client *client, MessageTag *mtags, const char *comment) 416 { 417 Client *acptr, *next; 418 419 list_for_each_entry_safe(acptr, next, &client_list, client_node) 420 { 421 if (acptr->uplink != client) 422 continue; 423 424 exit_one_client(acptr, mtags, comment); 425 } 426 427 list_for_each_entry_safe(acptr, next, &global_server_list, client_node) 428 { 429 if (acptr->uplink != client) 430 continue; 431 432 recurse_remove_clients(acptr, mtags, comment); 433 exit_one_client(acptr, mtags, comment); 434 } 435 } 436 437 /* 438 ** Remove *everything* that depends on source_p, from all lists, and sending 439 ** all necessary QUITs and SQUITs. source_p itself is still on the lists, 440 ** and its SQUITs have been sent except for the upstream one -orabidoo 441 */ 442 static void remove_dependents(Client *client, Client *from, MessageTag *mtags, const char *comment, const char *splitstr) 443 { 444 Client *acptr; 445 446 list_for_each_entry(acptr, &global_server_list, client_node) 447 { 448 if (acptr != from && !(acptr->direction && (acptr->direction == from))) 449 sendto_one(acptr, mtags, "SQUIT %s :%s", client->name, comment); 450 } 451 452 recurse_remove_clients(client, mtags, splitstr); 453 } 454 455 /* 456 ** Exit one client, local or remote. Assuming all dependants have 457 ** been already removed, and socket closed for local client. 458 */ 459 static void exit_one_client(Client *client, MessageTag *mtags_i, const char *comment) 460 { 461 Link *lp; 462 Membership *mp; 463 464 assert(!IsMe(client)); 465 466 if (IsUser(client)) 467 { 468 MessageTag *mtags_o = NULL; 469 470 if (!MyUser(client)) 471 RunHook(HOOKTYPE_REMOTE_QUIT, client, mtags_i, comment); 472 473 new_message_special(client, mtags_i, &mtags_o, ":%s QUIT", client->name); 474 if (find_mtag(mtags_o, "unrealircd.org/real-quit-reason")) 475 quit_sendto_local_common_channels(client, mtags_o, comment); 476 else 477 sendto_local_common_channels(client, NULL, 0, mtags_o, ":%s QUIT :%s", client->name, comment); 478 free_message_tags(mtags_o); 479 480 while ((mp = client->user->channel)) 481 remove_user_from_channel(client, mp->channel, 1); 482 /* again, this is all that is needed */ 483 484 /* Clean up dccallow list and (if needed) notify other clients 485 * that have this person on DCCALLOW that the user just left/got removed. 486 */ 487 remove_dcc_references(client); 488 489 /* For remote clients, we need to check for any outstanding async 490 * connects attached to this 'client', and set those records to NULL. 491 * Why not for local? Well, we already do that in close_connection ;) 492 */ 493 if (!MyConnect(client)) 494 unrealdns_delreq_bycptr(client); 495 } 496 497 /* Free module related data for this client */ 498 moddata_free_client(client); 499 if (MyConnect(client)) 500 moddata_free_local_client(client); 501 502 /* Remove client from the client list */ 503 if (*client->id) 504 { 505 del_from_id_hash_table(client->id, client); 506 *client->id = '\0'; 507 } 508 if (*client->name) 509 del_from_client_hash_table(client->name, client); 510 if (remote_rehash_client == client) 511 remote_rehash_client = NULL; /* client did a /REHASH and QUIT before rehash was complete */ 512 remove_client_from_list(client); 513 } 514 515 /** Exit this IRC client, and all the dependents (users, servers) if this is a server. 516 * @param client The client to exit. 517 * @param recv_mtags Message tags to use as a base (if any). 518 * @param comment The (s)quit message 519 */ 520 void exit_client(Client *client, MessageTag *recv_mtags, const char *comment) 521 { 522 exit_client_ex(client, client->direction, recv_mtags, comment); 523 } 524 525 /** Exit this IRC client, and all the dependents (users, servers) if this is a server. 526 * @param client The client to exit. 527 * @param recv_mtags Message tags to use as a base (if any). 528 * @param comment The (s)quit message 529 */ 530 void exit_client_fmt(Client *client, MessageTag *recv_mtags, FORMAT_STRING(const char *pattern), ...) 531 { 532 char comment[512]; 533 534 va_list vl; 535 va_start(vl, pattern); 536 vsnprintf(comment, sizeof(comment), pattern, vl); 537 va_end(vl); 538 539 exit_client_ex(client, client->direction, recv_mtags, comment); 540 } 541 542 /** Exit this IRC client, and all the dependents (users, servers) if this is a server. 543 * @param client The client to exit. 544 * @param recv_mtags Message tags to use as a base (if any). 545 * @param comment The (s)quit message 546 */ 547 void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, const char *comment) 548 { 549 long long on_for; 550 ConfigItem_listen *listen_conf; 551 MessageTag *mtags_generated = NULL; 552 553 if (IsDead(client)) 554 return; /* Already marked as exited */ 555 556 /* We replace 'recv_mtags' here with a newly 557 * generated id if 'recv_mtags' is NULL or is 558 * non-NULL and contains no msgid etc. 559 * This saves us from doing a new_message() 560 * prior to the exit_client() call at around 561 * 100+ places elsewhere in the code. 562 */ 563 new_message(client, recv_mtags, &mtags_generated); 564 recv_mtags = mtags_generated; 565 566 if (MyConnect(client)) 567 { 568 if (client->local->class) 569 { 570 client->local->class->clients--; 571 if ((client->local->class->flag.temporary) && !client->local->class->clients && !client->local->class->xrefcount) 572 { 573 delete_classblock(client->local->class); 574 client->local->class = NULL; 575 } 576 } 577 if (IsUser(client)) 578 irccounts.me_clients--; 579 if (client->server && client->server->conf) 580 { 581 client->server->conf->refcount--; 582 if (!client->server->conf->refcount 583 && client->server->conf->flag.temporary) 584 { 585 delete_linkblock(client->server->conf); 586 client->server->conf = NULL; 587 } 588 } 589 if (IsServer(client)) 590 { 591 irccounts.me_servers--; 592 if (!IsServerDisconnectLogged(client)) 593 { 594 unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, 595 "Lost server link to $client [$client.ip]: $reason", 596 log_data_string("reason", comment)); 597 } 598 } 599 free_pending_net(client); 600 SetClosing(client); 601 if (IsUser(client)) 602 { 603 long connected_time = TStime() - client->local->creationtime; 604 RunHook(HOOKTYPE_LOCAL_QUIT, client, recv_mtags, comment); 605 unreal_log(ULOG_INFO, "connect", "LOCAL_CLIENT_DISCONNECT", client, 606 "Client exiting: $client ($client.user.username@$client.hostname) [$client.ip] ($reason)", 607 log_data_string("extended_client_info", get_connect_extinfo(client)), 608 log_data_string("reason", comment), 609 log_data_integer("connected_time", connected_time)); 610 } else 611 if (IsUnknown(client)) 612 { 613 RunHook(HOOKTYPE_UNKUSER_QUIT, client, recv_mtags, comment); 614 } 615 616 if (client->local->fd >= 0 && !IsConnecting(client)) 617 { 618 if (!IsControl(client) && !IsRPC(client)) 619 sendto_one(client, NULL, "ERROR :Closing Link: %s (%s)", get_client_name(client, FALSE), comment); 620 } 621 close_connection(client); 622 } 623 else if (IsUser(client) && !IsULine(client)) 624 { 625 if (client->uplink != &me) 626 { 627 unreal_log(ULOG_INFO, "connect", "REMOTE_CLIENT_DISCONNECT", client, 628 "Client exiting: $client ($client.user.username@$client.hostname) [$client.ip] ($reason)", 629 log_data_string("extended_client_info", get_connect_extinfo(client)), 630 log_data_string("reason", comment), 631 log_data_string("from_server_name", client->user->server)); 632 } 633 } 634 635 /* 636 * Recurse down the client list and get rid of clients who are no 637 * longer connected to the network (from my point of view) 638 * Only do this expensive stuff if exited==server -Donwulff 639 */ 640 if (IsServer(client)) 641 { 642 char splitstr[HOSTLEN + HOSTLEN + 2]; 643 Client *acptr, *next; 644 645 assert(client->server != NULL && client->uplink != NULL); 646 647 if (FLAT_MAP) 648 strlcpy(splitstr, "*.net *.split", sizeof splitstr); 649 else 650 ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->uplink->name, client->name); 651 652 remove_dependents(client, origin, recv_mtags, comment, splitstr); 653 654 /* Special case for remote async RPC, server.rehash in particular.. */ 655 list_for_each_entry_safe(acptr, next, &rpc_remote_list, client_node) 656 if (!strncmp(client->id, acptr->id, SIDLEN)) 657 free_client(acptr); 658 659 RunHook(HOOKTYPE_SERVER_QUIT, client, recv_mtags); 660 } 661 else if (IsUser(client) && !IsKilled(client)) 662 { 663 sendto_server(client, 0, 0, recv_mtags, ":%s QUIT :%s", client->id, comment); 664 } 665 666 /* Finally, the client/server itself exits.. */ 667 exit_one_client(client, recv_mtags, comment); 668 669 free_message_tags(mtags_generated); 670 } 671 672 /** Initialize the (quite useless) IRC statistics */ 673 void initstats(void) 674 { 675 memset(&ircstats, 0, sizeof(ircstats)); 676 } 677 678 /** Verify operator count, to catch bugs introduced by flawed services */ 679 void verify_opercount(Client *orig, const char *tag) 680 { 681 int counted = 0; 682 Client *client; 683 char text[2048]; 684 685 list_for_each_entry(client, &client_list, client_node) 686 { 687 if (IsOper(client) && !IsHideOper(client)) 688 counted++; 689 } 690 if (counted == irccounts.operators) 691 return; 692 unreal_log(ULOG_WARNING, "main", "BUG_LUSERS_OPERS", orig, 693 "[BUG] Operator count bug at $where! Value in /LUSERS is $opers, " 694 "we counted $counted_opers, " 695 "triggered by $client.details on $client.user.servername", 696 log_data_integer("opers", irccounts.operators), 697 log_data_integer("counted_opers", counted), 698 log_data_string("where", tag)); 699 irccounts.operators = counted; 700 } 701 702 /** Check if the specified hostname does not contain forbidden characters. 703 * @param host The host name to check 704 * @param strict If set to 1 then we also check if the hostname 705 * resembles an IP address (eg contains ':') and 706 * some other stuff that we don't consider valid 707 * in actual DNS names (eg '/'). 708 * @returns 1 if valid, 0 if not. 709 */ 710 int valid_host(const char *host, int strict) 711 { 712 const char *p; 713 714 if (!*host) 715 return 0; /* must at least contain something */ 716 717 if (strlen(host) > HOSTLEN) 718 return 0; /* too long hosts are invalid too */ 719 720 if (strict) 721 { 722 for (p=host; *p; p++) 723 if (!isalnum(*p) && !strchr("_-.", *p)) 724 return 0; 725 } else { 726 for (p=host; *p; p++) 727 if (!isalnum(*p) && !strchr("_-.:/", *p)) 728 return 0; 729 } 730 731 return 1; 732 } 733 734 /** Check if the specified ident / user name does not contain forbidden characters. 735 * @param username The username / ident to check 736 * @returns 1 if valid, 0 if not. 737 */ 738 int valid_username(const char *username) 739 { 740 const char *s; 741 742 if (strlen(username) > USERLEN) 743 return 0; /* Too long */ 744 745 for (s = username; *s; s++) 746 { 747 if ((*s == '~') && (s == username)) 748 continue; 749 if (!isallowed(*s)) 750 return 0; 751 } 752 753 return 1; 754 } 755 756 /** Check validity of a vhost which can be both in 'host' or 'user@host' format. 757 * This will call valid_username() and valid_host(xxx, 0) accordingly. 758 * @param userhost the "host" or "user@host" 759 * @returns 1 if valid, 0 if not. 760 */ 761 int valid_vhost(const char *userhost) 762 { 763 char uhost[512], *p; 764 const char *host = userhost; 765 766 strlcpy(uhost, userhost, sizeof(uhost)); 767 768 if ((p = strchr(uhost, '@'))) 769 { 770 *p++ = '\0'; 771 if (!valid_username(uhost)) 772 return 0; 773 host = p; 774 } 775 776 if (!valid_host(host, 0)) 777 return 0; 778 779 return 1; 780 } 781 782 /*|| BAN ACTION ROUTINES FOLLOW ||*/ 783 784 /** Converts a banaction string (eg: "kill") to an integer value (eg: BAN_ACT_KILL) */ 785 BanAction banact_stringtoval(const char *s) 786 { 787 BanActTable *b; 788 789 for (b = &banacttable[0]; b->value; b++) 790 if (!strcasecmp(s, b->name)) 791 return b->value; 792 return 0; 793 } 794 795 /** Converts a banaction character (eg: 'K') to an integer value (eg: BAN_ACT_KILL) */ 796 BanAction banact_chartoval(char c) 797 { 798 BanActTable *b; 799 800 for (b = &banacttable[0]; b->value; b++) 801 if (b->character == c) 802 return b->value; 803 return 0; 804 } 805 806 /** Converts a banaction value (eg: BAN_ACT_KILL) to a character value (eg: 'k') */ 807 char banact_valtochar(BanAction val) 808 { 809 BanActTable *b; 810 811 for (b = &banacttable[0]; b->value; b++) 812 if (b->value == val) 813 return b->character; 814 return '\0'; 815 } 816 817 /** Converts a banaction value (eg: BAN_ACT_KLINE) to a string (eg: "kline") */ 818 const char *banact_valtostring(BanAction val) 819 { 820 BanActTable *b; 821 822 for (b = &banacttable[0]; b->value; b++) 823 if (b->value == val) 824 return b->name; 825 return "UNKNOWN"; 826 } 827 828 /*|| BAN TARGET ROUTINES FOLLOW ||*/ 829 830 /** Extract target flags from string 's'. */ 831 int spamfilter_gettargets(const char *s, Client *client) 832 { 833 SpamfilterTargetTable *e; 834 int flags = 0; 835 836 for (; *s; s++) 837 { 838 for (e = &spamfiltertargettable[0]; e->value; e++) 839 if (e->character == *s) 840 { 841 flags |= e->value; 842 break; 843 } 844 if (!e->value && client) 845 { 846 sendnotice(client, "Unknown target type '%c'", *s); 847 return 0; 848 } 849 } 850 return flags; 851 } 852 853 /** Convert a string with a targetname to an integer value */ 854 int spamfilter_getconftargets(const char *s) 855 { 856 SpamfilterTargetTable *e; 857 858 for (e = &spamfiltertargettable[0]; e->value; e++) 859 if (!strcmp(s, e->name)) 860 return e->value; 861 return 0; 862 } 863 864 /** Create a string with (multiple) targets from an integer mask */ 865 char *spamfilter_target_inttostring(int v) 866 { 867 static char buf[128]; 868 SpamfilterTargetTable *e; 869 char *p = buf; 870 871 for (e = &spamfiltertargettable[0]; e->value; e++) 872 if (v & e->value) 873 *p++ = e->character; 874 *p = '\0'; 875 return buf; 876 } 877 878 /** Replace underscores back to the space character. 879 * This is used for the spamfilter reason. 880 */ 881 char *unreal_decodespace(const char *s) 882 { 883 const char *i; 884 static char buf[512], *o; 885 886 for (i = s, o = buf; (*i) && (o < buf+510); i++) 887 if (*i == '_') 888 { 889 if (i[1] != '_') 890 *o++ = ' '; 891 else { 892 *o++ = '_'; 893 i++; 894 } 895 } 896 else 897 *o++ = *i; 898 *o = '\0'; 899 return buf; 900 } 901 902 /** Replace spaces to underscore characters. 903 * This is used for the spamfilter reason. 904 */ 905 char *unreal_encodespace(const char *s) 906 { 907 const char *i; 908 static char buf[512], *o; 909 910 if (!s) 911 return NULL; /* NULL in = NULL out */ 912 913 for (i = s, o = buf; (*i) && (o < buf+509); i++) 914 { 915 if (*i == ' ') 916 *o++ = '_'; 917 else if (*i == '_') 918 { 919 *o++ = '_'; 920 *o++ = '_'; 921 } 922 else 923 *o++ = *i; 924 } 925 *o = '\0'; 926 return buf; 927 } 928 929 /** This is basically only used internally by match_spamfilter()... */ 930 const char *cmdname_by_spamftarget(int target) 931 { 932 SpamfilterTargetTable *e; 933 934 for (e = &spamfiltertargettable[0]; e->value; e++) 935 if (e->value == target) 936 return e->irccommand; 937 return "???"; 938 } 939 940 /** Returns 1 if this is a channel from set::auto-join or set::oper-auto-join */ 941 int is_autojoin_chan(const char *chname) 942 { 943 char buf[512]; 944 char *p, *name; 945 946 if (OPER_AUTO_JOIN_CHANS) 947 { 948 strlcpy(buf, OPER_AUTO_JOIN_CHANS, sizeof(buf)); 949 950 for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) 951 if (!strcasecmp(name, chname)) 952 return 1; 953 } 954 955 if (AUTO_JOIN_CHANS) 956 { 957 strlcpy(buf, AUTO_JOIN_CHANS, sizeof(buf)); 958 959 for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) 960 if (!strcasecmp(name, chname)) 961 return 1; 962 } 963 964 return 0; 965 } 966 967 /** Add name entries from config */ 968 void unreal_add_names(NameList **n, ConfigEntry *ce) 969 { 970 if (ce->items) 971 { 972 ConfigEntry *cep; 973 for (cep = ce->items; cep; cep = cep->next) 974 _add_name_list(n, cep->value ? cep->value : cep->name); 975 } else 976 if (ce->value) 977 { 978 _add_name_list(n, ce->value); 979 } 980 } 981 982 /** Add name/value entries from config */ 983 void unreal_add_name_values(NameValuePrioList **n, const char *name, ConfigEntry *ce) 984 { 985 if (ce->items) 986 { 987 ConfigEntry *cep; 988 for (cep = ce->items; cep; cep = cep->next) 989 add_nvplist(n, 0, name, cep->value ? cep->value : cep->name); 990 } else 991 if (ce->value) 992 { 993 add_nvplist(n, 0, name, ce->value); 994 } 995 } 996 997 /** Prints the name:value pair of a NameValuePrioList */ 998 const char *namevalue(NameValuePrioList *n) 999 { 1000 static char buf[512]; 1001 1002 if (!n->name) 1003 return ""; 1004 1005 if (!n->value) 1006 return n->name; 1007 1008 snprintf(buf, sizeof(buf), "%s:%s", n->name, n->value); 1009 return buf; 1010 } 1011 1012 /** Version of namevalue() but replaces spaces with underscores. 1013 * Used in for example numeric sending routines where a field 1014 * may not contain any spaces. 1015 */ 1016 const char *namevalue_nospaces(NameValuePrioList *n) 1017 { 1018 static char buf[512]; 1019 char *p; 1020 1021 if (!n->name) 1022 return ""; 1023 1024 if (!n->value) 1025 strlcpy(buf, n->name, sizeof(buf)); 1026 1027 snprintf(buf, sizeof(buf), "%s:%s", n->name, n->value); 1028 1029 /* Replace spaces with underscores */ 1030 for (p=buf; *p; p++) 1031 if (*p == ' ') 1032 *p = '_'; 1033 1034 return buf; 1035 } 1036 1037 /** Our own strcasestr implementation because strcasestr is 1038 * often not available or is not working correctly. 1039 */ 1040 char *our_strcasestr(const char *haystack, const char *needle) 1041 { 1042 int i; 1043 int nlength = strlen(needle); 1044 int hlength = strlen(haystack); 1045 1046 if (nlength > hlength) 1047 return NULL; 1048 1049 if (hlength <= 0) 1050 return NULL; 1051 1052 if (nlength <= 0) 1053 return (char *)haystack; 1054 1055 for (i = 0; i <= (hlength - nlength); i++) 1056 { 1057 if (strncasecmp (haystack + i, needle, nlength) == 0) 1058 return (char *)(haystack + i); 1059 } 1060 1061 return NULL; /* not found */ 1062 } 1063 1064 /** Add a title to the users' WHOIS ("special whois"). Broadcast change to servers. 1065 * @param client The client 1066 * @param tag A tag used internally and for server-to-server traffic, 1067 * not visible to end-users. 1068 * @param priority Priority - for ordering multiple swhois entries 1069 * (lower number = further up in the swhoises list in WHOIS) 1070 * @param swhois The actual special whois title (string) you want to add to the user 1071 * @param from Who added this entry 1072 * @param skip Which server(-side) to skip broadcasting this entry to. 1073 */ 1074 int swhois_add(Client *client, const char *tag, int priority, const char *swhois, Client *from, Client *skip) 1075 { 1076 SWhois *s; 1077 1078 /* Make sure the line isn't added yet. If so, then bail out silently. */ 1079 for (s = client->user->swhois; s; s = s->next) 1080 if (!strcmp(s->line, swhois)) 1081 return -1; /* exists */ 1082 1083 s = safe_alloc(sizeof(SWhois)); 1084 safe_strdup(s->line, swhois); 1085 safe_strdup(s->setby, tag); 1086 s->priority = priority; 1087 AddListItemPrio(s, client->user->swhois, s->priority); 1088 1089 sendto_server(skip, 0, PROTO_EXTSWHOIS, NULL, ":%s SWHOIS %s :%s", 1090 from->id, client->id, swhois); 1091 1092 sendto_server(skip, PROTO_EXTSWHOIS, 0, NULL, ":%s SWHOIS %s + %s %d :%s", 1093 from->id, client->id, tag, priority, swhois); 1094 1095 return 0; 1096 } 1097 1098 /** Delete swhois title(s). 1099 * Delete swhois by tag and swhois. Then broadcast this change to all other servers. 1100 * @param client The client 1101 * @param tag A tag used internally and for server-to-server traffic, 1102 * not visible to end-users. 1103 * @param swhois The actual special whois title (string) you are removing 1104 * @param from Who added this entry earlier on 1105 * @param skip Which server(-side) to skip broadcasting this entry to. 1106 * @note If you use swhois "*" then it will remove all swhois titles for that tag 1107 */ 1108 int swhois_delete(Client *client, const char *tag, const char *swhois, Client *from, Client *skip) 1109 { 1110 SWhois *s, *s_next; 1111 int ret = -1; /* default to 'not found' */ 1112 1113 for (s = client->user->swhois; s; s = s_next) 1114 { 1115 s_next = s->next; 1116 1117 /* If ( same swhois or "*" ) AND same tag */ 1118 if ( ((!strcmp(s->line, swhois) || !strcmp(swhois, "*")) && 1119 !strcmp(s->setby, tag))) 1120 { 1121 DelListItem(s, client->user->swhois); 1122 safe_free(s->line); 1123 safe_free(s->setby); 1124 safe_free(s); 1125 1126 sendto_server(skip, 0, PROTO_EXTSWHOIS, NULL, ":%s SWHOIS %s :", 1127 from->id, client->id); 1128 1129 sendto_server(skip, PROTO_EXTSWHOIS, 0, NULL, ":%s SWHOIS %s - %s %d :%s", 1130 from->id, client->id, tag, 0, swhois); 1131 1132 ret = 0; 1133 } 1134 } 1135 1136 return ret; 1137 } 1138 1139 /** Is this user using a websocket? (LOCAL USERS ONLY) */ 1140 int IsWebsocket(Client *client) 1141 { 1142 ModDataInfo *md = findmoddata_byname("websocket", MODDATATYPE_CLIENT); 1143 if (!md) 1144 return 0; /* websocket module not loaded */ 1145 return (MyConnect(client) && moddata_client(client, md).ptr) ? 1 : 0; 1146 } 1147 1148 /** Generic function to inform the user he/she has been banned. 1149 * @param client The affected client. 1150 * @param bantype The ban type, such as: "K-Lined", "G-Lined" or "realname". 1151 * @param reason The specified reason. 1152 * @param global Whether the ban is global (1) or for this server only (0) 1153 * @param noexit Set this to NO_EXIT_CLIENT to make us not call exit_client(). 1154 * This is really only needed from the accept code, do not 1155 * use it anywhere else. No really, never. 1156 * 1157 * @note This function will call exit_client() appropriately. 1158 */ 1159 void banned_client(Client *client, const char *bantype, const char *reason, int global, int noexit) 1160 { 1161 char buf[512]; 1162 char *fmt = global ? iConf.reject_message_gline : iConf.reject_message_kline; 1163 const char *vars[6], *values[6]; 1164 MessageTag *mtags = NULL; 1165 1166 if (!MyConnect(client)) 1167 abort(); 1168 1169 /* This was: "You are not welcome on this %s. %s: %s. %s" but is now dynamic: */ 1170 vars[0] = "bantype"; 1171 values[0] = bantype; 1172 vars[1] = "banreason"; 1173 values[1] = reason; 1174 vars[2] = "klineaddr"; 1175 values[2] = KLINE_ADDRESS; 1176 vars[3] = "glineaddr"; 1177 values[3] = GLINE_ADDRESS ? GLINE_ADDRESS : KLINE_ADDRESS; /* fallback to klineaddr */ 1178 vars[4] = "ip"; 1179 values[4] = GetIP(client); 1180 vars[5] = NULL; 1181 values[5] = NULL; 1182 buildvarstring(fmt, buf, sizeof(buf), vars, values); 1183 1184 /* This is a bit extensive but we will send both a YOUAREBANNEDCREEP 1185 * and a notice to the user. 1186 * The YOUAREBANNEDCREEP will be helpful for the client since it makes 1187 * clear the user should not quickly reconnect, as (s)he is banned. 1188 * The notice still needs to be there because it stands out well 1189 * at most IRC clients. 1190 */ 1191 if (noexit != NO_EXIT_CLIENT) 1192 { 1193 sendnumeric(client, ERR_YOUREBANNEDCREEP, buf); 1194 sendnotice(client, "%s", buf); 1195 } else { 1196 send_raw_direct(client, ":%s %d %s :%s", 1197 me.name, ERR_YOUREBANNEDCREEP, 1198 (*client->name ? client->name : "*"), 1199 buf); 1200 send_raw_direct(client, ":%s NOTICE %s :%s", 1201 me.name, (*client->name ? client->name : "*"), buf); 1202 } 1203 1204 /* The final message in the ERROR is shorter. */ 1205 if (HIDE_BAN_REASON && IsRegistered(client)) 1206 { 1207 /* Hide the ban reason, but put the real reason in unrealircd.org/real-quit-reason */ 1208 MessageTag *m = safe_alloc(sizeof(MessageTag)); 1209 safe_strdup(m->name, "unrealircd.org/real-quit-reason"); 1210 snprintf(buf, sizeof(buf), "Banned (%s): %s", bantype, reason); 1211 safe_strdup(m->value, buf); 1212 AddListItem(m, mtags); 1213 /* And the quit reason for anyone else, goes here.. */ 1214 snprintf(buf, sizeof(buf), "Banned (%s)", bantype); 1215 } else { 1216 snprintf(buf, sizeof(buf), "Banned (%s): %s", bantype, reason); 1217 } 1218 1219 if (noexit != NO_EXIT_CLIENT) 1220 { 1221 exit_client(client, mtags, buf); 1222 } else { 1223 /* Special handling for direct Z-line code */ 1224 send_raw_direct(client, "ERROR :Closing Link: [%s] (%s)", 1225 client->ip, buf); 1226 } 1227 safe_free_message_tags(mtags); 1228 } 1229 1230 /** Our stpcpy implementation - discouraged due to lack of bounds checking */ 1231 char *mystpcpy(char *dst, const char *src) 1232 { 1233 for (; *src; src++) 1234 *dst++ = *src; 1235 *dst = '\0'; 1236 return dst; 1237 } 1238 1239 /** Helper function for send_channel_modes_sjoin3() and cmd_sjoin() 1240 * to build the SJSBY prefix which is <seton,setby> to 1241 * communicate when the ban was set and by whom. 1242 * @param buf The buffer to write to 1243 * @param setby The setter of the "ban" 1244 * @param seton The time the "ban" was set 1245 * @retval The number of bytes written EXCLUDING the NUL byte, 1246 * so similar to what strlen() would have returned. 1247 * @note Caller must ensure that the buffer 'buf' is of sufficient size. 1248 */ 1249 size_t add_sjsby(char *buf, const char *setby, time_t seton) 1250 { 1251 char tbuf[32]; 1252 char *p = buf; 1253 1254 snprintf(tbuf, sizeof(tbuf), "%ld", (long)seton); 1255 1256 *p++ = '<'; 1257 p = mystpcpy(p, tbuf); 1258 *p++ = ','; 1259 p = mystpcpy(p, setby); 1260 *p++ = '>'; 1261 *p = '\0'; 1262 1263 return p - buf; 1264 } 1265 1266 /** Concatenate the entire parameter string. 1267 * The function will take care of spaces in the final parameter (if any). 1268 * @param buf The buffer to output in. 1269 * @param len Length of the buffer. 1270 * @param parc Parameter count, ircd style. 1271 * @param parv Parameters, ircd style, so we will start at parv[1]. 1272 * @section ex1 Example 1273 * @code 1274 * char buf[512]; 1275 * concat_params(buf, sizeof(buf), parc, parv); 1276 * sendto_server(client, 0, 0, recv_mtags, ":%s SOMECOMMAND %s", client->name, buf); 1277 * @endcode 1278 */ 1279 void concat_params(char *buf, int len, int parc, const char *parv[]) 1280 { 1281 int i; 1282 1283 *buf = '\0'; 1284 for (i = 1; i < parc; i++) 1285 { 1286 const char *param = parv[i]; 1287 1288 if (!param) 1289 break; 1290 1291 if (*buf) 1292 strlcat(buf, " ", len); 1293 1294 if (strchr(param, ' ') || (*param == ':')) 1295 { 1296 /* Last parameter, with : */ 1297 strlcat(buf, ":", len); 1298 strlcat(buf, parv[i], len); 1299 break; 1300 } 1301 strlcat(buf, parv[i], len); 1302 } 1303 } 1304 1305 /** Find a particular message-tag in the 'mtags' list */ 1306 MessageTag *find_mtag(MessageTag *mtags, const char *token) 1307 { 1308 for (; mtags; mtags = mtags->next) 1309 if (!strcmp(mtags->name, token)) 1310 return mtags; 1311 return NULL; 1312 } 1313 1314 /** Free all message tags in the list 'm' */ 1315 void free_message_tags(MessageTag *m) 1316 { 1317 MessageTag *m_next; 1318 1319 for (; m; m = m_next) 1320 { 1321 m_next = m->next; 1322 safe_free(m->name); 1323 safe_free(m->value); 1324 safe_free(m); 1325 } 1326 } 1327 1328 /** Duplicate a MessageTag structure. 1329 * @note This duplicate a single MessageTag. 1330 * It does not duplicate an entire linked list. 1331 */ 1332 MessageTag *duplicate_mtag(MessageTag *mtag) 1333 { 1334 MessageTag *m = safe_alloc(sizeof(MessageTag)); 1335 safe_strdup(m->name, mtag->name); 1336 safe_strdup(m->value, mtag->value); 1337 return m; 1338 } 1339 1340 /** New message. Either really brand new, or inherited from other servers. 1341 * This function calls modules so they can add tags, such as: 1342 * msgid, time and account. 1343 */ 1344 void new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list) 1345 { 1346 Hook *h; 1347 for (h = Hooks[HOOKTYPE_NEW_MESSAGE]; h; h = h->next) 1348 (*(h->func.voidfunc))(sender, recv_mtags, mtag_list, NULL); 1349 } 1350 1351 /** New message - SPECIAL edition. Either really brand new, or inherited 1352 * from other servers. 1353 * This function calls modules so they can add tags, such as: 1354 * msgid, time and account. 1355 * This special version deals in a special way with msgid in particular. 1356 * The pattern and vararg create a 'signature', this is normally 1357 * identical to the message that is sent to clients (end-users). 1358 * For example ":xyz JOIN #chan". 1359 */ 1360 void new_message_special(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, FORMAT_STRING(const char *pattern), ...) 1361 { 1362 Hook *h; 1363 va_list vl; 1364 char buf[512]; 1365 1366 va_start(vl, pattern); 1367 ircvsnprintf(buf, sizeof(buf), pattern, vl); 1368 va_end(vl); 1369 1370 for (h = Hooks[HOOKTYPE_NEW_MESSAGE]; h; h = h->next) 1371 (*(h->func.voidfunc))(sender, recv_mtags, mtag_list, buf); 1372 } 1373 1374 /** Default handler for parse_message_tags(). 1375 * This is only used if the 'mtags' module is NOT loaded, 1376 * which would be quite unusual, but possible. 1377 */ 1378 void parse_message_tags_default_handler(Client *client, char **str, MessageTag **mtag_list) 1379 { 1380 /* Just skip everything until the space character */ 1381 for (; **str && **str != ' '; *str = *str + 1); 1382 } 1383 1384 /** Default handler for mtags_to_string(). 1385 * This is only used if the 'mtags' module is NOT loaded, 1386 * which would be quite unusual, but possible. 1387 */ 1388 const char *mtags_to_string_default_handler(MessageTag *m, Client *client) 1389 { 1390 return NULL; 1391 } 1392 1393 /** Default handler for add_silence(). 1394 * This is only used if the 'silence' module is NOT loaded, 1395 * which would be unusual, but possible. 1396 */ 1397 int add_silence_default_handler(Client *client, const char *mask, int senderr) 1398 { 1399 return 0; 1400 } 1401 1402 /** Default handler for del_silence(). 1403 * This is only used if the 'silence' module is NOT loaded, 1404 * which would be unusual, but possible. 1405 */ 1406 int del_silence_default_handler(Client *client, const char *mask) 1407 { 1408 return 0; 1409 } 1410 1411 /** Default handler for is_silenced(). 1412 * This is only used if the 'silence' module is NOT loaded, 1413 * which would be unusual, but possible. 1414 */ 1415 int is_silenced_default_handler(Client *client, Client *acptr) 1416 { 1417 return 0; 1418 } 1419 1420 /** Generate a BATCH id. 1421 * This can be used in a :serv BATCH +%s ... message 1422 */ 1423 void generate_batch_id(char *str) 1424 { 1425 gen_random_alnum(str, BATCHLEN); 1426 } 1427 1428 /** A default handler if labeled-response module is not loaded. 1429 * Normally a NOOP, but since caller will safe_free it 1430 * later we do actually allocate something. 1431 */ 1432 void *labeled_response_save_context_default_handler(void) 1433 { 1434 return safe_alloc(8); 1435 } 1436 1437 /** A default handler for if labeled-response module is not loaded */ 1438 void labeled_response_set_context_default_handler(void *ctx) 1439 { 1440 } 1441 1442 /** A default handler for if labeled-response module is not loaded */ 1443 void labeled_response_force_end_default_handler(void) 1444 { 1445 } 1446 1447 /** Ad default handler for if the slog module is not loaded */ 1448 void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized) 1449 { 1450 } 1451 1452 int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost) 1453 { 1454 return 0; 1455 } 1456 1457 void webserver_send_response_default_handler(Client *client, int status, char *msg) 1458 { 1459 } 1460 1461 void webserver_close_client_default_handler(Client *client) 1462 { 1463 } 1464 1465 int webserver_handle_body_default_handler(Client *client, WebRequest *web, const char *readbuf, int length) 1466 { 1467 return 0; 1468 } 1469 1470 void rpc_response_default_handler(Client *client, json_t *request, json_t *result) 1471 { 1472 } 1473 1474 void rpc_error_default_handler(Client *client, json_t *request, JsonRpcError error_code, const char *error_message) 1475 { 1476 } 1477 1478 void rpc_error_fmt_default_handler(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...) 1479 { 1480 } 1481 1482 void rpc_send_request_to_remote_default_handler(Client *source, Client *target, json_t *request) 1483 { 1484 } 1485 1486 void rpc_send_response_to_remote_default_handler(Client *source, Client *target, json_t *response) 1487 { 1488 } 1489 1490 int rrpc_supported_simple_default_handler(Client *target, char **problem_server) 1491 { 1492 if (problem_server) 1493 *problem_server = me.name; 1494 return 0; 1495 } 1496 1497 int rrpc_supported_default_handler(Client *target, const char *module, const char *minimum_version, char **problem_server) 1498 { 1499 if (problem_server) 1500 *problem_server = me.name; 1501 return 0; 1502 } 1503 1504 int websocket_handle_websocket_default_handler(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)) 1505 { 1506 return -1; 1507 } 1508 1509 int websocket_create_packet_default_handler(int opcode, char **buf, int *len) 1510 { 1511 return -1; 1512 } 1513 1514 int websocket_create_packet_ex_default_handler(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize) 1515 { 1516 return -1; 1517 } 1518 1519 int websocket_create_packet_simple_default_handler(int opcode, const char **buf, int *len) 1520 { 1521 return -1; 1522 } 1523 1524 void mtag_add_issued_by_default_handler(MessageTag **mtags, Client *client, MessageTag *recv_mtags) 1525 { 1526 } 1527 1528 /** my_timegm: mktime()-like function which will use GMT/UTC. 1529 * Strangely enough there is no standard function for this. 1530 * On some *NIX OS's timegm() may be available, sometimes only 1531 * with the help of certain #define's which we may or may 1532 * not do. 1533 * Windows provides _mkgmtime(). 1534 * In the other cases the man pages and basically everyone 1535 * suggests to set TZ to empty prior to calling mktime and 1536 * restoring it after the call. Whut? How ridiculous is that? 1537 */ 1538 time_t my_timegm(struct tm *tm) 1539 { 1540 #if HAVE_TIMEGM 1541 return timegm(tm); 1542 #elif defined(_WIN32) 1543 return _mkgmtime(tm); 1544 #else 1545 time_t ret; 1546 char *tz = NULL; 1547 1548 safe_strdup(tz, getenv("TZ")); 1549 setenv("TZ", "", 1); 1550 ret = mktime(tm); 1551 if (tz) 1552 { 1553 setenv("TZ", tz, 1); 1554 safe_free(tz); 1555 } else { 1556 unsetenv("TZ"); 1557 } 1558 tzset(); 1559 1560 return ret; 1561 #endif 1562 } 1563 1564 /** Convert an ISO 8601 timestamp ('server-time') to UNIX time */ 1565 time_t server_time_to_unix_time(const char *tbuf) 1566 { 1567 struct tm tm; 1568 int dontcare = 0; 1569 time_t ret; 1570 1571 if (!tbuf) 1572 return 0; 1573 1574 if (strlen(tbuf) < 20) 1575 return 0; 1576 1577 memset(&tm, 0, sizeof(tm)); 1578 ret = sscanf(tbuf, "%d-%d-%dT%d:%d:%d.%dZ", 1579 &tm.tm_year, 1580 &tm.tm_mon, 1581 &tm.tm_mday, 1582 &tm.tm_hour, 1583 &tm.tm_min, 1584 &tm.tm_sec, 1585 &dontcare); 1586 1587 if (ret != 7) 1588 return 0; 1589 1590 tm.tm_year -= 1900; 1591 tm.tm_mon -= 1; 1592 1593 ret = my_timegm(&tm); 1594 return ret; 1595 } 1596 1597 /** Convert an RFC 2616 timestamp (used in HTTP headers) to UNIX time */ 1598 time_t rfc2616_time_to_unix_time(const char *tbuf) 1599 { 1600 struct tm tm; 1601 int dontcare = 0; 1602 time_t ret; 1603 char month[8]; 1604 int i; 1605 1606 if (!tbuf) 1607 return 0; 1608 1609 if (strlen(tbuf) < 20) 1610 return 0; 1611 1612 memset(&tm, 0, sizeof(tm)); 1613 *month = '\0'; 1614 ret = sscanf(tbuf, "%*[a-zA-Z,] %d %3s %d %d:%d:%d", 1615 &tm.tm_mday, month, &tm.tm_year, 1616 &tm.tm_hour, &tm.tm_min, &tm.tm_sec); 1617 1618 if (ret < 6) 1619 return 0; 1620 1621 for (i=0; i < 12; i++) 1622 { 1623 if (!strcmp(short_months[i], month)) 1624 { 1625 tm.tm_mon = i; 1626 break; 1627 } 1628 } 1629 if (i == 12) 1630 return 0; /* Month not found */ 1631 if (tm.tm_year < 1900) 1632 return 0; 1633 1634 tm.tm_year -= 1900; 1635 ret = my_timegm(&tm); 1636 return ret; /* can still be 0 */ 1637 } 1638 1639 /** Returns an RFC 2616 timestamp (used in HTTP headers) */ 1640 const char *rfc2616_time(time_t clock) 1641 { 1642 static char buf[80], plus; 1643 struct tm *lt, *gm; 1644 struct tm gmbuf; 1645 int minswest; 1646 1647 if (!clock) 1648 time(&clock); 1649 gm = gmtime(&clock); 1650 1651 snprintf(buf, sizeof(buf), 1652 "%s, %02d %.3s %4d %02d:%02d:%02d GMT", 1653 short_weekdays[gm->tm_wday], gm->tm_mday, short_months[gm->tm_mon], 1654 gm->tm_year + 1900, gm->tm_hour, gm->tm_min, gm->tm_sec); 1655 1656 return buf; 1657 } 1658 1659 /** Write a 64 bit integer to a file. 1660 * @param fd File descriptor 1661 * @param t The value to write 1662 * @returns 1 on success, 0 on failure. 1663 */ 1664 int write_int64(FILE *fd, uint64_t t) 1665 { 1666 if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) 1667 return 0; 1668 return 1; 1669 } 1670 1671 /** Write a 32 bit integer to a file. 1672 * @param fd File descriptor 1673 * @param t The value to write 1674 * @returns 1 on success, 0 on failure. 1675 */ 1676 int write_int32(FILE *fd, uint32_t t) 1677 { 1678 if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) 1679 return 0; 1680 return 1; 1681 } 1682 1683 /** Read a 64 bit integer from a file. 1684 * @param fd File descriptor 1685 * @param t The value to write 1686 * @returns 1 on success, 0 on failure. 1687 */ 1688 int read_int64(FILE *fd, uint64_t *t) 1689 { 1690 if (fread(t, 1, sizeof(uint64_t), fd) < sizeof(uint64_t)) 1691 return 0; 1692 return 1; 1693 } 1694 1695 /** Read a 32 bit integer from a file. 1696 * @param fd File descriptor 1697 * @param t The value to write 1698 * @returns 1 on success, 0 on failure. 1699 */ 1700 int read_int32(FILE *fd, uint32_t *t) 1701 { 1702 if (fread(t, 1, sizeof(uint32_t), fd) < sizeof(uint32_t)) 1703 return 0; 1704 return 1; 1705 } 1706 1707 /** Read binary data from a file. 1708 * @param fd File descriptor 1709 * @param buf Pointer to buffer 1710 * @param len Size of buffer 1711 * @note This function is not used much, in most cases 1712 * you should use read_str(), read_int32() or 1713 * read_int64() instead. 1714 * @returns 1 on success, 0 on failure. 1715 */ 1716 int read_data(FILE *fd, void *buf, size_t len) 1717 { 1718 if (fread(buf, 1, len, fd) < len) 1719 return 0; 1720 return 1; 1721 } 1722 1723 /** Write binary data to a file. 1724 * @param fd File descriptor 1725 * @param buf Pointer to buffer 1726 * @param len Size of buffer 1727 * @note This function is not used much, in most cases 1728 * you should use write_str(), write_int32() or 1729 * write_int64() instead. 1730 * @returns 1 on success, 0 on failure. 1731 */ 1732 int write_data(FILE *fd, const void *buf, size_t len) 1733 { 1734 if (fwrite(buf, 1, len, fd) < len) 1735 return 0; 1736 return 1; 1737 } 1738 1739 /** Write a string to a file. 1740 * @param fd File descriptor 1741 * @param x Pointer to string 1742 * @note This function can write a string up to 65534 1743 * characters, which should be plenty for usage 1744 * in UnrealIRCd. 1745 * Note that 'x' can safely be NULL. 1746 * @returns 1 on success, 0 on failure. 1747 */ 1748 int write_str(FILE *fd, const char *x) 1749 { 1750 uint16_t len; 1751 1752 len = x ? strlen(x) : 0xffff; 1753 if (!write_data(fd, &len, sizeof(len))) 1754 return 0; 1755 if ((len > 0) && (len < 0xffff)) 1756 { 1757 if (!write_data(fd, x, len)) 1758 return 0; 1759 } 1760 return 1; 1761 } 1762 1763 /** Read a string from a file. 1764 * @param fd File descriptor 1765 * @param x Pointer to string pointer 1766 * @note This function will allocate memory for the data 1767 * and set the string pointer to this value. 1768 * If a NULL pointer was written via write_str() 1769 * then read_str() may also return a NULL pointer. 1770 * @returns 1 on success, 0 on failure. 1771 */ 1772 int read_str(FILE *fd, char **x) 1773 { 1774 uint16_t len; 1775 size_t size; 1776 1777 *x = NULL; 1778 1779 if (!read_data(fd, &len, sizeof(len))) 1780 return 0; 1781 1782 if (len == 0xffff) 1783 { 1784 /* Magic value meaning NULL */ 1785 *x = NULL; 1786 return 1; 1787 } 1788 1789 if (len == 0) 1790 { 1791 /* 0 means empty string */ 1792 safe_strdup(*x, ""); 1793 return 1; 1794 } 1795 1796 if (len > 10000) 1797 return 0; 1798 1799 size = len; 1800 *x = safe_alloc(size + 1); 1801 if (!read_data(fd, *x, size)) 1802 { 1803 safe_free(*x); 1804 return 0; 1805 } 1806 (*x)[len] = 0; 1807 return 1; 1808 } 1809 1810 /** Convert binary 'data' of size 'len' to a hexadecimal string 'str'. 1811 * The caller is responsible to ensure that 'str' is sufficiently large. 1812 */ 1813 void binarytohex(void *data, size_t len, char *str) 1814 { 1815 const char hexchars[16] = "0123456789abcdef"; 1816 char *datastr = (char *)data; 1817 int i, n = 0; 1818 1819 for (i=0; i<len; i++) 1820 { 1821 str[n++] = hexchars[(datastr[i] >> 4) & 0xF]; 1822 str[n++] = hexchars[datastr[i] & 0xF]; 1823 } 1824 str[n] = '\0'; 1825 } 1826 1827 /** Generates an MD5 checksum - binary version. 1828 * @param mdout[out] Buffer to store result in, the result will be 16 bytes in binary 1829 * (not ascii printable!). 1830 * @param src[in] The input data used to generate the checksum. 1831 * @param n[in] Length of data. 1832 * @deprecated The MD5 algorithm is deprecated and insecure, 1833 * so only use this if absolutely needed. 1834 */ 1835 void DoMD5(char *mdout, const char *src, unsigned long n) 1836 { 1837 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1838 unsigned int md_len; 1839 EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); 1840 if (EVP_DigestInit_ex(mdctx, md5_function, NULL) != 1) 1841 abort(); 1842 EVP_DigestUpdate(mdctx, src, n); 1843 EVP_DigestFinal_ex(mdctx, mdout, &md_len); 1844 EVP_MD_CTX_free(mdctx); 1845 #else 1846 MD5_CTX hash; 1847 1848 MD5_Init(&hash); 1849 MD5_Update(&hash, src, n); 1850 MD5_Final(mdout, &hash); 1851 #endif 1852 } 1853 1854 /** Generates an MD5 checksum - ASCII printable string (0011223344..etc..). 1855 * @param dst[out] Buffer to store result in, this will be the result will be 1856 * 32 characters + nul terminator, so needs to be at least 33 characters. 1857 * @param src[in] The input data used to generate the checksum. 1858 * @param n[in] Length of data. 1859 * @deprecated The MD5 algorithm is deprecated and insecure, 1860 * so only use this if absolutely needed. 1861 */ 1862 char *md5hash(char *dst, const char *src, unsigned long n) 1863 { 1864 char tmp[16]; 1865 1866 DoMD5(tmp, src, n); 1867 binarytohex(tmp, sizeof(tmp), dst); 1868 return dst; 1869 } 1870 1871 /** Generates a SHA256 checksum - binary version. 1872 * Most people will want to use sha256hash() instead which outputs hex. 1873 * @param dst[out] Buffer to store result in, which needs to be 32 bytes in length 1874 * (SHA256_DIGEST_LENGTH). 1875 * @param src[in] The input data used to generate the checksum. 1876 * @param n[in] Length of data. 1877 */ 1878 void sha256hash_binary(char *dst, const char *src, unsigned long n) 1879 { 1880 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1881 unsigned int md_len; 1882 EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); 1883 if (EVP_DigestInit_ex(mdctx, sha256_function, NULL) != 1) 1884 abort(); 1885 EVP_DigestUpdate(mdctx, src, n); 1886 EVP_DigestFinal_ex(mdctx, dst, &md_len); 1887 EVP_MD_CTX_free(mdctx); 1888 #else 1889 SHA256_CTX hash; 1890 1891 SHA256_Init(&hash); 1892 SHA256_Update(&hash, src, n); 1893 SHA256_Final(dst, &hash); 1894 #endif 1895 } 1896 1897 /** Generates a SHA256 checksum - ASCII printable string (0011223344..etc..). 1898 * @param dst[out] Buffer to store result in, which needs to be 65 bytes minimum. 1899 * @param src[in] The input data used to generate the checksum. 1900 * @param n[in] Length of data. 1901 */ 1902 char *sha256hash(char *dst, const char *src, unsigned long n) 1903 { 1904 char binaryhash[SHA256_DIGEST_LENGTH]; 1905 1906 sha256hash_binary(binaryhash, src, n); 1907 binarytohex(binaryhash, sizeof(binaryhash), dst); 1908 return dst; 1909 } 1910 1911 /** Calculate the SHA256 checksum of a file */ 1912 const char *sha256sum_file(const char *fname) 1913 { 1914 FILE *fd; 1915 char buf[2048]; 1916 SHA256_CTX hash; 1917 char binaryhash[SHA256_DIGEST_LENGTH]; 1918 static char hexhash[SHA256_DIGEST_LENGTH*2+1]; 1919 int n; 1920 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1921 unsigned int md_len; 1922 EVP_MD_CTX *mdctx; 1923 1924 mdctx = EVP_MD_CTX_new(); 1925 if (EVP_DigestInit_ex(mdctx, sha256_function, NULL) != 1) 1926 abort(); 1927 #else 1928 SHA256_Init(&hash); 1929 #endif 1930 1931 fd = fopen(fname, "rb"); 1932 if (!fd) 1933 return NULL; 1934 1935 while ((n = fread(buf, 1, sizeof(buf), fd)) > 0) 1936 { 1937 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1938 EVP_DigestUpdate(mdctx, buf, n); 1939 #else 1940 SHA256_Update(&hash, buf, n); 1941 #endif 1942 } 1943 fclose(fd); 1944 1945 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1946 EVP_DigestFinal_ex(mdctx, binaryhash, &md_len); 1947 EVP_MD_CTX_free(mdctx); 1948 #else 1949 SHA256_Final(binaryhash, &hash); 1950 #endif 1951 binarytohex(binaryhash, sizeof(binaryhash), hexhash); 1952 return hexhash; 1953 } 1954 1955 /** Generates a SHA1 checksum - binary version. 1956 * @param dst[out] Buffer to store result in, which needs to be 32 bytes in length 1957 * (SHA1_DIGEST_LENGTH). 1958 * @param src[in] The input data used to generate the checksum. 1959 * @param n[in] Length of data. 1960 * @deprecated The SHA1 algorithm is deprecated and insecure, 1961 * so only use this if absolutely needed. 1962 */ 1963 void sha1hash_binary(char *dst, const char *src, unsigned long n) 1964 { 1965 #if OPENSSL_VERSION_NUMBER >= 0x30000000L 1966 unsigned int md_len; 1967 EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); 1968 if (EVP_DigestInit_ex(mdctx, sha1_function, NULL) != 1) 1969 abort(); 1970 EVP_DigestUpdate(mdctx, src, n); 1971 EVP_DigestFinal_ex(mdctx, dst, &md_len); 1972 EVP_MD_CTX_free(mdctx); 1973 #else 1974 SHA_CTX hash; 1975 1976 SHA1_Init(&hash); 1977 SHA1_Update(&hash, src, n); 1978 SHA1_Final(dst, &hash); 1979 #endif 1980 } 1981 1982 /** Remove a suffix from a filename, eg ".c" (if it is present) */ 1983 char *filename_strip_suffix(const char *fname, const char *suffix) 1984 { 1985 static char buf[512]; 1986 1987 strlcpy(buf, fname, sizeof(buf)); 1988 1989 if (suffix) 1990 { 1991 int buf_len = strlen(buf); 1992 int suffix_len = strlen(suffix); 1993 if (buf_len >= suffix_len) 1994 { 1995 if (!strncmp(buf+buf_len-suffix_len, suffix, suffix_len)) 1996 buf[buf_len-suffix_len] = '\0'; 1997 } 1998 } else { 1999 char *p = strrchr(buf, '.'); 2000 if (p) 2001 *p = '\0'; 2002 } 2003 return buf; 2004 } 2005 2006 /** Add a suffix to a filename, eg ".c" */ 2007 char *filename_add_suffix(const char *fname, const char *suffix) 2008 { 2009 static char buf[512]; 2010 snprintf(buf, sizeof(buf), "%s%s", fname, suffix); 2011 return buf; 2012 } 2013 2014 /* Returns 1 if the filename has the suffix, eg ".c" */ 2015 int filename_has_suffix(const char *fname, const char *suffix) 2016 { 2017 char buf[256]; 2018 char *p; 2019 strlcpy(buf, fname, sizeof(buf)); 2020 p = strrchr(buf, '.'); 2021 if (!p) 2022 return 0; 2023 if (!strcmp(p, suffix)) 2024 return 1; 2025 return 0; 2026 } 2027 2028 /** Check if the specified file or directory exists */ 2029 int file_exists(const char *file) 2030 { 2031 #ifdef _WIN32 2032 if (_access(file, 0) == 0) 2033 #else 2034 if (access(file, 0) == 0) 2035 #endif 2036 return 1; 2037 return 0; 2038 } 2039 2040 /** Get the file creation time */ 2041 time_t get_file_time(const char *fname) 2042 { 2043 struct stat st; 2044 2045 if (stat(fname, &st) != 0) 2046 return 0; 2047 2048 return (time_t)st.st_ctime; 2049 } 2050 2051 /** Get the size of a file */ 2052 long get_file_size(const char *fname) 2053 { 2054 struct stat st; 2055 2056 if (stat(fname, &st) != 0) 2057 return -1; 2058 2059 return (long)st.st_size; 2060 } 2061 2062 /** Add a line to a MultiLine list */ 2063 void addmultiline(MultiLine **l, const char *line) 2064 { 2065 MultiLine *m = safe_alloc(sizeof(MultiLine)); 2066 safe_strdup(m->line, line); 2067 append_ListItem((ListStruct *)m, (ListStruct **)l); 2068 } 2069 2070 /** Free an entire MultiLine list */ 2071 void freemultiline(MultiLine *l) 2072 { 2073 MultiLine *l_next; 2074 for (; l; l = l_next) 2075 { 2076 l_next = l->next; 2077 safe_free(l->line); 2078 safe_free(l); 2079 } 2080 } 2081 2082 /** Convert a line regular string containing \n's to a MultiLine linked list */ 2083 MultiLine *line2multiline(const char *str) 2084 { 2085 static char buf[8192]; 2086 char *p, *p2; 2087 MultiLine *ml = NULL; 2088 2089 strlcpy(buf, str, sizeof(buf)); 2090 p = buf; 2091 do { 2092 p2 = strchr(p, '\n'); 2093 if (p2) 2094 *p2++ = '\0'; 2095 addmultiline(&ml, p); 2096 p = p2; 2097 } while(p2 && *p2); 2098 return ml; 2099 } 2100 2101 /** Convert a sendtype to a command string */ 2102 const char *sendtype_to_cmd(SendType sendtype) 2103 { 2104 if (sendtype == SEND_TYPE_PRIVMSG) 2105 return "PRIVMSG"; 2106 if (sendtype == SEND_TYPE_NOTICE) 2107 return "NOTICE"; 2108 if (sendtype == SEND_TYPE_TAGMSG) 2109 return "TAGMSG"; 2110 return NULL; 2111 } 2112 2113 /** Check password strength. 2114 * @param pass The password to check 2115 * @param min_length The minimum length of the password 2116 * @param strict Whether to require UPPER+lower+digits 2117 * @returns 1 if good, 0 if not. 2118 */ 2119 int check_password_strength(const char *pass, int min_length, int strict, char **err) 2120 { 2121 static char buf[256]; 2122 char has_lowercase=0, has_uppercase=0, has_digit=0; 2123 const char *p; 2124 2125 if (err) 2126 *err = NULL; 2127 2128 if (strlen(pass) < min_length) 2129 { 2130 if (err) 2131 { 2132 snprintf(buf, sizeof(buf), "Password must be at least %d characters", min_length); 2133 *err = buf; 2134 } 2135 return 0; 2136 } 2137 2138 for (p=pass; *p; p++) 2139 { 2140 if (islower(*p)) 2141 has_lowercase = 1; 2142 else if (isupper(*p)) 2143 has_uppercase = 1; 2144 else if (isdigit(*p)) 2145 has_digit = 1; 2146 } 2147 2148 if (strict) 2149 { 2150 if (!has_lowercase) 2151 { 2152 if (err) 2153 *err = "Password must contain at least 1 lowercase character"; 2154 return 0; 2155 } else 2156 if (!has_uppercase) 2157 { 2158 if (err) 2159 *err = "Password must contain at least 1 UPPERcase character"; 2160 return 0; 2161 } else 2162 if (!has_digit) 2163 { 2164 if (err) 2165 *err = "Password must contain at least 1 digit (number)"; 2166 return 0; 2167 } 2168 } 2169 2170 return 1; 2171 } 2172 2173 int valid_secret_password(const char *pass, char **err) 2174 { 2175 return check_password_strength(pass, 10, 1, err); 2176 } 2177 2178 int running_interactively(void) 2179 { 2180 #ifndef _WIN32 2181 char *s; 2182 2183 if (!isatty(0)) 2184 return 0; 2185 2186 s = getenv("TERM"); 2187 if (!s || !strcasecmp(s, "dumb") || !strcasecmp(s, "none")) 2188 return 0; 2189 2190 return 1; 2191 #else 2192 return IsService ? 0 : 1; 2193 #endif 2194 } 2195 2196 int terminal_supports_color(void) 2197 { 2198 #ifndef _WIN32 2199 char *s; 2200 2201 /* Support NO_COLOR as per https://no-color.org */ 2202 s = getenv("NO_COLOR"); 2203 if (s != NULL && s[0] != '\0') 2204 return 0; 2205 2206 /* Yeah we check all of stdin, stdout, stderr, because one 2207 * or more may be redirected (bin/unrealircd >log 2>&1), 2208 * and then we want to say no to color support. 2209 */ 2210 if (!isatty(0) || !isatty(1) || !isatty(2)) 2211 return 0; 2212 2213 s = getenv("TERM"); 2214 /* Yeah this is a lazy way to detect color-capable terminals 2215 * but it is good enough for me. 2216 */ 2217 if (s) 2218 { 2219 if (strstr(s, "color") || strstr(s, "ansi")) 2220 return 1; 2221 } 2222 2223 return 0; 2224 #else 2225 return 0; 2226 #endif 2227 } 2228 2229 /** Skip whitespace (if any) */ 2230 void skip_whitespace(char **p) 2231 { 2232 for (; **p == ' ' || **p == '\t'; *p = *p + 1); 2233 } 2234 2235 /** Keep reading '*p' until we hit any of the 'stopchars'. 2236 * Actually behaves like strstr() but then hit the end 2237 * of the string (\0) i guess? 2238 */ 2239 void read_until(char **p, char *stopchars) 2240 { 2241 for (; **p && !strchr(stopchars, **p); *p = *p + 1); 2242 } 2243 2244 void write_pidfile_failed(void) 2245 { 2246 char *errstr = strerror(errno); 2247 unreal_log(ULOG_WARNING, "config", "WRITE_PID_FILE_FAILED", NULL, 2248 "Unable to write to pid file '$filename': $system_error", 2249 log_data_string("filename", conf_files->pid_file), 2250 log_data_string("system_error", errstr)); 2251 } 2252 2253 /** Write PID file */ 2254 void write_pidfile(void) 2255 { 2256 #ifdef IRCD_PIDFILE 2257 int fd; 2258 char buff[20]; 2259 if ((fd = open(conf_files->pid_file, O_CREAT | O_WRONLY, 0600)) < 0) 2260 { 2261 write_pidfile_failed(); 2262 return; 2263 } 2264 ircsnprintf(buff, sizeof(buff), "%5d\n", (int)getpid()); 2265 if (write(fd, buff, strlen(buff)) < 0) 2266 write_pidfile_failed(); 2267 if (close(fd) < 0) 2268 write_pidfile_failed(); 2269 #endif 2270 } 2271 2272 /* 2273 * Determines if the given string is a valid URL. Since libcurl 2274 * supports telnet, ldap, and dict such strings are treated as 2275 * invalid URLs here since we don't want them supported in 2276 * unreal. 2277 */ 2278 int url_is_valid(const char *string) 2279 { 2280 if (strstr(string, " ") || strstr(string, "\t")) 2281 return 0; 2282 2283 if (strstr(string, "telnet://") == string || 2284 strstr(string, "ldap://") == string || 2285 strstr(string, "dict://") == string) 2286 { 2287 return 0; 2288 } 2289 return (strstr(string, "://") != NULL); 2290 } 2291 2292 /** A displayable URL for in error messages and such. 2293 * This leaves out any authentication information (user:pass) 2294 * the URL may contain. 2295 */ 2296 const char *displayurl(const char *url) 2297 { 2298 static char buf[512]; 2299 char *proto, *rest; 2300 2301 /* protocol://user:pass@host/etc.. */ 2302 rest = strchr(url, '@'); 2303 2304 if (!rest) 2305 return url; /* contains no auth information */ 2306 2307 rest++; /* now points to the rest (remainder) of the URL */ 2308 2309 proto = strstr(url, "://"); 2310 if (!proto || (proto > rest) || (proto == url)) 2311 return url; /* incorrectly formatted, just show entire URL. */ 2312 2313 strlncpy(buf, url, sizeof(buf), proto - url); 2314 strlcat(buf, "://***:***@", sizeof(buf)); 2315 strlcat(buf, rest, sizeof(buf)); 2316 2317 return buf; 2318 } 2319 2320 /* 2321 * Returns the filename portion of the URL. The returned string 2322 * is malloc()'ed and must be freed by the caller. If the specified 2323 * URL does not contain a filename, a '-' is allocated and returned. 2324 */ 2325 char *url_getfilename(const char *url) 2326 { 2327 const char *c, *start; 2328 2329 if ((c = strstr(url, "://"))) 2330 c += 3; 2331 else 2332 c = url; 2333 2334 while (*c && *c != '/') 2335 c++; 2336 2337 if (*c == '/') 2338 { 2339 c++; 2340 if (!*c || *c == '?') 2341 return raw_strdup("-"); 2342 start = c; 2343 while (*c && *c != '?') 2344 c++; 2345 if (!*c) 2346 return raw_strdup(start); 2347 else 2348 return raw_strldup(start, c-start+1); 2349 2350 } 2351 return raw_strdup("-"); 2352 } 2353 2354 #ifdef _WIN32 2355 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess 2356 // mode value Checks file for 2357 // 04 Read-only 2358 #define R_OK 04 2359 #endif 2360 2361 /* 2362 * Checks whether a file can be opened for reading. 2363 */ 2364 int is_file_readable(const char *file, const char *dir) 2365 { 2366 char *filename = strdup(file); 2367 convert_to_absolute_path(&filename, dir); 2368 if (access(filename, R_OK)){ 2369 safe_free(filename); 2370 return 0; 2371 } 2372 safe_free(filename); 2373 return 1; 2374 } 2375 2376 void delletterfromstring(char *s, char letter) 2377 { 2378 if (s == NULL) 2379 return; 2380 for (; *s; s++) 2381 { 2382 if (*s == letter) 2383 { 2384 for (; *s; s++) 2385 *s = s[1]; 2386 break; 2387 } 2388 } 2389 } 2390 2391 int sort_character_lowercase_before_uppercase(char x, char y) 2392 { 2393 /* Lower before upper */ 2394 if (islower(x) && isupper(y)) 2395 return 1; 2396 if (isupper(x) && islower(y)) 2397 return 0; 2398 /* Other than that, easy */ 2399 return x < y ? 1 : 0; 2400 } 2401 2402 /* Helper function, mainly used by snomask code */ 2403 void addlettertodynamicstringsorted(char **str, char letter) 2404 { 2405 char *i, *o; 2406 char *newbuf; 2407 size_t newbuflen; 2408 2409 /* NULL string? Easy! */ 2410 if (*str == NULL) 2411 { 2412 *str = safe_alloc(2); 2413 **str = letter; 2414 return; 2415 } 2416 2417 /* Exists? Then nothing to do */ 2418 if (strchr(*str, letter)) 2419 return; 2420 2421 /* Ok, we really need to add it */ 2422 newbuflen = strlen(*str) + 2; 2423 newbuf = safe_alloc(newbuflen); 2424 for (i = *str, o = newbuf; *i; i++) 2425 { 2426 /* Insert before a higher letter */ 2427 if (letter && sort_character_lowercase_before_uppercase(letter, *i)) 2428 { 2429 *o++ = letter; 2430 letter = '\0'; 2431 } 2432 *o++ = *i; 2433 } 2434 /* Or maybe we should be at the final spot? */ 2435 if (letter) 2436 *o++ = letter; 2437 *o = '\0'; 2438 safe_free_raw(*str); 2439 *str = newbuf; 2440 } 2441 2442 void s_die() 2443 { 2444 #ifdef _WIN32 2445 Client *client; 2446 if (!IsService) 2447 { 2448 loop.terminating = 1; 2449 unload_all_modules(); 2450 2451 list_for_each_entry(client, &lclient_list, lclient_node) 2452 (void) send_queued(client); 2453 2454 exit(-1); 2455 } 2456 else { 2457 SERVICE_STATUS status; 2458 SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 2459 SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP); 2460 ControlService(hService, SERVICE_CONTROL_STOP, &status); 2461 } 2462 #else 2463 loop.terminating = 1; 2464 unload_all_modules(); 2465 unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); 2466 exit(0); 2467 #endif 2468 } 2469 2470 #ifndef _WIN32 2471 void s_rehash() 2472 { 2473 struct sigaction act; 2474 dorehash = 1; 2475 act.sa_handler = s_rehash; 2476 act.sa_flags = 0; 2477 (void)sigemptyset(&act.sa_mask); 2478 (void)sigaddset(&act.sa_mask, SIGHUP); 2479 (void)sigaction(SIGHUP, &act, NULL); 2480 } 2481 2482 void s_reloadcert() 2483 { 2484 struct sigaction act; 2485 doreloadcert = 1; 2486 act.sa_handler = s_reloadcert; 2487 act.sa_flags = 0; 2488 (void)sigemptyset(&act.sa_mask); 2489 (void)sigaddset(&act.sa_mask, SIGUSR1); 2490 (void)sigaction(SIGUSR1, &act, NULL); 2491 } 2492 #endif // #ifndef _WIN32 2493 2494 void restart(const char *mesg) 2495 { 2496 server_reboot(mesg); 2497 } 2498 2499 void s_restart() 2500 { 2501 dorestart = 1; 2502 } 2503 2504 #ifndef _WIN32 2505 /** Signal handler for signals which we ignore, 2506 * like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc. 2507 */ 2508 void ignore_this_signal() 2509 { 2510 struct sigaction act; 2511 2512 act.sa_handler = ignore_this_signal; 2513 act.sa_flags = 0; 2514 (void)sigemptyset(&act.sa_mask); 2515 (void)sigaddset(&act.sa_mask, SIGALRM); 2516 (void)sigaddset(&act.sa_mask, SIGPIPE); 2517 (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); 2518 (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); 2519 #ifdef SIGWINCH 2520 (void)sigaddset(&act.sa_mask, SIGWINCH); 2521 (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); 2522 #endif 2523 } 2524 #endif /* #ifndef _WIN32 */ 2525 2526 2527 void server_reboot(const char *mesg) 2528 { 2529 int i; 2530 Client *client; 2531 unreal_log(ULOG_INFO, "main", "UNREALIRCD_RESTARTING", NULL, 2532 "Restarting server: $reason", 2533 log_data_string("reason", mesg)); 2534 2535 list_for_each_entry(client, &lclient_list, lclient_node) 2536 (void) send_queued(client); 2537 2538 /* 2539 * ** fd 0 must be 'preserved' if either the -d or -i options have 2540 * ** been passed to us before restarting. 2541 */ 2542 #ifdef HAVE_SYSLOG 2543 (void)closelog(); 2544 #endif 2545 #ifndef _WIN32 2546 for (i = 3; i < MAXCONNECTIONS; i++) 2547 (void)close(i); 2548 if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) 2549 (void)close(2); 2550 (void)close(1); 2551 (void)close(0); 2552 close_std_descriptors(); 2553 (void)execv(MYNAME, myargv); 2554 #else 2555 close_connections(); 2556 if (!IsService) 2557 { 2558 CleanUp(); 2559 WinExec(cmdLine, SW_SHOWDEFAULT); 2560 } 2561 #endif 2562 loop.terminating = 1; 2563 unload_all_modules(); 2564 #ifdef _WIN32 2565 if (IsService) 2566 { 2567 SERVICE_STATUS status; 2568 PROCESS_INFORMATION pi; 2569 STARTUPINFO si; 2570 char fname[MAX_PATH]; 2571 memset(&status, 0, sizeof(status)); 2572 memset(&si, 0, sizeof(si)); 2573 IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING; 2574 SetServiceStatus(IRCDStatusHandle, &IRCDStatus); 2575 GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH); 2576 CreateProcess(fname, "restartsvc", NULL, NULL, FALSE, 2577 0, NULL, NULL, &si, &pi); 2578 IRCDStatus.dwCurrentState = SERVICE_STOPPED; 2579 SetServiceStatus(IRCDStatusHandle, &IRCDStatus); 2580 ExitProcess(0); 2581 } 2582 else 2583 #endif 2584 exit(-1); 2585 } 2586 2587 /** Check if at least 'minimum' seconds passed by since last run. 2588 * @param tv_old Pointer to a timeval struct to keep track of things. 2589 * @param minimum The time specified in milliseconds (eg: 1000 for 1 second) 2590 * @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned. 2591 */ 2592 int minimum_msec_since_last_run(struct timeval *tv_old, long minimum) 2593 { 2594 long v; 2595 2596 if (tv_old->tv_sec == 0) 2597 { 2598 /* First call ever */ 2599 tv_old->tv_sec = timeofday_tv.tv_sec; 2600 tv_old->tv_usec = timeofday_tv.tv_usec; 2601 return 0; 2602 } 2603 v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000); 2604 if (v >= minimum) 2605 { 2606 tv_old->tv_sec = timeofday_tv.tv_sec; 2607 tv_old->tv_usec = timeofday_tv.tv_usec; 2608 return 1; 2609 } 2610 return 0; 2611 } 2612 2613 /** Strip color, bold, underline, and reverse codes from a string. 2614 * @param text The input text 2615 * @param output The buffer for the output text 2616 * @param outputlen The length of the output buffer 2617 * @param strip_flags Zero or (a combination of) UNRL_STRIP_LOW_ASCII / UNRL_STRIP_KEEP_LF. 2618 * @returns The new string, which will be 'output', or in unusual cases (outputlen==0) will be NULL. 2619 */ 2620 const char *StripControlCodesEx(const char *text, char *output, size_t outputlen, int strip_flags) 2621 { 2622 int i = 0, len = strlen(text), save_len=0; 2623 char nc = 0, col = 0, rgb = 0; 2624 char *o = output; 2625 const char *save_text=NULL; 2626 2627 /* Handle special cases first.. */ 2628 2629 if (outputlen == 0) 2630 return NULL; 2631 2632 if (outputlen == 1) 2633 { 2634 *output = '\0'; 2635 return output; 2636 } 2637 2638 /* Reserve room for the NUL byte */ 2639 outputlen--; 2640 2641 while (len > 0) 2642 { 2643 if ((col && isdigit(*text) && nc < 2) || 2644 ((col == 1) && (*text == ',') && isdigit(text[1]) && (nc > 0) && (nc < 3))) 2645 { 2646 nc++; 2647 if (*text == ',') 2648 { 2649 nc = 0; 2650 col++; 2651 } 2652 } 2653 /* Syntax for RGB is ^DHHHHHH where H is a hex digit. 2654 * If < 6 hex digits are specified, the code is displayed 2655 * as text 2656 */ 2657 else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7)) 2658 { 2659 nc++; 2660 if (*text == ',') 2661 nc = 0; 2662 } 2663 else 2664 { 2665 if (col) 2666 col = 0; 2667 if (rgb) 2668 { 2669 if (nc != 6) 2670 { 2671 text = save_text+1; 2672 len = save_len-1; 2673 rgb = 0; 2674 continue; 2675 } 2676 rgb = 0; 2677 } 2678 switch (*text) 2679 { 2680 case 3: 2681 /* color */ 2682 col = 1; 2683 nc = 0; 2684 break; 2685 case 4: 2686 /* RGB */ 2687 save_text = text; 2688 save_len = len; 2689 rgb = 1; 2690 nc = 0; 2691 break; 2692 case 2: 2693 /* bold */ 2694 break; 2695 case 31: 2696 /* underline */ 2697 break; 2698 case 22: 2699 /* reverse */ 2700 break; 2701 case 15: 2702 /* plain */ 2703 break; 2704 case 29: 2705 /* italic */ 2706 break; 2707 case 30: 2708 /* strikethrough */ 2709 break; 2710 case 17: 2711 /* monospace */ 2712 break; 2713 case 0xe2: 2714 if (!strncmp(text+1, "\x80\x8b", 2)) 2715 { 2716 /* +2 means we skip 3 */ 2717 text += 2; 2718 len -= 2; 2719 break; 2720 } 2721 /*fallthrough*/ 2722 default: 2723 if ((*text >= ' ') || 2724 !(strip_flags & UNRL_STRIP_LOW_ASCII) || 2725 ((strip_flags & UNRL_STRIP_KEEP_LF) && (*text == '\n')) 2726 ) 2727 { 2728 *o++ = *text; 2729 outputlen--; 2730 if (outputlen == 0) 2731 { 2732 *o = '\0'; 2733 return output; 2734 } 2735 } 2736 break; 2737 } 2738 } 2739 text++; 2740 len--; 2741 } 2742 2743 *o = '\0'; 2744 return output; 2745 } 2746 2747 /* strip color, bold, underline, and reverse codes from a string */ 2748 const char *StripControlCodes(const char *text) 2749 { 2750 static unsigned char new_str[4096]; 2751 2752 return StripControlCodesEx(text, new_str, sizeof(new_str), 0); 2753 } 2754 2755 const char *command_issued_by_rpc(MessageTag *mtags) 2756 { 2757 MessageTag *m = find_mtag(mtags, "unrealircd.org/issued-by"); 2758 if (m && m->value && !strncmp(m->value, "RPC:", 4)) 2759 return m->value; 2760 return NULL; 2761 }