unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
channel.c (36177B)
1 /* Unreal Internet Relay Chat Daemon, src/channel.c 2 * (C) Copyright 1990 Jarkko Oikarinen and 3 * University of Oulu, Co Center 4 * (C) Copyright 1999-present The UnrealIRCd team 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 1, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 /** @file 22 * @brief Various important (common) channel functions. 23 */ 24 25 #include "unrealircd.h" 26 27 /** Lazy way to signal an OperOverride MODE */ 28 long opermode = 0; 29 /** Lazy way to signal an SAJOIN MODE */ 30 long sajoinmode = 0; 31 /** List of all channels on the server. 32 * @ingroup ListFunctions 33 * @section channels_example Example 34 * This code will list all channels on the network. 35 * @code 36 * sendnotice(client, "List of all channels:"); 37 * for (channel = channels; channel; channel=channel->nextch) 38 * sendnotice(client, "Channel %s", channel->name); 39 * @endcode 40 */ 41 Channel *channels = NULL; 42 43 static mp_pool_t *channel_pool = NULL; 44 45 /** This describes the letters, modes and options for core channel modes. 46 * These are +ntmispklr and also the list modes +vhoaq and +beI. 47 */ 48 CoreChannelModeTable corechannelmodetable[] = { 49 {MODE_BAN, 'b', 1, 1}, 50 {MODE_EXCEPT, 'e', 1, 1}, /* exception ban */ 51 {MODE_INVEX, 'I', 1, 1}, /* invite-only exception */ 52 {0x0, 0x0, 0x0, 0x0} 53 }; 54 55 /** The advertised supported channel modes in the 004 numeric */ 56 char cmodestring[512]; 57 58 /** Returns 1 if the IRCOp can override or is a remote connection */ 59 inline int op_can_override(const char *acl, Client *client, Channel *channel, void* extra) 60 { 61 #ifndef NO_OPEROVERRIDE 62 if (MyUser(client) && !(ValidatePermissionsForPath(acl,client,NULL,channel,extra))) 63 return 0; 64 return 1; 65 #else 66 return 0; 67 #endif 68 } 69 70 /** Returns 1 if a half-op can set this channel mode */ 71 int Halfop_mode(long mode) 72 { 73 CoreChannelModeTable *tab = &corechannelmodetable[0]; 74 75 while (tab->mode != 0x0) 76 { 77 if (tab->mode == mode) 78 return (tab->halfop == 1 ? TRUE : FALSE); 79 tab++; 80 } 81 return TRUE; 82 } 83 84 /** Find client in a Member linked list (eg: channel->members) */ 85 Member *find_member_link(Member *lp, Client *ptr) 86 { 87 if (ptr) 88 { 89 while (lp) 90 { 91 if (lp->client == ptr) 92 return (lp); 93 lp = lp->next; 94 } 95 } 96 return NULL; 97 } 98 99 /** Find channel in a Membership linked list (eg: client->user->channel) */ 100 Membership *find_membership_link(Membership *lp, Channel *ptr) 101 { 102 if (ptr) 103 while (lp) 104 { 105 if (lp->channel == ptr) 106 return (lp); 107 lp = lp->next; 108 } 109 return NULL; 110 } 111 112 /** Allocate and return an empty Member struct */ 113 static Member *make_member(void) 114 { 115 Member *lp; 116 unsigned int i; 117 118 if (freemember == NULL) 119 { 120 for (i = 1; i <= (4072/sizeof(Member)); ++i) 121 { 122 lp = safe_alloc(sizeof(Member)); 123 lp->next = freemember; 124 freemember = lp; 125 } 126 } 127 lp = freemember; 128 freemember = freemember->next; 129 lp->next = NULL; 130 return lp; 131 } 132 133 /** Free a Member struct */ 134 static void free_member(Member *lp) 135 { 136 if (!lp) 137 return; 138 moddata_free_member(lp); 139 memset(lp, 0, sizeof(Member)); 140 lp->next = freemember; 141 freemember = lp; 142 } 143 144 /** Allocate and return an empty Membership struct */ 145 static Membership *make_membership(void) 146 { 147 Membership *m = NULL; 148 unsigned int i; 149 150 if (freemembership == NULL) 151 { 152 for (i = 1; i <= (4072/sizeof(Membership)); i++) 153 { 154 m = safe_alloc(sizeof(Membership)); 155 m->next = freemembership; 156 freemembership = m; 157 } 158 m = freemembership; 159 freemembership = m->next; 160 } 161 else 162 { 163 m = freemembership; 164 freemembership = freemembership->next; 165 } 166 memset(m, 0, sizeof(Membership)); 167 return m; 168 } 169 170 /** Free a Membership struct */ 171 static void free_membership(Membership *m) 172 { 173 if (m) 174 { 175 moddata_free_membership(m); 176 memset(m, 0, sizeof(Membership)); 177 m->next = freemembership; 178 freemembership = m; 179 } 180 } 181 182 /** Find a client by nickname, hunt for older nick names if not found. 183 * This can be handy, for example for /KILL nick, if 'nick' keeps 184 * nick-changing and you are slow with typing. 185 * @param client The requestor 186 * @param user The nick name (or server name) 187 * @param chasing This will be set to 1 if the client was found 188 * only after searching through the nick history. 189 * @returns The client (if found) or NULL (if not found). 190 */ 191 Client *find_chasing(Client *client, const char *user, int *chasing) 192 { 193 Client *who = find_client(user, NULL); 194 195 if (chasing) 196 *chasing = 0; 197 if (who) 198 { 199 if (!IsServer(who)) 200 return who; 201 else 202 return NULL; 203 } 204 if (!(who = get_history(user, (long)KILLCHASETIMELIMIT))) 205 { 206 sendnumeric(client, ERR_NOSUCHNICK, user); 207 return NULL; 208 } 209 if (chasing) 210 *chasing = 1; 211 if (!IsServer(who)) 212 return who; 213 else return NULL; 214 } 215 216 /** Return 1 if the bans are identical, taking into account special handling for extbans */ 217 int identical_ban(const char *one, const char *two) 218 { 219 #if 0 220 if (is_extended_ban(one) && is_extended_ban(two)) 221 { 222 /* compare the first 3 characters case-sensitive and if identical then compare 223 * the remainder of the string case-insensitive. 224 */ 225 if (!strncmp(one, two, 3) && !strcasecmp(one+3, two+3)) 226 return 1; 227 } else { 228 if (!mycmp(one, two)) 229 return 1; 230 } 231 #else 232 /* Actually I think we can live with this nowadays. 233 * We are pushing towards named extbans, and all the 234 * letter extbans that could clash no longer exist. 235 */ 236 if (!mycmp(one, two)) 237 return 1; 238 #endif 239 return 0; 240 } 241 242 /** Add a listmode (+beI) with the specified banid to 243 * the specified channel. (Extended version with 244 * set by nick and set on timestamp) 245 */ 246 int add_listmode_ex(Ban **list, Client *client, Channel *channel, const char *banid, const char *setby, time_t seton) 247 { 248 Ban *ban; 249 int cnt = 0, len; 250 int do_not_add = 0; 251 252 //if (MyUser(client)) 253 // collapse(banid); 254 255 len = strlen(banid); 256 if (!*list && ((len > MAXBANLENGTH) || (MAXBANS < 1))) 257 { 258 if (MyUser(client)) 259 { 260 /* Only send the error to local clients */ 261 sendnumeric(client, ERR_BANLISTFULL, channel->name, banid); 262 } 263 do_not_add = 1; 264 } 265 for (ban = *list; ban; ban = ban->next) 266 { 267 len += strlen(ban->banstr); 268 /* Check MAXBANLENGTH / MAXBANS only for local clients 269 * and 'me' (for +b's set during +f). 270 */ 271 if ((MyUser(client) || IsMe(client)) && ((len > MAXBANLENGTH) || (++cnt >= MAXBANS))) 272 { 273 do_not_add = 1; 274 } 275 if (identical_ban(ban->banstr, banid)) 276 break; /* update existing ban (potentially) */ 277 } 278 279 /* Create a new ban if needed */ 280 if (!ban) 281 { 282 if (do_not_add) 283 { 284 /* The banlist is full and trying to add a new ban. 285 * This is not permitted. 286 */ 287 if (MyUser(client)) 288 { 289 /* Only send the error to local clients */ 290 sendnumeric(client, ERR_BANLISTFULL, channel->name, banid); 291 } 292 return -1; 293 } 294 ban = make_ban(); 295 ban->next = *list; 296 *list = ban; 297 } 298 299 if ((ban->when > 0) && (seton >= ban->when)) 300 { 301 /* Trying to add the same ban while an older version 302 * or identical version of the ban already exists. 303 */ 304 return -1; 305 } 306 307 /* Update/set if this ban is new or older than existing one */ 308 safe_strdup(ban->banstr, banid); /* cAsE may differ, use oldest version of it */ 309 safe_strdup(ban->who, setby); 310 ban->when = seton; 311 return 0; 312 } 313 314 /** Add a listmode (+beI) with the specified banid to 315 * the specified channel. (Simplified version) 316 */ 317 int add_listmode(Ban **list, Client *client, Channel *channel, const char *banid) 318 { 319 char *setby = client->name; 320 char nuhbuf[NICKLEN+USERLEN+HOSTLEN+4]; 321 322 if (IsUser(client) && (iConf.ban_setter == SETTER_NICK_USER_HOST)) 323 setby = make_nick_user_host_r(nuhbuf, sizeof(nuhbuf), client->name, client->user->username, GetHost(client)); 324 325 return add_listmode_ex(list, client, channel, banid, setby, TStime()); 326 } 327 328 /** Delete a listmode (+beI) from a channel that matches the specified banid. 329 */ 330 int del_listmode(Ban **list, Channel *channel, const char *banid) 331 { 332 Ban **ban; 333 Ban *tmp; 334 335 if (!banid) 336 return -1; 337 for (ban = list; *ban; ban = &((*ban)->next)) 338 { 339 if (identical_ban(banid, (*ban)->banstr)) 340 { 341 tmp = *ban; 342 *ban = tmp->next; 343 safe_free(tmp->banstr); 344 safe_free(tmp->who); 345 free_ban(tmp); 346 return 0; 347 } 348 } 349 return -1; 350 } 351 352 /** is_banned - Check if a user is banned on a channel. 353 * @param client Client to check (can be remote client) 354 * @param channel Channel to check 355 * @param type Type of ban to check for (BANCHK_*) 356 * @param msg Message, only for some BANCHK_* types, otherwise NULL 357 * @param errmsg Error message returned, could be NULL (which does not 358 * indicate absence of an error). 359 * @returns A pointer to the ban struct if banned, otherwise NULL. 360 * @comments Simple wrapper for is_banned_with_nick() 361 */ 362 inline Ban *is_banned(Client *client, Channel *channel, int type, const char **msg, const char **errmsg) 363 { 364 return is_banned_with_nick(client, channel, type, NULL, msg, errmsg); 365 } 366 367 /** ban_check_mask - Checks if the user matches the specified n!u@h mask -or- run an extended ban. 368 * This is basically extracting the mask and extban check from is_banned_with_nick, 369 * but with being a bit more strict in what an extban is. 370 * Strange things could happen if this is called outside standard ban checking. 371 * @param b Ban context, see BanContext 372 * @returns Nonzero if the mask/extban succeeds. Zero if it doesn't. 373 */ 374 inline int ban_check_mask(BanContext *b) 375 { 376 if (!b->no_extbans && is_extended_ban(b->banstr)) 377 { 378 /* Is an extended ban. */ 379 const char *nextbanstr; 380 Extban *extban = findmod_by_bantype(b->banstr, &nextbanstr); 381 if (!extban || !(extban->is_banned_events & b->ban_check_types)) 382 { 383 return 0; 384 } else { 385 b->banstr = nextbanstr; 386 return extban->is_banned(b); 387 } 388 } 389 else 390 { 391 /* Is a n!u@h mask. */ 392 return match_user(b->banstr, b->client, MATCH_CHECK_ALL); 393 } 394 } 395 396 /** is_banned_with_nick - Check if a user is banned on a channel. 397 * @param client Client to check (can be remote client) 398 * @param channel Channel to check 399 * @param type Type of ban to check for (BANCHK_*) 400 * @param nick Nick of the user (or NULL, to default to client->name) 401 * @param msg Message, only for some BANCHK_* types, otherwise NULL 402 * @returns A pointer to the ban struct if banned, otherwise NULL. 403 */ 404 Ban *is_banned_with_nick(Client *client, Channel *channel, int type, const char *nick, const char **msg, const char **errmsg) 405 { 406 Ban *ban, *ex; 407 char savednick[NICKLEN+1]; 408 BanContext *b = safe_alloc(sizeof(BanContext)); 409 410 /* It's not really doable to pass 'nick' to all the ban layers, 411 * including extbans (with stacking) and so on. Or at least not 412 * without breaking several module API's. 413 * So, instead, we temporarily set 'client->name' to 'nick' and 414 * restore it to the orginal value at the end of this function. 415 * This is possible because all these layers never send a message 416 * to 'client' and only indicate success/failure. 417 * Note that all this ONLY happens if is_banned_with_nick() is called 418 * with a non-NULL nick. That doesn't happen much. In UnrealIRCd 419 * only in case of '/NICK newnick'. This fixes #5165. 420 */ 421 if (nick) 422 { 423 strlcpy(savednick, client->name, sizeof(savednick)); 424 strlcpy(client->name, nick, sizeof(client->name)); 425 } 426 427 b->client = client; 428 b->channel = channel; 429 b->ban_check_types = type; 430 if (msg) 431 b->msg = *msg; 432 433 /* We check +b first, if a +b is found we then see if there is a +e. 434 * If a +e was found we return NULL, if not, we return the ban. 435 */ 436 437 for (ban = channel->banlist; ban; ban = ban->next) 438 { 439 b->banstr = ban->banstr; 440 if (ban_check_mask(b)) 441 break; 442 } 443 444 if (ban) 445 { 446 /* Ban found, now check for +e */ 447 for (ex = channel->exlist; ex; ex = ex->next) 448 { 449 b->banstr = ex->banstr; 450 if (ban_check_mask(b)) 451 { 452 /* except matched */ 453 ban = NULL; 454 break; 455 } 456 } 457 /* user is not on except, 'ban' stays non-NULL. */ 458 } 459 460 if (nick) 461 { 462 /* Restore the nick */ 463 strlcpy(client->name, savednick, sizeof(client->name)); 464 } 465 466 /* OUT: */ 467 if (msg) 468 *msg = b->msg; 469 if (errmsg) 470 *errmsg = b->error_msg; 471 472 safe_free(b); 473 return ban; 474 } 475 476 /** Checks if a ban already exists */ 477 int ban_exists(Ban *lst, const char *str) 478 { 479 for (; lst; lst = lst->next) 480 if (!mycmp(lst->banstr, str)) 481 return 1; 482 return 0; 483 } 484 485 /** Checks if a ban already exists - special version. 486 * This ignores the "~time:xx:" suffixes in the banlist. 487 * So it will return 1 if a ban is there for ~time:5:blah!*@* 488 * and you call ban_exists_ignore_time(channel->banlist, "blah!*@*") 489 */ 490 int ban_exists_ignore_time(Ban *lst, const char *str) 491 { 492 const char *p; 493 494 for (; lst; lst = lst->next) 495 { 496 if (!strncmp(lst->banstr, "~time:", 6)) 497 { 498 /* Special treatment for ~time:xx: */ 499 p = strchr(lst->banstr+6, ':'); 500 if (p) 501 { 502 p++; 503 if (!mycmp(p, str)) 504 return 1; 505 } 506 } else 507 { 508 /* The simple version */ 509 if (!mycmp(lst->banstr, str)) 510 return 1; 511 } 512 } 513 return 0; 514 } 515 516 /** Add user to the channel. 517 * This adds both the Member struct to the channel->members linked list 518 * and also the Membership struct to the client->user->channel linked list. 519 * @note This does NOT send the JOIN, it only does the linked list stuff. 520 */ 521 void add_user_to_channel(Channel *channel, Client *client, const char *modes) 522 { 523 Member *m; 524 Membership *mb; 525 const char *p; 526 527 if (!client->user) 528 return; 529 530 m = make_member(); 531 m->client = client; 532 m->next = channel->members; 533 channel->members = m; 534 channel->users++; 535 536 mb = make_membership(); 537 mb->channel = channel; 538 mb->next = client->user->channel; 539 client->user->channel = mb; 540 client->user->joined++; 541 542 for (p = modes; *p; p++) 543 add_member_mode_fast(m, mb, *p); 544 545 RunHook(HOOKTYPE_JOIN_DATA, client, channel); 546 } 547 548 /** Remove the user from the channel. 549 * This frees the memberships, decreases the user counts, 550 * destroys the channel if needed, etc. 551 * This does not send any PART/KICK/..! 552 * @param client The client that is removed from the channel 553 * @param channel The channel 554 * @param dont_log Set to 1 if it should not be logged as a part, 555 * for example if you are already logging it as a kick. 556 */ 557 int remove_user_from_channel(Client *client, Channel *channel, int dont_log) 558 { 559 Member **m; 560 Member *m2; 561 Membership **mb; 562 Membership *mb2; 563 564 /* Update channel->members list */ 565 for (m = &channel->members; (m2 = *m); m = &m2->next) 566 { 567 if (m2->client == client) 568 { 569 *m = m2->next; 570 free_member(m2); 571 break; 572 } 573 } 574 575 /* Update client->user->channel list */ 576 for (mb = &client->user->channel; (mb2 = *mb); mb = &mb2->next) 577 { 578 if (mb2->channel == channel) 579 { 580 *mb = mb2->next; 581 free_membership(mb2); 582 break; 583 } 584 } 585 586 /* Update user record to reflect 1 less joined */ 587 client->user->joined--; 588 589 if (!dont_log) 590 { 591 if (MyUser(client)) 592 { 593 unreal_log(ULOG_INFO, "part", "LOCAL_CLIENT_PART", client, 594 "User $client left $channel", 595 log_data_channel("channel", channel)); 596 } else { 597 unreal_log(ULOG_INFO, "part", "REMOTE_CLIENT_PART", client, 598 "User $client left $channel", 599 log_data_channel("channel", channel)); 600 } 601 } 602 603 /* Now sub1_from_channel() will deal with the channel record 604 * and destroy the channel if needed. 605 */ 606 return sub1_from_channel(channel); 607 } 608 609 /** Returns 1 if channel has this channel mode set and 0 if not */ 610 int has_channel_mode(Channel *channel, char mode) 611 { 612 Cmode *cm; 613 614 for (cm=channelmodes; cm; cm = cm->next) 615 if ((cm->letter == mode) && (channel->mode.mode & cm->mode)) 616 return 1; 617 618 return 0; /* Not found */ 619 } 620 621 /** Returns 1 if channel has this mode is set and 0 if not */ 622 int has_channel_mode_raw(Cmode_t m, char mode) 623 { 624 Cmode *cm; 625 626 for (cm=channelmodes; cm; cm = cm->next) 627 if ((cm->letter == mode) && (m & cm->mode)) 628 return 1; 629 630 return 0; /* Not found */ 631 } 632 633 /** Get the extended channel mode 'bit' value (eg: 0x20) by character (eg: 'Z') */ 634 Cmode_t get_extmode_bitbychar(char m) 635 { 636 Cmode *cm; 637 638 for (cm=channelmodes; cm; cm = cm->next) 639 if (cm->letter == m) 640 return cm->mode; 641 642 return 0; 643 } 644 645 /** Write the "simple" list of channel modes for channel channel onto buffer mbuf with the parameters in pbuf. 646 * @param client The client requesting the mode list (can be NULL) 647 * @param mbuf Modes will be stored here 648 * @param pbuf Mode parameters will be stored here 649 * @param mbuf_size Length of the mbuf buffer 650 * @param pbuf_size Length of the pbuf buffer 651 * @param channel The channel to fetch modes from 652 * @param hide_local_modes If set to 1 then we will hide local channel modes like Z and d 653 * (eg: if you intend to send the buffer to a remote server) 654 */ 655 void channel_modes(Client *client, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel, int hide_local_modes) 656 { 657 int show_mode_parameters = 0; 658 Cmode *cm; 659 660 if (!mbuf_size || !pbuf_size) 661 return; 662 663 if (!client || IsMember(client, channel) || IsServer(client) || IsMe(client) || IsULine(client) || 664 ValidatePermissionsForPath("channel:see:mode:remote",client,NULL,channel,NULL)) 665 { 666 show_mode_parameters = 1; 667 } 668 669 *pbuf = '\0'; 670 strlcpy(mbuf, "+", mbuf_size); 671 672 for (cm=channelmodes; cm; cm = cm->next) 673 { 674 if (cm->letter && 675 !(hide_local_modes && cm->local) && 676 (channel->mode.mode & cm->mode)) 677 { 678 char flag = cm->letter; 679 680 if (mbuf_size) 681 strlcat_letter(mbuf, flag, mbuf_size); 682 683 if (cm->paracount && show_mode_parameters) 684 { 685 strlcat(pbuf, cm_getparameter(channel, flag), pbuf_size); 686 strlcat(pbuf, " ", pbuf_size); 687 } 688 } 689 } 690 691 /* Remove the trailing space from the parameters -- codemastr */ 692 if (*pbuf) 693 pbuf[strlen(pbuf)-1]='\0'; 694 } 695 696 /** Make a pretty mask from the input string - only used by SILENCE 697 */ 698 char *pretty_mask(const char *mask_in) 699 { 700 char mask[512]; 701 char *cp, *user, *host; 702 703 strlcpy(mask, mask_in, sizeof(mask)); 704 705 if ((user = strchr((cp = mask), '!'))) 706 *user++ = '\0'; 707 708 if ((host = strrchr(user ? user : cp, '@'))) 709 { 710 *host++ = '\0'; 711 if (!user) 712 return make_nick_user_host(NULL, cp, host); 713 } 714 else if (!user && strchr(cp, '.')) 715 { 716 return make_nick_user_host(NULL, NULL, cp); 717 } 718 return make_nick_user_host(cp, user, host); 719 } 720 721 /** Trim a string - rather than cutting it off sharply, this adds a * at the end. 722 * So "toolong" becomes "toolon*" 723 */ 724 char *trim_str(char *str, int len) 725 { 726 int l; 727 if (!str) 728 return NULL; 729 if ((l = strlen(str)) > len) 730 { 731 str += l - len; 732 *str = '*'; 733 } 734 return str; 735 } 736 737 /* Convert regular ban (non-extban) if needed. 738 * This does things like: 739 * nick!user@host -> nick!user@host (usually no change) 740 * nickkkkkkkkkkkkkkkkkkkkkkkkkk!user@host -> nickkkkkkk*!user@host (dealing with NICKLEN restrictions and such). 741 * user@host -> *!user@host 742 * 1.2.3.4 -> *!*@1.2.3.4 (converting IP to a proper mask) 743 * @param mask Incoming mask (this will be touched/fragged!) 744 * @param buf Output buffer 745 * @param buflen Length of the output buffer, eg sizeof(buf) 746 * @retval The sanitized mask, or NULL if it should be rejected fully. 747 * @note Since 'mask' will be fragged, you most likely wish to pass a copy of it rather than the original. 748 */ 749 const char *convert_regular_ban(char *mask, char *buf, size_t buflen) 750 { 751 static char namebuf[USERLEN + HOSTLEN + 6]; 752 char *user, *host; 753 754 if (!*mask) 755 return NULL; /* empty extban */ 756 757 if (!buf) 758 { 759 buf = namebuf; 760 buflen = sizeof(namebuf); 761 } 762 763 if ((*mask == '~') && !strchr(mask, '@')) 764 { 765 /* has a '~', which makes it look like an extban, 766 * but is not a user@host ban, too confusing. 767 */ 768 return NULL; 769 } 770 771 if ((user = strchr(mask, '!'))) 772 *user++ = '\0'; 773 774 if ((host = strrchr(user ? user : mask, '@'))) 775 { 776 *host++ = '\0'; 777 if (!user) 778 return make_nick_user_host_r(buf, buflen, NULL, trim_str(mask,USERLEN), trim_str(host,HOSTLEN)); 779 } 780 else if (!user && (strchr(mask, '.') || strchr(mask, ':'))) 781 { 782 /* 1.2.3.4 -> *!*@1.2.3.4 (and the same for IPv6) */ 783 return make_nick_user_host_r(buf, buflen, NULL, NULL, trim_str(mask,HOSTLEN)); 784 } 785 786 /* regular nick!user@host with the auto-trimming feature */ 787 return make_nick_user_host_r(buf, buflen, trim_str(mask,NICKLEN), trim_str(user,USERLEN), trim_str(host,HOSTLEN)); 788 } 789 790 /** Make a proper ban mask. 791 * This takes user input (eg: "nick") and converts it to a mask suitable 792 * in the +beI lists (eg: "nick!*@*"). It also deals with extended bans, 793 * in which case it will call the extban->conv_param() function. 794 * @param mask The ban mask 795 * @param what MODE_DEL or MODE_ADD 796 * @param client The client adding/removing this ban mask 797 * @param conv_options Options for BanContext.conv_options (eg BCTX_CONV_OPTION_WRITE_LETTER_BANS) 798 * @returns pointer to correct banmask or NULL in case of error 799 * @note A pointer is returned to a static buffer, which is overwritten 800 * on next clean_ban_mask or make_nick_user_host call. 801 */ 802 const char *clean_ban_mask(const char *mask_in, int what, Client *client, int conv_options) 803 { 804 char *cp, *x; 805 static char mask[512]; 806 807 /* Strip any ':' at beginning since that would cause a desync */ 808 for (; (*mask_in && (*mask_in == ':')); mask_in++); 809 if (!*mask_in) 810 return NULL; 811 812 /* Work on a copy */ 813 strlcpy(mask, mask_in, sizeof(mask)); 814 815 cp = strchr(mask, ' '); 816 if (cp) 817 *cp = '\0'; 818 819 /* Forbid ASCII <= 32 in all bans */ 820 for (x = mask; *x; x++) 821 if (*x <= ' ') 822 return NULL; 823 824 /* Extended ban? */ 825 if (is_extended_ban(mask)) 826 { 827 const char *nextbanstr; 828 Extban *extban; 829 830 if (RESTRICT_EXTENDEDBANS && MyUser(client) && !ValidatePermissionsForPath("immune:restrict-extendedbans",client,NULL,NULL,NULL)) 831 { 832 if (!strcmp(RESTRICT_EXTENDEDBANS, "*")) 833 { 834 sendnotice(client, "Setting/removing of extended bans has been disabled"); 835 return NULL; 836 } 837 if (strchr(RESTRICT_EXTENDEDBANS, mask[1])) 838 { 839 sendnotice(client, "Setting/removing of extended bantypes '%s' has been disabled", 840 RESTRICT_EXTENDEDBANS); 841 return NULL; 842 } 843 } 844 845 extban = findmod_by_bantype(mask, &nextbanstr); 846 if (!extban) 847 { 848 /* extended bantype not supported, what to do? 849 * Here are the rules: 850 * - if from a remote client/server: allow it (easy upgrading, 851 * no desync) 852 * - if from a local client trying to REMOVE the extban, 853 * allow it too (so you don't get "unremovable" extbans). 854 */ 855 if (!MyUser(client) || (what == MODE_DEL)) 856 return mask; /* allow it */ 857 return NULL; /* reject */ 858 } 859 860 if (extban->conv_param) 861 { 862 const char *ret; 863 static char retbuf[512]; 864 BanContext *b = safe_alloc(sizeof(BanContext)); 865 b->client = client; 866 b->what = what; 867 b->banstr = nextbanstr; 868 b->conv_options = conv_options; 869 ret = extban->conv_param(b, extban); 870 ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf)); 871 safe_free(b); 872 return ret; 873 } 874 /* else, do some basic sanity checks and cut it off at 80 bytes */ 875 if ((mask[1] != ':') || (mask[2] == '\0')) 876 return NULL; /* require a ":<char>" after extban type */ 877 if (strlen(mask) > 80) 878 mask[80] = '\0'; 879 return mask; 880 } 881 882 return convert_regular_ban(mask, NULL, 0); 883 } 884 885 /** Check if 'client' matches an invite exception (+I) on 'channel' */ 886 int find_invex(Channel *channel, Client *client) 887 { 888 Ban *inv; 889 BanContext *b = safe_alloc(sizeof(BanContext)); 890 891 b->client = client; 892 b->channel = channel; 893 b->ban_check_types = BANCHK_JOIN; 894 895 for (inv = channel->invexlist; inv; inv = inv->next) 896 { 897 b->banstr = inv->banstr; 898 if (ban_check_mask(b)) 899 { 900 safe_free(b); 901 return 1; 902 } 903 } 904 905 safe_free(b); 906 return 0; 907 } 908 909 /** Remove unwanted characters from channel name. 910 * You must call this before creating a new channel, 911 * eg in case of /JOIN. 912 */ 913 int valid_channelname(const char *cname) 914 { 915 const char *p; 916 917 /* Channel name must start with a dash */ 918 if (*cname != '#') 919 return 0; 920 921 if (strlen(cname) > CHANNELLEN) 922 return 0; 923 924 if ((iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY) || !iConf.allowed_channelchars) 925 { 926 /* The default up to and including UnrealIRCd 4 */ 927 for (p = cname; *p; p++) 928 { 929 if (*p < 33 || *p == ',' || *p == ':') 930 return 0; 931 } 932 } else 933 if (iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ASCII) 934 { 935 /* The strict setting: only allow ASCII 32-128, except some chars */ 936 for (p = cname; *p; p++) 937 { 938 if (*p < 33 || *p == ',' || *p == ':' || *p > 127) 939 return 0; 940 } 941 } else 942 if (iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_UTF8) 943 { 944 /* Only allow UTF8, and also disallow some chars */ 945 for (p = cname; *p; p++) 946 { 947 if (*p < 33 || *p == ',' || *p == ':') 948 return 0; 949 } 950 /* And run it through the UTF8 validator */ 951 if (!unrl_utf8_validate(cname, (const char **)&p)) 952 return 0; 953 } else 954 { 955 /* Impossible */ 956 abort(); 957 } 958 return 1; /* Valid */ 959 } 960 961 void initlist_channels(void) 962 { 963 channel_pool = mp_pool_new(sizeof(Channel), 512 * 1024); 964 } 965 966 /** Create channel 'name' (or if it exists, return the existing one) 967 * @param name Channel name 968 * @param flag If set to 'CREATE' then the channel is 969 * created if it does not exist. 970 * @returns Pointer to channel (new or existing). 971 * @note Be sure to call valid_channelname() first before 972 * you blindly call this function! 973 */ 974 Channel *make_channel(const char *name) 975 { 976 Channel *channel; 977 int len; 978 char *p; 979 char namebuf[CHANNELLEN+1]; 980 981 if (BadPtr(name)) 982 return NULL; 983 984 /* Copy and silently truncate */ 985 strlcpy(namebuf, name, sizeof(namebuf)); 986 987 /* Copied from valid_channelname(), the minimal requirements */ 988 for (p = namebuf; *p; p++) 989 { 990 if (*p < 33 || *p == ',' || *p == ':') 991 { 992 *p = '\0'; 993 break; 994 } 995 } 996 997 /* Exists? Return it. */ 998 if ((channel = find_channel(name))) 999 return channel; 1000 1001 channel = mp_pool_get(channel_pool); 1002 memset(channel, 0, sizeof(Channel)); 1003 1004 strlcpy(channel->name, name, sizeof(channel->name)); 1005 1006 if (channels) 1007 channels->prevch = channel; 1008 1009 channel->topic = NULL; 1010 channel->topic_nick = NULL; 1011 channel->prevch = NULL; 1012 channel->nextch = channels; 1013 channel->creationtime = TStime(); 1014 channels = channel; 1015 add_to_channel_hash_table(channel->name, channel); 1016 irccounts.channels++; 1017 1018 RunHook(HOOKTYPE_CHANNEL_CREATE, channel); 1019 1020 return channel; 1021 } 1022 1023 /** Is the user 'client' invited to channel 'channel' by a chanop? 1024 * @param client The client who was invited 1025 * @param channel The channel to which the person was invited 1026 */ 1027 int is_invited(Client *client, Channel *channel) 1028 { 1029 int invited = 0; 1030 RunHook(HOOKTYPE_IS_INVITED, client, channel, &invited); 1031 return invited; 1032 } 1033 1034 /** Subtract one user from channel i. Free the channel if it became empty. 1035 * @param channel The channel 1036 * @returns 1 if the channel was freed, 0 if the channel still exists. 1037 */ 1038 int sub1_from_channel(Channel *channel) 1039 { 1040 Ban *ban; 1041 Link *lp; 1042 int should_destroy = 1; 1043 1044 --channel->users; 1045 if (channel->users > 0) 1046 return 0; 1047 1048 /* No users in the channel anymore */ 1049 channel->users = 0; /* to be sure */ 1050 1051 /* If the channel is +P then this hook will actually stop destruction. */ 1052 RunHook(HOOKTYPE_CHANNEL_DESTROY, channel, &should_destroy); 1053 if (!should_destroy) 1054 return 0; 1055 1056 /* We are now going to destroy the channel. 1057 * But first we will destroy all kinds of references and lists... 1058 */ 1059 1060 moddata_free_channel(channel); 1061 1062 while (channel->banlist) 1063 { 1064 ban = channel->banlist; 1065 channel->banlist = ban->next; 1066 safe_free(ban->banstr); 1067 safe_free(ban->who); 1068 free_ban(ban); 1069 } 1070 while (channel->exlist) 1071 { 1072 ban = channel->exlist; 1073 channel->exlist = ban->next; 1074 safe_free(ban->banstr); 1075 safe_free(ban->who); 1076 free_ban(ban); 1077 } 1078 while (channel->invexlist) 1079 { 1080 ban = channel->invexlist; 1081 channel->invexlist = ban->next; 1082 safe_free(ban->banstr); 1083 safe_free(ban->who); 1084 free_ban(ban); 1085 } 1086 1087 /* free extcmode params */ 1088 extcmode_free_paramlist(channel->mode.mode_params); 1089 1090 safe_free(channel->mode_lock); 1091 safe_free(channel->topic); 1092 safe_free(channel->topic_nick); 1093 1094 if (channel->prevch) 1095 channel->prevch->nextch = channel->nextch; 1096 else 1097 channels = channel->nextch; 1098 1099 if (channel->nextch) 1100 channel->nextch->prevch = channel->prevch; 1101 del_from_channel_hash_table(channel->name, channel); 1102 1103 irccounts.channels--; 1104 mp_pool_release(channel); 1105 return 1; 1106 } 1107 1108 /** Set channel mode lock on the channel, these are modes that users cannot change. 1109 * @param client The client or server issueing the MLOCK 1110 * @param channel The channel that will be MLOCK'ed 1111 * @param newmlock The MLOCK string: list of mode characters that are locked 1112 */ 1113 void set_channel_mlock(Client *client, Channel *channel, const char *newmlock, int propagate) 1114 { 1115 safe_strdup(channel->mode_lock, newmlock); 1116 1117 if (propagate) 1118 { 1119 sendto_server(client, 0, 0, NULL, ":%s MLOCK %lld %s :%s", 1120 client->id, (long long)channel->creationtime, channel->name, 1121 BadPtr(channel->mode_lock) ? "" : channel->mode_lock); 1122 } 1123 } 1124 1125 /** Parse a channelmode line. 1126 * @in pm A ParseMode struct, used to return values and to maintain internal state. 1127 * @in modebuf_in Buffer pointing to mode characters (eg: +snk-l) 1128 * @in parabuf_in Buffer pointing to all parameters (eg: key 123) 1129 * @retval Returns 1 if we have valid data to return, 0 if at end of mode line. 1130 * @section parse_chanmode_example Example: 1131 * @code 1132 * ParseMode pm; 1133 * int ret; 1134 * for (ret = parse_chanmode(&pm, modebuf, parabuf); ret; ret = parse_chanmode(&pm, NULL, NULL)) 1135 * { 1136 * unreal_log(ULOG_INFO, "test", "TEST", "Got %c%c %s", 1137 * pm.what == MODE_ADD ? '+' : '-', 1138 * pm.modechar, 1139 * pm.param ? pm.param : ""); 1140 * } 1141 * @endcode 1142 */ 1143 int parse_chanmode(ParseMode *pm, const char *modebuf_in, const char *parabuf_in) 1144 { 1145 if (modebuf_in) 1146 { 1147 /* Initialize */ 1148 memset(pm, 0, sizeof(ParseMode)); 1149 pm->modebuf = modebuf_in; 1150 pm->parabuf = parabuf_in; 1151 pm->what = MODE_ADD; 1152 } 1153 1154 while(1) 1155 { 1156 if (*pm->modebuf == '\0') 1157 return 0; 1158 else if (*pm->modebuf == '+') 1159 { 1160 pm->what = MODE_ADD; 1161 pm->modebuf++; 1162 continue; 1163 } 1164 else if (*pm->modebuf == '-') 1165 { 1166 pm->what = MODE_DEL; 1167 pm->modebuf++; 1168 continue; 1169 } 1170 else 1171 { 1172 CoreChannelModeTable *tab = &corechannelmodetable[0]; 1173 Cmode *cm; 1174 int eatparam = 0; 1175 1176 /* Set some defaults */ 1177 pm->extm = NULL; 1178 pm->modechar = *pm->modebuf; 1179 pm->param = NULL; 1180 1181 while (tab->mode != 0x0) 1182 { 1183 if (tab->flag == *pm->modebuf) 1184 break; 1185 tab++; 1186 } 1187 1188 if (tab->mode) 1189 { 1190 /* INTERNAL MODE */ 1191 if (tab->parameters) 1192 { 1193 eatparam = 1; 1194 } 1195 } else { 1196 /* EXTENDED CHANNEL MODE */ 1197 int found = 0; 1198 for (cm=channelmodes; cm; cm = cm->next) 1199 { 1200 if (cm->letter == *pm->modebuf) 1201 { 1202 found = 1; 1203 break; 1204 } 1205 } 1206 if (!found) 1207 { 1208 /* Not found. Will be ignored, just move on.. */ 1209 pm->modebuf++; 1210 continue; 1211 } 1212 pm->extm = cm; 1213 if (cm->paracount == 1) 1214 { 1215 if (pm->what == MODE_ADD) 1216 eatparam = 1; 1217 else if (cm->unset_with_param) 1218 eatparam = 1; 1219 /* else 0 (if MODE_DEL && !unset_with_param) */ 1220 } 1221 } 1222 1223 if (eatparam) 1224 { 1225 /* Hungry.. */ 1226 if (pm->parabuf && *pm->parabuf) 1227 { 1228 const char *start, *end; 1229 for (; *pm->parabuf == ' '; pm->parabuf++); /* skip whitespace */ 1230 start = pm->parabuf; 1231 if (*pm->parabuf == '\0') 1232 { 1233 pm->modebuf++; 1234 continue; /* invalid, got mode but no parameter available */ 1235 } 1236 end = strchr(start, ' '); 1237 /* copy start .. end (where end may be null, then just copy all) */ 1238 if (end) 1239 { 1240 pm->parabuf = end + 1; /* point to next param, or \0 */ 1241 if (end - start + 1 > sizeof(pm->buf)) 1242 end = start + sizeof(pm->buf); /* 'never' reached */ 1243 strlcpy(pm->buf, start, end - start + 1); 1244 } 1245 else 1246 { 1247 strlcpy(pm->buf, start, sizeof(pm->buf)); 1248 pm->parabuf = pm->parabuf + strlen(pm->parabuf); /* point to \0 at end */ 1249 } 1250 stripcrlf(pm->buf); /* needed for unreal_server_compat.c */ 1251 pm->param = pm->buf; 1252 } else { 1253 pm->modebuf++; 1254 continue; /* invalid, got mode but no parameter available */ 1255 } 1256 } 1257 } 1258 pm->modebuf++; /* advance pointer */ 1259 return 1; 1260 } 1261 } 1262 1263 /** Returns 1 if both clients are at least in 1 same channel */ 1264 int has_common_channels(Client *c1, Client *c2) 1265 { 1266 Membership *lp; 1267 1268 for (lp = c1->user->channel; lp; lp = lp->next) 1269 { 1270 if (IsMember(c2, lp->channel) && user_can_see_member(c1, c2, lp->channel)) 1271 return 1; 1272 } 1273 return 0; 1274 } 1275 1276 /** Returns 1 if user 'user' can see channel member 'target'. 1277 * This may return 0 if the user is 'invisible' due to mode +D rules. 1278 * NOTE: Membership is unchecked, assumed membership of both. 1279 */ 1280 int user_can_see_member(Client *user, Client *target, Channel *channel) 1281 { 1282 Hook *h; 1283 int j = 0; 1284 1285 if (user == target) 1286 return 1; 1287 1288 for (h = Hooks[HOOKTYPE_VISIBLE_IN_CHANNEL]; h; h = h->next) 1289 { 1290 j = (*(h->func.intfunc))(target,channel); 1291 if (j != 0) 1292 break; 1293 } 1294 1295 /* We must ensure that user is allowed to "see" target */ 1296 if (j != 0 && !(check_channel_access(target, channel, "hoaq") || check_channel_access(target,channel, "v")) && !check_channel_access(user, channel, "hoaq")) 1297 return 0; 1298 1299 return 1; 1300 } 1301 1302 /** Returns 1 if user 'target' is invisible in channel 'channel'. 1303 * This may return 0 if the user is 'invisible' due to mode +D rules. 1304 */ 1305 int invisible_user_in_channel(Client *target, Channel *channel) 1306 { 1307 Hook *h; 1308 int j = 0; 1309 1310 for (h = Hooks[HOOKTYPE_VISIBLE_IN_CHANNEL]; h; h = h->next) 1311 { 1312 j = (*(h->func.intfunc))(target,channel); 1313 if (j != 0) 1314 break; 1315 } 1316 1317 /* We must ensure that user is allowed to "see" target */ 1318 if (j != 0 && !(check_channel_access(target, channel, "hoaq") || check_channel_access(target,channel, "v"))) 1319 return 1; 1320 1321 return 0; 1322 } 1323 1324 /** Send a message to the user that (s)he is using an invalid channel name. 1325 * This is usually called after an if (MyUser(client) && !valid_channelname(name)). 1326 * @param client The client to send the message to. 1327 * @param channelname The (invalid) channel that the user tried to join. 1328 */ 1329 void send_invalid_channelname(Client *client, const char *channelname) 1330 { 1331 const char *reason; 1332 1333 if (*channelname != '#') 1334 { 1335 reason = "Channel name must start with a hash mark (#)"; 1336 } else 1337 if (strlen(channelname) > CHANNELLEN) 1338 { 1339 reason = "Channel name is too long"; 1340 } else { 1341 switch(iConf.allowed_channelchars) 1342 { 1343 case ALLOWED_CHANNELCHARS_ASCII: 1344 reason = "Channel name contains illegal characters (must be ASCII)"; 1345 break; 1346 case ALLOWED_CHANNELCHARS_UTF8: 1347 reason = "Channel name contains illegal characters (must be valid UTF8)"; 1348 break; 1349 case ALLOWED_CHANNELCHARS_ANY: 1350 default: 1351 reason = "Channel name contains illegal characters"; 1352 } 1353 } 1354 1355 sendnumeric(client, ERR_FORBIDDENCHANNEL, channelname, reason); 1356 } 1357 1358 /** Is the provided string possibly an extended ban? 1359 * Note that it still may not exist, it just tests the first part. 1360 * @param str The string to check (eg "~account:xyz") 1361 */ 1362 int is_extended_ban(const char *str) 1363 { 1364 const char *p; 1365 1366 if (*str != '~') 1367 return 0; 1368 for (p = str+1; *p; p++) 1369 { 1370 if (!isalnum(*p)) 1371 { 1372 if (*p == ':') 1373 return 1; 1374 } 1375 } 1376 return 0; 1377 } 1378 1379 /** Is the provided string possibly an extended server ban? 1380 * Actually this is only a very light check. 1381 * It may still not exist, it just tests the first part. 1382 * @param str The string to check (eg "~account:xyz") 1383 * The only difference between this and is_extended_ban() 1384 * is that we allow a % at the beginning for soft-bans. 1385 * @see is_extended_ban() 1386 */ 1387 int is_extended_server_ban(const char *str) 1388 { 1389 if (*str == '%') 1390 str++; 1391 return is_extended_ban(str); 1392 } 1393 1394 /** Check if it is an empty (useless) mode, namely "", "+" or "-". 1395 * Typically called as: empty_mode(modebuf) 1396 */ 1397 int empty_mode(const char *m) 1398 { 1399 if (!*m || (((m[0] == '+') || (m[0] == '-')) && m[1] == '\0')) 1400 return 1; 1401 return 0; 1402 } 1403 1404 /** Free everything of/in a MultiLineMode */ 1405 void free_multilinemode(MultiLineMode *m) 1406 { 1407 int i; 1408 if (m == NULL) 1409 return; 1410 for (i=0; i < m->numlines; i++) 1411 { 1412 safe_free(m->modeline[i]); 1413 safe_free(m->paramline[i]); 1414 } 1415 safe_free(m); 1416 }