unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
api-channelmode.c (30017B)
1 /************************************************************************ 2 * IRC - Internet Relay Chat, src/api-channelmode.c 3 * (C) 2003-2007 Bram Matthys (Syzop) and the UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 /** @file 24 * @brief The channel mode API used by modules. 25 */ 26 27 #include "unrealircd.h" 28 29 /** This is the extended channel mode API, 30 * see also https://www.unrealircd.org/docs/Dev:Channel_Mode_API 31 * for more information. 32 * @defgroup ChannelModeAPI Channel mode API 33 * @{ 34 */ 35 36 /** List of all channel modes, their handlers, etc */ 37 Cmode *channelmodes = NULL; 38 39 /** @} */ 40 41 /** Channel parameter to slot# mapping - used by GETPARAMSLOT() macro */ 42 MODVAR unsigned char param_to_slot_mapping[256]; 43 /** Extended channel modes in use - used by ISUPPORT/005 numeric only */ 44 char extchmstr[4][64]; 45 46 /* Private functions (forward declaration) and variables */ 47 static void make_cmodestr(void); 48 static char previous_chanmodes[256]; 49 static char previous_prefix[256]; 50 static Cmode *ParamTable[MAXPARAMMODES+1]; 51 static void unload_extcmode_commit(Cmode *cmode); 52 53 /** Create the strings that are used for CHANMODES=a,b,c,d in numeric 005 */ 54 void make_extcmodestr() 55 { 56 char *p; 57 Cmode *cm; 58 int i; 59 60 extchmstr[0][0] = extchmstr[1][0] = extchmstr[2][0] = extchmstr[3][0] = '\0'; 61 62 /* type 1: lists (like b/e) */ 63 /* [NOT IMPLEMENTED IN EXTCMODES] */ 64 65 /* type 2: 1 par to set/unset (has .unset_with_param) */ 66 p = extchmstr[1]; 67 for (cm=channelmodes; cm; cm = cm->next) 68 if (cm->paracount && cm->letter && cm->unset_with_param && (cm->type != CMODE_MEMBER)) 69 *p++ = cm->letter; 70 *p = '\0'; 71 72 /* type 3: 1 param to set, 0 params to unset (does not have .unset_with_param) */ 73 p = extchmstr[2]; 74 for (cm=channelmodes; cm; cm = cm->next) 75 if (cm->paracount && cm->letter && !cm->unset_with_param) 76 *p++ = cm->letter; 77 *p = '\0'; 78 79 /* type 4: paramless modes */ 80 p = extchmstr[3]; 81 for (cm=channelmodes; cm; cm = cm->next) 82 if (!cm->paracount && cm->letter) 83 *p++ = cm->letter; 84 *p = '\0'; 85 } 86 87 /** Create the string that is used in numeric 004 */ 88 static void make_cmodestr(void) 89 { 90 Cmode *cm; 91 char *p = &cmodestring[0]; 92 CoreChannelModeTable *tab = &corechannelmodetable[0]; 93 int i; 94 while (tab->mode != 0x0) 95 { 96 *p = tab->flag; 97 p++; 98 tab++; 99 } 100 for (cm=channelmodes; cm; cm = cm->next) 101 if (cm->letter) 102 *p++ = cm->letter; 103 *p = '\0'; 104 } 105 106 /** Check for changes - if any are detected, we broadcast the change */ 107 void extcmodes_check_for_changed_channel_modes(void) 108 { 109 char chanmodes[256]; 110 ISupport *isup; 111 112 //sort_cmodes(); 113 make_cmodestr(); 114 make_extcmodestr(); 115 116 snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR1, EXPAR1); 117 safe_strdup(me.server->features.chanmodes[0], chanmodes); 118 snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR2); 119 safe_strdup(me.server->features.chanmodes[1], chanmodes); 120 snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR3); 121 safe_strdup(me.server->features.chanmodes[2], chanmodes); 122 snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR4); 123 safe_strdup(me.server->features.chanmodes[3], chanmodes); 124 125 ircsnprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", 126 me.server->features.chanmodes[0], 127 me.server->features.chanmodes[1], 128 me.server->features.chanmodes[2], 129 me.server->features.chanmodes[3]); 130 131 isup = ISupportFind("CHANMODES"); 132 if (!isup) 133 { 134 strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes)); 135 return; /* not booted yet. then we are done here. */ 136 } 137 138 ISupportSetValue(isup, chanmodes); 139 140 if (*previous_chanmodes && strcmp(chanmodes, previous_chanmodes)) 141 { 142 unreal_log(ULOG_INFO, "mode", "CHANNEL_MODES_CHANGED", NULL, 143 "Channel modes changed at runtime: $old_channel_modes -> $new_channel_modes", 144 log_data_string("old_channel_modes", previous_chanmodes), 145 log_data_string("new_channel_modes", chanmodes)); 146 /* Broadcast change to all (locally connected) servers */ 147 sendto_server(NULL, 0, 0, NULL, "PROTOCTL CHANMODES=%s", chanmodes); 148 } 149 150 strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes)); 151 } 152 153 void make_prefix(char **isupport_prefix, char **isupport_statusmsg) 154 { 155 static char prefix[256]; 156 static char prefix_prefix[256]; 157 char prefix_modes[256]; 158 int rank[256]; 159 Cmode *cm; 160 int n; 161 162 *prefix = *prefix_prefix = *prefix_modes = '\0'; 163 164 for (n=0, cm=channelmodes; cm && n < ARRAY_SIZEOF(rank)-1; cm = cm->next) 165 { 166 if ((cm->type == CMODE_MEMBER) && cm->letter) 167 { 168 strlcat_letter(prefix_prefix, cm->prefix, sizeof(prefix_prefix)); 169 strlcat_letter(prefix_modes, cm->letter, sizeof(prefix_modes)); 170 rank[n] = cm->rank; 171 n++; 172 } 173 } 174 175 if (*prefix_prefix) 176 { 177 int i, j; 178 /* Now sort the damn thing */ 179 for (i=0; i < n; i++) 180 { 181 for (j=i+1; j < n; j++) 182 { 183 if (rank[i] < rank[j]) 184 { 185 /* swap */ 186 char save; 187 int save_rank; 188 save = prefix_prefix[i]; 189 prefix_prefix[i] = prefix_prefix[j]; 190 prefix_prefix[j] = save; 191 save = prefix_modes[i]; 192 prefix_modes[i] = prefix_modes[j]; 193 prefix_modes[j] = save; 194 save_rank = rank[i]; 195 rank[i] = rank[j]; 196 rank[j] = save_rank; 197 } 198 } 199 } 200 snprintf(prefix, sizeof(prefix), "(%s)%s", prefix_modes, prefix_prefix); 201 } 202 203 *isupport_prefix = prefix; 204 *isupport_statusmsg = prefix_prefix; 205 } 206 207 void extcmodes_check_for_changed_prefixes(void) 208 { 209 ISupport *isup; 210 char *prefix, *statusmsg; 211 212 make_prefix(&prefix, &statusmsg); 213 ISupportSet(NULL, "PREFIX", prefix); 214 ISupportSet(NULL, "STATUSMSG", statusmsg); 215 216 if (*previous_prefix && strcmp(prefix, previous_prefix)) 217 { 218 unreal_log(ULOG_INFO, "mode", "PREFIX_CHANGED", NULL, 219 "Prefix changed at runtime: $old_prefix -> $new_prefix", 220 log_data_string("old_prefix", previous_prefix), 221 log_data_string("new_prefix", prefix)); 222 /* Broadcast change to all (locally connected) servers */ 223 sendto_server(NULL, 0, 0, NULL, "PROTOCTL PREFIX=%s", prefix); 224 } 225 226 strlcpy(previous_prefix, prefix, sizeof(previous_prefix)); 227 } 228 229 /** Check for changes - if any are detected, we broadcast the change */ 230 void extcmodes_check_for_changes(void) 231 { 232 extcmodes_check_for_changed_channel_modes(); 233 extcmodes_check_for_changed_prefixes(); 234 } 235 236 /** Initialize the extended channel modes system */ 237 void extcmode_init(void) 238 { 239 memset(&extchmstr, 0, sizeof(extchmstr)); 240 memset(¶m_to_slot_mapping, 0, sizeof(param_to_slot_mapping)); 241 *previous_chanmodes = '\0'; 242 *previous_prefix = '\0'; 243 } 244 245 /** Update letter->slot mapping and slot->handler mapping */ 246 void extcmode_para_addslot(Cmode *cm, int slot) 247 { 248 if ((slot < 0) || (slot > MAXPARAMMODES)) 249 abort(); 250 cm->param_slot = slot; 251 ParamTable[slot] = cm; 252 param_to_slot_mapping[cm->letter] = slot; 253 } 254 255 /** Update letter->slot mapping and slot->handler mapping */ 256 void extcmode_para_delslot(Cmode *cm, int slot) 257 { 258 if ((slot < 0) || (slot > MAXPARAMMODES)) 259 abort(); 260 ParamTable[slot] = NULL; 261 param_to_slot_mapping[cm->letter] = 0; 262 } 263 264 void channelmode_add_sorted(Cmode *n) 265 { 266 Cmode *m; 267 268 if (channelmodes == NULL) 269 { 270 channelmodes = n; 271 return; 272 } 273 274 for (m = channelmodes; m; m = m->next) 275 { 276 if (m->letter == '\0') 277 abort(); 278 if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) 279 { 280 /* Insert us before */ 281 if (m->prev) 282 m->prev->next = n; 283 else 284 channelmodes = n; /* new head */ 285 n->prev = m->prev; 286 287 n->next = m; 288 m->prev = n; 289 return; 290 } 291 if (!m->next) 292 { 293 /* Append us at end */ 294 m->next = n; 295 n->prev = m; 296 return; 297 } 298 } 299 } 300 301 /** @defgroup ChannelModeAPI Channel mode API 302 * @{ 303 */ 304 305 /** Register a new channel mode (Channel mode API). 306 * @param module The module requesting this channel mode (usually: modinfo->handle) 307 * @param req Details of the channel mode request 308 * @param mode Store the mode value (bit) here on success 309 * @returns the newly created channel mode, or NULL in case of error. 310 */ 311 Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode) 312 { 313 int paraslot = -1; 314 int existing = 0; 315 Cmode *cm; 316 317 for (cm=channelmodes; cm; cm = cm->next) 318 { 319 if (cm->letter == req.letter) 320 { 321 if (cm->unloaded) 322 { 323 cm->unloaded = 0; 324 existing = 1; 325 break; 326 } else { 327 if (module) 328 module->errorcode = MODERR_EXISTS; 329 return NULL; 330 } 331 } 332 } 333 334 if (!cm) 335 { 336 long l, found = 0; 337 338 if (req.type == CMODE_NORMAL) 339 { 340 for (l = 1; l < LONG_MAX/2; l *= 2) 341 { 342 found = 0; 343 for (cm=channelmodes; cm; cm = cm->next) 344 { 345 if (cm->mode == l) 346 { 347 found = 1; 348 break; 349 } 350 } 351 if (!found) 352 break; 353 } 354 /* If 'found' is still true, then we are out of space */ 355 if (found) 356 { 357 unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL, 358 "CmodeAdd: out of space!!!"); 359 if (module) 360 module->errorcode = MODERR_NOSPACE; 361 return NULL; 362 } 363 cm = safe_alloc(sizeof(Cmode)); 364 cm->letter = req.letter; 365 cm->mode = l; 366 *mode = cm->mode; 367 } else if (req.type == CMODE_MEMBER) 368 { 369 if (!req.prefix || !req.sjoin_prefix || !req.paracount || 370 !req.unset_with_param || !req.rank) 371 { 372 unreal_log(ULOG_ERROR, "module", "CMODEADD_API_ERROR", NULL, 373 "CmodeAdd(): module is missing required information. " 374 "Module: $module_name", 375 log_data_string("module_name", module->header->name)); 376 module->errorcode = MODERR_INVALID; 377 return NULL; 378 } 379 cm = safe_alloc(sizeof(Cmode)); 380 cm->letter = req.letter; 381 } else { 382 abort(); 383 } 384 channelmode_add_sorted(cm); 385 } 386 387 if ((req.paracount == 1) && (req.type == CMODE_NORMAL)) 388 { 389 if (existing) 390 { 391 /* Re-use parameter slot of the module with the same modechar that is unloading */ 392 paraslot = cm->param_slot; 393 } 394 else 395 { 396 /* Allocate a new one */ 397 for (paraslot = 0; ParamTable[paraslot]; paraslot++) 398 { 399 if (paraslot == MAXPARAMMODES - 1) 400 { 401 unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL, 402 "CmodeAdd: out of space!!! Place 2."); 403 if (module) 404 module->errorcode = MODERR_NOSPACE; 405 return NULL; 406 } 407 } 408 } 409 } 410 411 cm->letter = req.letter; 412 cm->type = req.type; 413 cm->prefix = req.prefix; 414 cm->sjoin_prefix = req.sjoin_prefix; 415 cm->rank = req.rank; 416 cm->paracount = req.paracount; 417 cm->is_ok = req.is_ok; 418 cm->put_param = req.put_param; 419 cm->get_param = req.get_param; 420 cm->conv_param = req.conv_param; 421 cm->free_param = req.free_param; 422 cm->dup_struct = req.dup_struct; 423 cm->sjoin_check = req.sjoin_check; 424 cm->local = req.local; 425 cm->unset_with_param = req.unset_with_param; 426 cm->flood_type_action = req.flood_type_action; 427 cm->owner = module; 428 cm->unloaded = 0; 429 430 if (cm->type == CMODE_NORMAL) 431 { 432 *mode = cm->mode; 433 if (cm->paracount == 1) 434 extcmode_para_addslot(cm, paraslot); 435 } 436 437 if (module) 438 { 439 ModuleObject *cmodeobj = safe_alloc(sizeof(ModuleObject)); 440 cmodeobj->object.cmode = cm; 441 cmodeobj->type = MOBJ_CMODE; 442 AddListItem(cmodeobj, module->objects); 443 module->errorcode = MODERR_NOERROR; 444 } 445 return cm; 446 } 447 448 /** Delete a previously registered channel mode - not called by modules. 449 * For modules this is done automatically on unload, no need to call this explicitly. 450 */ 451 void CmodeDel(Cmode *cmode) 452 { 453 if (cmode->owner) 454 { 455 ModuleObject *cmodeobj; 456 for (cmodeobj = cmode->owner->objects; cmodeobj; cmodeobj = cmodeobj->next) { 457 if (cmodeobj->type == MOBJ_CMODE && cmodeobj->object.cmode == cmode) { 458 DelListItem(cmodeobj, cmode->owner->objects); 459 safe_free(cmodeobj); 460 break; 461 } 462 } 463 cmode->owner = NULL; 464 } 465 if (loop.rehashing) 466 cmode->unloaded = 1; 467 else 468 unload_extcmode_commit(cmode); 469 470 } 471 472 /** @} */ 473 474 /** After a channel mode is deregistered for sure, unload it completely. 475 * This is done after a REHASH when no new module has registered the mode. 476 * Then we can unload it for good. This also sends MODE -.. out etc. 477 */ 478 static void unload_extcmode_commit(Cmode *cmode) 479 { 480 Channel *channel; 481 482 if (!cmode) 483 return; 484 485 if (cmode->type == CMODE_NORMAL) 486 { 487 /* Unset channel mode and send MODE to everyone */ 488 if (cmode->paracount == 0) 489 { 490 /* Paramless mode, easy */ 491 for (channel = channels; channel; channel = channel->nextch) 492 { 493 if (channel->mode.mode & cmode->mode) 494 { 495 MessageTag *mtags = NULL; 496 497 new_message(&me, NULL, &mtags); 498 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 499 ":%s MODE %s -%c", 500 me.name, channel->name, cmode->letter); 501 sendto_server(NULL, 0, 0, mtags, 502 ":%s MODE %s -%c 0", 503 me.id, channel->name, cmode->letter); 504 free_message_tags(mtags); 505 506 channel->mode.mode &= ~cmode->mode; 507 } 508 } 509 } else 510 { 511 /* Parameter mode, more complicated */ 512 for (channel = channels; channel; channel = channel->nextch) 513 { 514 if (channel->mode.mode & cmode->mode) 515 { 516 MessageTag *mtags = NULL; 517 518 new_message(&me, NULL, &mtags); 519 if (cmode->unset_with_param) 520 { 521 const char *param = cmode->get_param(GETPARASTRUCT(channel, cmode->letter)); 522 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 523 ":%s MODE %s -%c %s", 524 me.name, channel->name, cmode->letter, param); 525 sendto_server(NULL, 0, 0, mtags, 526 ":%s MODE %s -%c %s 0", 527 me.id, channel->name, cmode->letter, param); 528 } else { 529 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 530 ":%s MODE %s -%c", 531 me.name, channel->name, cmode->letter); 532 sendto_server(NULL, 0, 0, mtags, 533 ":%s MODE %s -%c 0", 534 me.id, channel->name, cmode->letter); 535 } 536 free_message_tags(mtags); 537 538 cmode->free_param(GETPARASTRUCT(channel, cmode->letter), 0); 539 channel->mode.mode &= ~cmode->mode; 540 } 541 } 542 extcmode_para_delslot(cmode, cmode->param_slot); 543 } 544 } else 545 if (cmode->type == CMODE_MEMBER) 546 { 547 for (channel = channels; channel; channel = channel->nextch) 548 { 549 Member *m; 550 for (m = channel->members; m; m = m->next) 551 { 552 if (strchr(m->member_modes, cmode->letter)) 553 { 554 MessageTag *mtags = NULL; 555 556 new_message(&me, NULL, &mtags); 557 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 558 ":%s MODE %s -%c %s", 559 me.name, channel->name, cmode->letter, m->client->name); 560 sendto_server(NULL, 0, 0, mtags, 561 ":%s MODE %s -%c %s 0", 562 me.id, channel->name, cmode->letter, m->client->id); 563 free_message_tags(mtags); 564 del_member_mode(m->client, channel, cmode->letter); 565 } 566 } 567 } 568 } 569 570 DelListItem(cmode, channelmodes); 571 safe_free(cmode); 572 } 573 574 /** Unload all unused channel modes after a REHASH */ 575 void unload_all_unused_extcmodes(void) 576 { 577 Cmode *cm, *cm_next; 578 579 for (cm=channelmodes; cm; cm = cm_next) 580 { 581 cm_next = cm->next; 582 if (cm->letter && cm->unloaded) 583 { 584 unload_extcmode_commit(cm); 585 } 586 } 587 588 } 589 590 /** @defgroup ChannelModeAPI Channel mode API 591 * @{ 592 */ 593 594 /** Get parameter for a channel mode. 595 * @param channel The channel 596 * @param mode The mode character (eg: 'f') 597 */ 598 const char *cm_getparameter(Channel *channel, char mode) 599 { 600 return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCT(channel, mode)); 601 } 602 603 /** Get parameter for a channel mode - special version for SJOIN. 604 * This version doesn't take a channel, but a mode.mode_params. 605 * It is only used by SJOIN and should not be used in 3rd party modules. 606 * @param p The list, eg oldmode.mode_params 607 * @param mode The mode letter 608 */ 609 const char *cm_getparameter_ex(void **p, char mode) 610 { 611 return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCTEX(p, mode)); 612 } 613 614 /** Set parameter for a channel mode. 615 * @param channel The channel 616 * @param mode The mode character (eg: 'f') 617 * @param str The parameter string 618 * @note Module users should not use this function directly, it is only used by MODE and SJOIN. 619 */ 620 void cm_putparameter(Channel *channel, char mode, const char *str) 621 { 622 GETPARASTRUCT(channel, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCT(channel, mode), str); 623 } 624 625 /** Free a channel mode parameter. 626 * @param channel The channel 627 * @param mode The mode character (eg: 'f') 628 */ 629 void cm_freeparameter(Channel *channel, char mode) 630 { 631 int n = GETPARAMHANDLERBYLETTER(mode)->free_param(GETPARASTRUCT(channel, mode), 1); 632 if (n == 0) 633 GETPARASTRUCT(channel, mode) = NULL; 634 } 635 636 637 /** Set parameter for a channel mode - special version for SJOIN. 638 * This version doesn't take a channel, but a mode.mode_params. 639 * It is only used by SJOIN and should not be used in 3rd party modules. 640 * @param p The list, eg oldmode.mode_params 641 * @param mode The mode letter 642 * @param str The mode parameter string to set 643 */ 644 void cm_putparameter_ex(void **p, char mode, const char *str) 645 { 646 GETPARASTRUCTEX(p, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCTEX(p, mode), str); 647 } 648 649 /** Default handler for - require channel operator or higher (+o/+a/+q) 650 * @param client The client issueing the MODE 651 * @param channel The channel 652 * @param mode The mode letter (eg: 'f') 653 * @param para The parameter, if any (can be NULL) 654 * @param checkt The check type, one of ..... 655 * @param what MODE_ADD / MODE_DEL (???) 656 * @returns EX_ALLOW or EX_DENY 657 */ 658 int extcmode_default_requirechop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) 659 { 660 if (IsUser(client) && check_channel_access(client, channel, "oaq")) 661 return EX_ALLOW; 662 if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ 663 sendnumeric(client, ERR_NOTFORHALFOPS, mode); 664 return EX_DENY; 665 } 666 667 /** Default handler for - require halfop or higher (+h/+o/+a/+q) 668 * @param client The client issueing the MODE 669 * @param channel The channel 670 * @param mode The mode letter (eg: 'f') 671 * @param para The parameter, if any (can be NULL) 672 * @param checkt The check type, one of ..... 673 * @param what MODE_ADD / MODE_DEL (???) 674 * @returns EX_ALLOW or EX_DENY 675 */ 676 int extcmode_default_requirehalfop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) 677 { 678 if (IsUser(client) && (check_channel_access(client, channel, "oaq") || check_channel_access(client, channel, "h"))) 679 return EX_ALLOW; 680 return EX_DENY; 681 } 682 683 /** Duplicate all channel mode parameters - only used by SJOIN. 684 * @param xi Input list 685 * @param xi Output list 686 */ 687 void extcmode_duplicate_paramlist(void **xi, void **xo) 688 { 689 int i; 690 Cmode *handler; 691 void *inx; 692 693 for (i = 0; i < MAXPARAMMODES; i++) 694 { 695 handler = CMP_GETHANDLERBYSLOT(i); 696 if (!handler) 697 continue; /* nothing there.. */ 698 inx = xi[handler->param_slot]; /* paramter data of input is here */ 699 if (!inx) 700 continue; /* not set */ 701 xo[handler->param_slot] = handler->dup_struct(inx); /* call dup_struct with that input and set the output param to that */ 702 } 703 } 704 705 /** Free all channel mode parameters - only used by SJOIN. 706 * @param ar The list 707 */ 708 void extcmode_free_paramlist(void **ar) 709 { 710 int i; 711 Cmode *handler; 712 713 for (i = 0; i < MAXPARAMMODES; i++) 714 { 715 handler = GETPARAMHANDLERBYSLOT(i); 716 if (!handler) 717 continue; /* nothing here... */ 718 handler->free_param(ar[handler->param_slot], 0); 719 ar[handler->param_slot] = NULL; 720 } 721 } 722 723 /** @} */ 724 725 /** Internal function: returns 1 if the specified module has 1 or more extended channel modes registered */ 726 int module_has_extcmode_param_mode(Module *mod) 727 { 728 Cmode *cm; 729 730 for (cm=channelmodes; cm; cm = cm->next) 731 if ((cm->letter) && (cm->owner == mod) && (cm->paracount)) 732 return 1; 733 734 return 0; 735 } 736 737 /** Channel member privileges - getting, setting, checking vhoaq status, etc. 738 * These functions get or set the access rights of channel members, such as +vhoaq. 739 * They can also convert between modes (vhoaq), prefixes and sjoin prefixes. 740 * @defgroup ChannelMember Channel members access privileges 741 * @{ 742 */ 743 744 /** Retrieve channel access for a user on a channel, returns modes eg "qao". 745 * @param client The client 746 * @param channel The channel 747 * @returns The modes, sorted by high ranking to lower ranking, eg "qao". 748 * An empty string ("") is returned when not in the channel or no modes. 749 */ 750 const char *get_channel_access(Client *client, Channel *channel) 751 { 752 Membership *mb; 753 754 mb = find_membership_link(client->user->channel, channel); 755 if (!mb) 756 return ""; 757 return mb->member_modes; 758 } 759 760 /** Check channel access for user. 761 * @param client The client to check 762 * @param channel The channel to check 763 * @param modes Which mode(s) to check for 764 * @returns If the client in channel has any of the modes set, 1 is returned. 765 * Otherwise 0 is returned, which is also the case if the user is 766 * not a user or is not in the channel at all. 767 */ 768 int check_channel_access(Client *client, Channel *channel, const char *modes) 769 { 770 Membership *mb; 771 const char *p; 772 773 if (!IsUser(client)) 774 return 0; /* eg server */ 775 776 mb = find_membership_link(client->user->channel, channel); 777 if (!mb) 778 return 0; /* not a member */ 779 780 for (p = mb->member_modes; *p; p++) 781 if (strchr(modes, *p)) 782 return 1; /* match new style */ 783 784 return 0; /* nomatch */ 785 } 786 787 /** Check channel access for user. 788 * @param client The client to check 789 * @param channel The channel to check 790 * @param modes Which mode(s) to check for 791 * @returns If the client in channel has any of the modes set, 1 is returned. 792 * Otherwise 0 is returned, which is also the case if the user is 793 * not a user or is not in the channel at all. 794 */ 795 int check_channel_access_membership(Membership *mb, const char *modes) 796 { 797 const char *p; 798 799 if (!mb) 800 return 0; 801 802 for (p = mb->member_modes; *p; p++) 803 if (strchr(modes, *p)) 804 return 1; /* match new style */ 805 806 return 0; /* nomatch */ 807 } 808 809 /** Check channel access for user. 810 * @param client The client to check 811 * @param channel The channel to check 812 * @param modes Which mode(s) to check for 813 * @returns If the client in channel has any of the modes set, 1 is returned. 814 * Otherwise 0 is returned, which is also the case if the user is 815 * not a user or is not in the channel at all. 816 */ 817 int check_channel_access_member(Member *mb, const char *modes) 818 { 819 const char *p; 820 821 if (!mb) 822 return 0; 823 824 for (p = mb->member_modes; *p; p++) 825 if (strchr(modes, *p)) 826 return 1; /* match new style */ 827 828 return 0; /* nomatch */ 829 } 830 831 /** Check channel access for user. 832 * @param current Flags currently set on the client (eg mb->member_modes) 833 * @param modes Which mode(s) to check for 834 * @returns If the client in channel has any of the modes set, 1 is returned. 835 * Otherwise 0 is returned. 836 */ 837 int check_channel_access_string(const char *current_modes, const char *modes) 838 { 839 const char *p; 840 841 for (p = current_modes; *p; p++) 842 if (strchr(modes, *p)) 843 return 1; 844 845 return 0; /* nomatch */ 846 } 847 848 /** Check channel access for user. 849 * @param current Flags currently set on the client (eg mb->member_modes) 850 * @param letter Which mode letter to check for 851 * @returns If the client in channel has any of the modes set, 1 is returned. 852 * Otherwise 0 is returned. 853 */ 854 int check_channel_access_letter(const char *current_modes, const char letter) 855 { 856 return strchr(current_modes, letter) ? 1 : 0; 857 } 858 859 Cmode *find_channel_mode_handler(char letter) 860 { 861 Cmode *cm; 862 863 for (cm=channelmodes; cm; cm = cm->next) 864 if (cm->letter == letter) 865 return cm; 866 return NULL; 867 } 868 869 /** Is 'letter' a valid mode used for access/levels/ranks? (vhoaq and such) 870 * @param letter The channel mode letter to check, eg 'v' 871 * @returns 1 if valid, 0 if the channel mode does not exist or is not a level mode. 872 */ 873 int valid_channel_access_mode_letter(char letter) 874 { 875 Cmode *cm; 876 877 if ((cm = find_channel_mode_handler(letter)) && (cm->type == CMODE_MEMBER)) 878 return 1; 879 880 return 0; 881 } 882 883 void addlettertomstring(char *str, char letter) 884 { 885 Cmode *cm; 886 int n; 887 int my_rank; 888 char *p; 889 890 if (!(cm = find_channel_mode_handler(letter)) || (cm->type != CMODE_MEMBER)) 891 return; // should we BUG on this? if something makes it this far, it can never be good right? 892 893 my_rank = cm->rank; 894 895 n = strlen(str); 896 if (n >= MEMBERMODESLEN-1) 897 return; // panic! 898 899 for (p = str; *p; p++) 900 { 901 cm = find_channel_mode_handler(*p); 902 if (!cm) 903 continue; /* wtf */ 904 if (cm->rank < my_rank) 905 { 906 /* We need to insert us here */ 907 n = strlen(p); 908 memmove(p+1, p, n+1); // +1 for NUL byte 909 *p = letter; 910 return; 911 } 912 } 913 /* We should be at the end */ 914 str[n] = letter; 915 str[n+1] = '\0'; 916 } 917 918 void add_member_mode_fast(Member *mb, Membership *mbs, char letter) 919 { 920 addlettertomstring(mb->member_modes, letter); 921 addlettertomstring(mbs->member_modes, letter); 922 } 923 924 void del_member_mode_fast(Member *mb, Membership *mbs, char letter) 925 { 926 delletterfromstring(mb->member_modes, letter); 927 delletterfromstring(mbs->member_modes, letter); 928 } 929 930 int find_mbs(Client *client, Channel *channel, Member **mb, Membership **mbs) 931 { 932 *mbs = NULL; 933 934 if (!(*mb = find_member_link(channel->members, client))) 935 return 0; 936 937 if (!(*mbs = find_membership_link(client->user->channel, channel))) 938 return 0; 939 940 return 1; 941 } 942 943 void add_member_mode(Client *client, Channel *channel, char letter) 944 { 945 Member *mb; 946 Membership *mbs; 947 948 if (!find_mbs(client, channel, &mb, &mbs)) 949 return; 950 951 add_member_mode_fast(mb, mbs, letter); 952 } 953 954 void del_member_mode(Client *client, Channel *channel, char letter) 955 { 956 Member *mb; 957 Membership *mbs; 958 959 if (!find_mbs(client, channel, &mb, &mbs)) 960 return; 961 962 del_member_mode_fast(mb, mbs, letter); 963 } 964 965 char sjoin_prefix_to_mode(char s) 966 { 967 Cmode *cm; 968 969 /* Filter this out early to avoid spurious results */ 970 if (s == '\0') 971 return '\0'; 972 973 /* First the hardcoded list modes: */ 974 if (s == '&') 975 return 'b'; 976 if (s == '"') 977 return 'e'; 978 if (s == '\'') 979 return 'I'; 980 981 /* Now the dynamic ones (+vhoaq): */ 982 for (cm=channelmodes; cm; cm = cm->next) 983 if ((cm->sjoin_prefix == s) && (cm->type == CMODE_MEMBER)) 984 return cm->letter; 985 986 /* Not found */ 987 return '\0'; 988 } 989 990 char mode_to_sjoin_prefix(char s) 991 { 992 Cmode *cm; 993 994 /* Filter this out early to avoid spurious results */ 995 if (s == '\0') 996 return '\0'; 997 998 /* First the hardcoded list modes: */ 999 if (s == 'b') 1000 return '&'; 1001 if (s == 'e') 1002 return '"'; 1003 if (s == 'I') 1004 return '\''; 1005 1006 /* Now the dynamic ones (+vhoaq): */ 1007 for (cm=channelmodes; cm; cm = cm->next) 1008 if ((cm->letter == s) && (cm->type == CMODE_MEMBER)) 1009 return cm->sjoin_prefix; 1010 1011 /* Not found */ 1012 return '\0'; 1013 } 1014 1015 const char *modes_to_sjoin_prefix(const char *modes) 1016 { 1017 static char buf[MEMBERMODESLEN]; 1018 const char *m; 1019 char f; 1020 1021 *buf = '\0'; 1022 for (m = modes; *m; m++) 1023 { 1024 f = mode_to_sjoin_prefix(*m); 1025 if (f) 1026 strlcat_letter(buf, f, sizeof(buf)); 1027 } 1028 1029 return buf; 1030 } 1031 1032 char mode_to_prefix(char s) 1033 { 1034 Cmode *cm; 1035 1036 /* Filter this out early to avoid spurious results */ 1037 if (s == '\0') 1038 return '\0'; 1039 1040 /* Now the dynamic ones (+vhoaq): */ 1041 for (cm=channelmodes; cm; cm = cm->next) 1042 if ((cm->letter == s) && (cm->type == CMODE_MEMBER)) 1043 return cm->prefix; 1044 1045 /* Not found */ 1046 return '\0'; 1047 } 1048 1049 const char *modes_to_prefix(const char *modes) 1050 { 1051 static char buf[MEMBERMODESLEN]; 1052 const char *m; 1053 char f; 1054 1055 *buf = '\0'; 1056 for (m = modes; *m; m++) 1057 { 1058 f = mode_to_prefix(*m); 1059 if (f) 1060 strlcat_letter(buf, f, sizeof(buf)); 1061 } 1062 1063 return buf; 1064 } 1065 1066 char prefix_to_mode(char s) 1067 { 1068 Cmode *cm; 1069 1070 /* Filter this out early to avoid spurious results */ 1071 if (s == '\0') 1072 return '\0'; 1073 1074 /* Now the dynamic ones (+vhoaq): */ 1075 for (cm=channelmodes; cm; cm = cm->next) 1076 if ((cm->prefix == s) && (cm->type == CMODE_MEMBER)) 1077 return cm->letter; 1078 1079 /* Not found */ 1080 return '\0'; 1081 } 1082 1083 char rank_to_mode(int rank) 1084 { 1085 Cmode *cm; 1086 for (cm=channelmodes; cm; cm = cm->next) 1087 if ((cm->type == CMODE_MEMBER) && (cm->rank == rank)) 1088 return cm->letter; 1089 return '\0'; 1090 } 1091 1092 int mode_to_rank(char mode) 1093 { 1094 Cmode *cm; 1095 for (cm=channelmodes; cm; cm = cm->next) 1096 if ((cm->type == CMODE_MEMBER) && (cm->letter == mode)) 1097 return cm->rank; 1098 return '\0'; 1099 } 1100 1101 int prefix_to_rank(char prefix) 1102 { 1103 Cmode *cm; 1104 for (cm=channelmodes; cm; cm = cm->next) 1105 if ((cm->type == CMODE_MEMBER) && (cm->prefix == prefix)) 1106 return cm->rank; 1107 return '\0'; 1108 } 1109 1110 char rank_to_prefix(int rank) 1111 { 1112 Cmode *cm; 1113 for (cm=channelmodes; cm; cm = cm->next) 1114 if ((cm->type == CMODE_MEMBER) && (cm->rank == rank)) 1115 return cm->prefix; 1116 return '\0'; 1117 } 1118 1119 char lowest_ranking_prefix(const char *prefix) 1120 { 1121 const char *p; 1122 int winning_rank = INT_MAX; 1123 1124 for (p = prefix; *p; p++) 1125 { 1126 int rank = prefix_to_rank(*p); 1127 if (rank < winning_rank) 1128 winning_rank = rank; 1129 } 1130 if (winning_rank == INT_MAX) 1131 return '\0'; /* No result */ 1132 return rank_to_prefix(winning_rank); 1133 } 1134 1135 char lowest_ranking_mode(const char *mode) 1136 { 1137 const char *p; 1138 int winning_rank = INT_MAX; 1139 1140 for (p = mode; *p; p++) 1141 { 1142 int rank = mode_to_rank(*p); 1143 if (rank < winning_rank) 1144 winning_rank = rank; 1145 } 1146 if (winning_rank == INT_MAX) 1147 return '\0'; /* No result */ 1148 return rank_to_mode(winning_rank); 1149 } 1150 1151 /** Generate all member modes that are equal or greater than 'modes'. 1152 * Eg calling this with "o" would generate "oaq" with the default loaded modules. 1153 * This is used in sendto_channel() to make multiple check_channel_access_member() 1154 * calls more easy / faster. 1155 */ 1156 void channel_member_modes_generate_equal_or_greater(const char *modes, char *buf, size_t buflen) 1157 { 1158 const char *p; 1159 int rank; 1160 Cmode *cm; 1161 1162 *buf = '\0'; 1163 1164 /* First we must grab the lowest ranking mode, eg 'vhoaq' results in rank for 'v' */ 1165 rank = lowest_ranking_mode(modes); 1166 if (!rank) 1167 return; /* zero matches */ 1168 1169 for (cm=channelmodes; cm; cm = cm->next) 1170 if ((cm->type == CMODE_MEMBER) && (cm->rank >= rank)) 1171 strlcat_letter(buf, cm->letter, buflen); 1172 } 1173 1174 /** @} */