Annotation of ircnowd/src/ngircd/irc-mode.c, Revision 1.1
1.1 ! tomglok 1: /*
! 2: * ngIRCd -- The Next Generation IRC Daemon
! 3: * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify
! 6: * it under the terms of the GNU General Public License as published by
! 7: * the Free Software Foundation; either version 2 of the License, or
! 8: * (at your option) any later version.
! 9: * Please read the file COPYING, README and AUTHORS for more information.
! 10: */
! 11:
! 12: #include "portab.h"
! 13:
! 14: /**
! 15: * @file
! 16: * IRC commands for mode changes (like MODE, AWAY, etc.)
! 17: */
! 18:
! 19: #include <assert.h>
! 20: #include <stdio.h>
! 21: #include <stdlib.h>
! 22: #include <string.h>
! 23:
! 24: #include "conn.h"
! 25: #include "channel.h"
! 26: #include "irc-macros.h"
! 27: #include "irc-write.h"
! 28: #include "lists.h"
! 29: #include "log.h"
! 30: #include "parse.h"
! 31: #include "messages.h"
! 32: #include "conf.h"
! 33:
! 34: #include "irc-mode.h"
! 35:
! 36: static bool Client_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
! 37: CLIENT *Target));
! 38: static bool Channel_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
! 39: CHANNEL *Channel));
! 40:
! 41: static bool Add_To_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
! 42: CHANNEL *Channel, const char *Pattern));
! 43: static bool Del_From_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
! 44: CHANNEL *Channel, const char *Pattern));
! 45:
! 46: static bool Send_ListChange PARAMS((const bool IsAdd, const char ModeChar,
! 47: CLIENT *Prefix, CLIENT *Client,
! 48: CHANNEL *Channel, const char *Mask));
! 49:
! 50: /**
! 51: * Handler for the IRC "MODE" command.
! 52: *
! 53: * This function detects whether user or channel modes should be modified
! 54: * and calls the appropriate sub-functions.
! 55: *
! 56: * @param Client The client from which this command has been received.
! 57: * @param Req Request structure with prefix and all parameters.
! 58: * @return CONNECTED or DISCONNECTED.
! 59: */
! 60: GLOBAL bool
! 61: IRC_MODE( CLIENT *Client, REQUEST *Req )
! 62: {
! 63: CLIENT *cl, *origin;
! 64: CHANNEL *chan;
! 65:
! 66: assert(Client != NULL);
! 67: assert(Req != NULL);
! 68:
! 69: _IRC_GET_SENDER_OR_RETURN_(origin, Req, Client)
! 70:
! 71: /* Test for "fake" MODE commands injected by this local instance,
! 72: * for example when handling the "DefaultUserModes" settings.
! 73: * This doesn't harm real commands, because prefixes of regular
! 74: * clients are checked in Validate_Prefix() and can't be faked. */
! 75: if (Req->prefix && Client_Search(Req->prefix) == Client_ThisServer())
! 76: Client = Client_Search(Req->prefix);
! 77:
! 78: /* Channel or user mode? */
! 79: cl = NULL; chan = NULL;
! 80: if (Client_IsValidNick(Req->argv[0]))
! 81: cl = Client_Search(Req->argv[0]);
! 82: if (Channel_IsValidName(Req->argv[0]))
! 83: chan = Channel_Search(Req->argv[0]);
! 84:
! 85: if (cl)
! 86: return Client_Mode(Client, Req, origin, cl);
! 87: if (chan)
! 88: return Channel_Mode(Client, Req, origin, chan);
! 89:
! 90: /* No target found! */
! 91: return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
! 92: Client_ID(Client), Req->argv[0]);
! 93: } /* IRC_MODE */
! 94:
! 95: /**
! 96: * Check if the "mode limit" for a client has been reached.
! 97: *
! 98: * This limit doesn't apply for servers or services!
! 99: *
! 100: * @param Client The client to check.
! 101: * @param Count The number of modes already handled.
! 102: * @return true if the limit has been reached.
! 103: */
! 104: static bool
! 105: Mode_Limit_Reached(CLIENT *Client, int Count)
! 106: {
! 107: if (Client_Type(Client) == CLIENT_SERVER
! 108: || Client_Type(Client) == CLIENT_SERVICE)
! 109: return false;
! 110: if (Count < MAX_HNDL_MODES_ARG)
! 111: return false;
! 112: return true;
! 113: }
! 114:
! 115: /**
! 116: * Handle client mode requests
! 117: *
! 118: * @param Client The client from which this command has been received.
! 119: * @param Req Request structure with prefix and all parameters.
! 120: * @param Origin The originator of the MODE command (prefix).
! 121: * @param Target The target (client) of this MODE command.
! 122: * @return CONNECTED or DISCONNECTED.
! 123: */
! 124: static bool
! 125: Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
! 126: {
! 127: char the_modes[COMMAND_LEN], x[2], *mode_ptr;
! 128: bool ok, set;
! 129: bool send_RPL_HOSTHIDDEN_MSG = false;
! 130: int mode_arg;
! 131: size_t len;
! 132:
! 133: /* Is the client allowed to request or change the modes? */
! 134: if (Client_Type(Client) == CLIENT_USER) {
! 135: /* Users are only allowed to manipulate their own modes! */
! 136: if (Target != Client)
! 137: return IRC_WriteErrClient(Client,
! 138: ERR_USERSDONTMATCH_MSG,
! 139: Client_ID(Client));
! 140: }
! 141:
! 142: /* Mode request: let's answer it :-) */
! 143: if (Req->argc == 1)
! 144: return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
! 145: Client_ID(Target),
! 146: Client_Modes(Target));
! 147:
! 148: mode_arg = 1;
! 149: mode_ptr = Req->argv[mode_arg];
! 150:
! 151: /* Initial state: set or unset modes? */
! 152: if (*mode_ptr == '+') {
! 153: set = true;
! 154: strcpy(the_modes, "+");
! 155: } else if (*mode_ptr == '-') {
! 156: set = false;
! 157: strcpy(the_modes, "-");
! 158: } else
! 159: return IRC_WriteErrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
! 160: Client_ID(Origin));
! 161:
! 162: x[1] = '\0';
! 163: ok = CONNECTED;
! 164: while (mode_ptr) {
! 165: mode_ptr++;
! 166: if (!*mode_ptr) {
! 167: /* Try next argument if there's any */
! 168: mode_arg++;
! 169: if (mode_arg < Req->argc)
! 170: mode_ptr = Req->argv[mode_arg];
! 171: else
! 172: break;
! 173: }
! 174:
! 175: switch(*mode_ptr) {
! 176: case '+':
! 177: case '-':
! 178: if ((*mode_ptr == '+' && !set)
! 179: || (*mode_ptr == '-' && set)) {
! 180: /* Action modifier ("+"/"-") must be changed */
! 181: len = strlen(the_modes) - 1;
! 182: if (the_modes[len] == '+'
! 183: || the_modes[len] == '-') {
! 184: /* Last character in the "result
! 185: * string" was an "action", so just
! 186: * overwrite it with the new action */
! 187: the_modes[len] = *mode_ptr;
! 188: } else {
! 189: /* Append new modifier character to
! 190: * the resulting mode string */
! 191: x[0] = *mode_ptr;
! 192: strlcat(the_modes, x,
! 193: sizeof(the_modes));
! 194: }
! 195: if (*mode_ptr == '+')
! 196: set = true;
! 197: else
! 198: set = false;
! 199: }
! 200: continue;
! 201: }
! 202:
! 203: /* Validate modes */
! 204: x[0] = '\0';
! 205: switch (*mode_ptr) {
! 206: case 'b': /* Block private msgs */
! 207: case 'C': /* Only messages from clients sharing a channel */
! 208: case 'i': /* Invisible */
! 209: case 'I': /* Hide channel list from WHOIS */
! 210: case 's': /* Server messages */
! 211: case 'w': /* Wallops messages */
! 212: x[0] = *mode_ptr;
! 213: break;
! 214: case 'a': /* Away */
! 215: if (Client_Type(Client) == CLIENT_SERVER) {
! 216: x[0] = 'a';
! 217: Client_SetAway(Origin, DEFAULT_AWAY_MSG);
! 218: } else
! 219: ok = IRC_WriteErrClient(Origin,
! 220: ERR_NOPRIVILEGES_MSG,
! 221: Client_ID(Origin));
! 222: break;
! 223: case 'B': /* Bot */
! 224: if (Client_HasMode(Client, 'r'))
! 225: ok = IRC_WriteErrClient(Origin,
! 226: ERR_RESTRICTED_MSG,
! 227: Client_ID(Origin));
! 228: else
! 229: x[0] = 'B';
! 230: break;
! 231: case 'c': /* Receive connect notices */
! 232: case 'q': /* KICK-protected user */
! 233: case 'F': /* disable flood protection */
! 234: /* (only settable by IRC operators!) */
! 235: if (!set || Client_Type(Client) == CLIENT_SERVER
! 236: || Client_HasMode(Origin, 'o'))
! 237: x[0] = *mode_ptr;
! 238: else
! 239: ok = IRC_WriteErrClient(Origin,
! 240: ERR_NOPRIVILEGES_MSG,
! 241: Client_ID(Origin));
! 242: break;
! 243: case 'o': /* IRC operator (only unsettable!) */
! 244: if (!set || Client_Type(Client) == CLIENT_SERVER) {
! 245: x[0] = 'o';
! 246: } else
! 247: ok = IRC_WriteErrClient(Origin,
! 248: ERR_NOPRIVILEGES_MSG,
! 249: Client_ID(Origin));
! 250: break;
! 251: case 'r': /* Restricted (only settable) */
! 252: if (set || Client_Type(Client) == CLIENT_SERVER)
! 253: x[0] = 'r';
! 254: else
! 255: ok = IRC_WriteErrClient(Origin,
! 256: ERR_RESTRICTED_MSG,
! 257: Client_ID(Origin));
! 258: break;
! 259: case 'R': /* Registered (not [un]settable by clients) */
! 260: if (Client_Type(Client) == CLIENT_SERVER)
! 261: x[0] = 'R';
! 262: else
! 263: ok = IRC_WriteErrClient(Origin,
! 264: ERR_NICKREGISTER_MSG,
! 265: Client_ID(Origin));
! 266: break;
! 267: case 'x': /* Cloak hostname */
! 268: if (Client_HasMode(Client, 'r'))
! 269: ok = IRC_WriteErrClient(Origin,
! 270: ERR_RESTRICTED_MSG,
! 271: Client_ID(Origin));
! 272: else if (!set || Conf_CloakHostModeX[0]
! 273: || Client_Type(Client) == CLIENT_SERVER
! 274: || Client_HasMode(Origin, 'o')) {
! 275: x[0] = 'x';
! 276: send_RPL_HOSTHIDDEN_MSG = true;
! 277: } else
! 278: ok = IRC_WriteErrClient(Origin,
! 279: ERR_NOPRIVILEGES_MSG,
! 280: Client_ID(Origin));
! 281: break;
! 282: default:
! 283: if (Client_Type(Client) != CLIENT_SERVER) {
! 284: Log(LOG_DEBUG,
! 285: "Unknown mode \"%c%c\" from \"%s\"!?",
! 286: set ? '+' : '-', *mode_ptr,
! 287: Client_ID(Origin));
! 288: ok = IRC_WriteErrClient(Origin,
! 289: ERR_UMODEUNKNOWNFLAG2_MSG,
! 290: Client_ID(Origin),
! 291: set ? '+' : '-',
! 292: *mode_ptr);
! 293: x[0] = '\0';
! 294: } else {
! 295: Log(LOG_DEBUG,
! 296: "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
! 297: set ? '+' : '-', *mode_ptr,
! 298: Client_ID(Origin), Client_ID(Target));
! 299: x[0] = *mode_ptr;
! 300: }
! 301: }
! 302:
! 303: if (!ok)
! 304: break;
! 305:
! 306: /* Is there a valid mode change? */
! 307: if (!x[0])
! 308: continue;
! 309:
! 310: if (set) {
! 311: if (Client_ModeAdd(Target, x[0]))
! 312: strlcat(the_modes, x, sizeof(the_modes));
! 313: } else {
! 314: if (Client_ModeDel(Target, x[0]))
! 315: strlcat(the_modes, x, sizeof(the_modes));
! 316: }
! 317: }
! 318:
! 319: /* Are there changed modes? */
! 320: if (the_modes[1]) {
! 321: /* Remove needless action modifier characters */
! 322: len = strlen(the_modes) - 1;
! 323: if (the_modes[len] == '+' || the_modes[len] == '-')
! 324: the_modes[len] = '\0';
! 325:
! 326: if (Client_Type(Client) == CLIENT_SERVER) {
! 327: /* Forward modes to other servers */
! 328: if (Client_Conn(Target) != NONE) {
! 329: /* Remote server (service?) changed modes
! 330: * for one of our clients. Inform it! */
! 331: IRC_WriteStrClientPrefix(Target, Origin,
! 332: "MODE %s :%s",
! 333: Client_ID(Target),
! 334: the_modes);
! 335: }
! 336: IRC_WriteStrServersPrefix(Client, Origin,
! 337: "MODE %s :%s",
! 338: Client_ID(Target),
! 339: the_modes);
! 340: } else {
! 341: /* Send reply to client and inform other servers */
! 342: ok = IRC_WriteStrClientPrefix(Client, Origin,
! 343: "MODE %s :%s",
! 344: Client_ID(Target),
! 345: the_modes);
! 346: IRC_WriteStrServersPrefix(Client, Origin,
! 347: "MODE %s :%s",
! 348: Client_ID(Target),
! 349: the_modes);
! 350: }
! 351:
! 352: if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
! 353: /* A new (cloaked) hostname must be announced */
! 354: IRC_WriteStrClientPrefix(Target, Origin,
! 355: RPL_HOSTHIDDEN_MSG,
! 356: Client_ID(Target),
! 357: Client_HostnameDisplayed(Target));
! 358:
! 359: }
! 360:
! 361: LogDebug("%s \"%s\": Mode change, now \"%s\".",
! 362: Client_TypeText(Target), Client_Mask(Target),
! 363: Client_Modes(Target));
! 364: }
! 365:
! 366: return ok;
! 367: } /* Client_Mode */
! 368:
! 369: /*
! 370: * Reply to a channel mode request.
! 371: *
! 372: * @param Origin The originator of the MODE command (prefix).
! 373: * @param Channel The channel of which the modes should be sent.
! 374: * @return CONNECTED or DISCONNECTED.
! 375: */
! 376: static bool
! 377: Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
! 378: {
! 379: char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
! 380: const char *mode_ptr;
! 381:
! 382: if (!Channel_IsMemberOf(Channel, Origin)) {
! 383: /* Not a member: "simple" mode reply */
! 384: if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
! 385: Client_ID(Origin), Channel_Name(Channel),
! 386: Channel_Modes(Channel)))
! 387: return DISCONNECTED;
! 388: } else {
! 389: /* The sender is a member: generate extended reply */
! 390: strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
! 391: mode_ptr = the_modes;
! 392: the_args[0] = '\0';
! 393:
! 394: while(*mode_ptr) {
! 395: switch(*mode_ptr) {
! 396: case 'l':
! 397: snprintf(argadd, sizeof(argadd), " %lu",
! 398: Channel_MaxUsers(Channel));
! 399: strlcat(the_args, argadd, sizeof(the_args));
! 400: break;
! 401: case 'k':
! 402: strlcat(the_args, " ", sizeof(the_args));
! 403: strlcat(the_args, Channel_Key(Channel),
! 404: sizeof(the_args));
! 405: break;
! 406: }
! 407: mode_ptr++;
! 408: }
! 409: if (the_args[0])
! 410: strlcat(the_modes, the_args, sizeof(the_modes));
! 411:
! 412: if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
! 413: Client_ID(Origin), Channel_Name(Channel),
! 414: the_modes))
! 415: return DISCONNECTED;
! 416: }
! 417:
! 418: #ifndef STRICT_RFC
! 419: /* Channel creation time */
! 420: if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
! 421: Client_ID(Origin), Channel_Name(Channel),
! 422: Channel_CreationTime(Channel)))
! 423: return DISCONNECTED;
! 424: #endif
! 425: return CONNECTED;
! 426: }
! 427:
! 428: /**
! 429: * Handle channel mode and channel-user mode changes
! 430: *
! 431: * @param Client The client from which this command has been received.
! 432: * @param Req Request structure with prefix and all parameters.
! 433: * @param Origin The originator of the MODE command (prefix).
! 434: * @param Channel The target channel of this MODE command.
! 435: * @return CONNECTED or DISCONNECTED.
! 436: */
! 437: static bool
! 438: Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
! 439: {
! 440: char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
! 441: argadd[CLIENT_PASS_LEN], *mode_ptr;
! 442: bool connected, set, skiponce, retval, use_servermode,
! 443: is_halfop, is_op, is_admin, is_owner, is_machine, is_oper;
! 444: int mode_arg, arg_arg, mode_arg_count = 0;
! 445: CLIENT *client;
! 446: long l;
! 447: size_t len;
! 448:
! 449: is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false;
! 450:
! 451: if (Channel_IsModeless(Channel))
! 452: return IRC_WriteErrClient(Client, ERR_NOCHANMODES_MSG,
! 453: Client_ID(Client), Channel_Name(Channel));
! 454:
! 455: /* Mode request: let's answer it :-) */
! 456: if (Req->argc <= 1)
! 457: return Channel_Mode_Answer_Request(Origin, Channel);
! 458:
! 459: /* Check if origin is oper and opers can use mode */
! 460: use_servermode = Conf_OperServerMode;
! 461: if(Client_HasMode(Client, 'o') && Conf_OperCanMode) {
! 462: is_oper = true;
! 463: }
! 464:
! 465: /* Check if client is a server/service */
! 466: if(Client_Type(Client) == CLIENT_SERVER ||
! 467: Client_Type(Client) == CLIENT_SERVICE) {
! 468: is_machine = true;
! 469: }
! 470:
! 471: /* Check if client is member of channel or an oper or an server/service */
! 472: if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine)
! 473: return IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG,
! 474: Client_ID(Origin),
! 475: Channel_Name(Channel));
! 476:
! 477: mode_arg = 1;
! 478: mode_ptr = Req->argv[mode_arg];
! 479: if (Req->argc > mode_arg + 1)
! 480: arg_arg = mode_arg + 1;
! 481: else
! 482: arg_arg = -1;
! 483:
! 484: /* Initial state: set or unset modes? */
! 485: skiponce = false;
! 486: switch (*mode_ptr) {
! 487: case '-':
! 488: set = false;
! 489: break;
! 490: case '+':
! 491: set = true;
! 492: break;
! 493: default:
! 494: set = true;
! 495: skiponce = true;
! 496: }
! 497:
! 498: /* Prepare reply string */
! 499: strcpy(the_modes, set ? "+" : "-");
! 500: the_args[0] = '\0';
! 501:
! 502: x[1] = '\0';
! 503: connected = CONNECTED;
! 504: while (mode_ptr) {
! 505: if (!skiponce)
! 506: mode_ptr++;
! 507: if (!*mode_ptr) {
! 508: /* Try next argument if there's any */
! 509: if (arg_arg < 0)
! 510: break;
! 511: if (arg_arg > mode_arg)
! 512: mode_arg = arg_arg;
! 513: else
! 514: mode_arg++;
! 515:
! 516: if (mode_arg >= Req->argc)
! 517: break;
! 518: mode_ptr = Req->argv[mode_arg];
! 519:
! 520: if (Req->argc > mode_arg + 1)
! 521: arg_arg = mode_arg + 1;
! 522: else
! 523: arg_arg = -1;
! 524: }
! 525: skiponce = false;
! 526:
! 527: switch (*mode_ptr) {
! 528: case '+':
! 529: case '-':
! 530: if (((*mode_ptr == '+') && !set)
! 531: || ((*mode_ptr == '-') && set)) {
! 532: /* Action modifier ("+"/"-") must be changed ... */
! 533: len = strlen(the_modes) - 1;
! 534: if (the_modes[len] == '+' || the_modes[len] == '-') {
! 535: /* Adjust last action modifier in result */
! 536: the_modes[len] = *mode_ptr;
! 537: } else {
! 538: /* Append modifier character to result string */
! 539: x[0] = *mode_ptr;
! 540: strlcat(the_modes, x, sizeof(the_modes));
! 541: }
! 542: set = *mode_ptr == '+';
! 543: }
! 544: continue;
! 545: }
! 546:
! 547: /* Are there arguments left? */
! 548: if (arg_arg >= Req->argc)
! 549: arg_arg = -1;
! 550:
! 551: if(!is_machine && !is_oper) {
! 552: if (Channel_UserHasMode(Channel, Client, 'q'))
! 553: is_owner = true;
! 554: if (Channel_UserHasMode(Channel, Client, 'a'))
! 555: is_admin = true;
! 556: if (Channel_UserHasMode(Channel, Client, 'o'))
! 557: is_op = true;
! 558: if (Channel_UserHasMode(Channel, Client, 'h'))
! 559: is_halfop = true;
! 560: }
! 561:
! 562: /* Validate modes */
! 563: x[0] = '\0';
! 564: argadd[0] = '\0';
! 565: client = NULL;
! 566: switch (*mode_ptr) {
! 567: /* --- Channel modes --- */
! 568: case 'R': /* Registered users only */
! 569: case 's': /* Secret channel */
! 570: case 'z': /* Secure connections only */
! 571: if(!is_oper && !is_machine && !is_owner &&
! 572: !is_admin && !is_op) {
! 573: connected = IRC_WriteErrClient(Origin,
! 574: ERR_CHANOPRIVSNEEDED_MSG,
! 575: Client_ID(Origin), Channel_Name(Channel));
! 576: goto chan_exit;
! 577: }
! 578: case 'i': /* Invite only */
! 579: case 'V': /* Invite disallow */
! 580: case 'M': /* Only identified nicks can write */
! 581: case 'm': /* Moderated */
! 582: case 'n': /* Only members can write */
! 583: case 'N': /* Can't change nick while on this channel */
! 584: case 'Q': /* No kicks */
! 585: case 't': /* Topic locked */
! 586: if(is_oper || is_machine || is_owner ||
! 587: is_admin || is_op || is_halfop)
! 588: x[0] = *mode_ptr;
! 589: else
! 590: connected = IRC_WriteErrClient(Origin,
! 591: ERR_CHANOPRIVSNEEDED_MSG,
! 592: Client_ID(Origin), Channel_Name(Channel));
! 593: break;
! 594: case 'k': /* Channel key */
! 595: if (Mode_Limit_Reached(Client, mode_arg_count++))
! 596: goto chan_exit;
! 597: if (!set) {
! 598: if (is_oper || is_machine || is_owner ||
! 599: is_admin || is_op || is_halfop) {
! 600: x[0] = *mode_ptr;
! 601: if (Channel_HasMode(Channel, 'k'))
! 602: strlcpy(argadd, "*", sizeof(argadd));
! 603: if (arg_arg > mode_arg)
! 604: arg_arg++;
! 605: } else
! 606: connected = IRC_WriteErrClient(Origin,
! 607: ERR_CHANOPRIVSNEEDED_MSG,
! 608: Client_ID(Origin),
! 609: Channel_Name(Channel));
! 610: break;
! 611: }
! 612: if (arg_arg > mode_arg) {
! 613: if (is_oper || is_machine || is_owner ||
! 614: is_admin || is_op || is_halfop) {
! 615: Channel_ModeDel(Channel, 'k');
! 616: Channel_SetKey(Channel,
! 617: Req->argv[arg_arg]);
! 618: strlcpy(argadd, Channel_Key(Channel),
! 619: sizeof(argadd));
! 620: x[0] = *mode_ptr;
! 621: } else {
! 622: connected = IRC_WriteErrClient(Origin,
! 623: ERR_CHANOPRIVSNEEDED_MSG,
! 624: Client_ID(Origin),
! 625: Channel_Name(Channel));
! 626: }
! 627: Req->argv[arg_arg][0] = '\0';
! 628: arg_arg++;
! 629: } else {
! 630: #ifdef STRICT_RFC
! 631: /* Only send error message in "strict" mode,
! 632: * this is how ircd2.11 and others behave ... */
! 633: connected = IRC_WriteErrClient(Origin,
! 634: ERR_NEEDMOREPARAMS_MSG,
! 635: Client_ID(Origin), Req->command);
! 636: #endif
! 637: goto chan_exit;
! 638: }
! 639: break;
! 640: case 'l': /* Member limit */
! 641: if (Mode_Limit_Reached(Client, mode_arg_count++))
! 642: goto chan_exit;
! 643: if (!set) {
! 644: if (is_oper || is_machine || is_owner ||
! 645: is_admin || is_op || is_halfop)
! 646: x[0] = *mode_ptr;
! 647: else
! 648: connected = IRC_WriteErrClient(Origin,
! 649: ERR_CHANOPRIVSNEEDED_MSG,
! 650: Client_ID(Origin),
! 651: Channel_Name(Channel));
! 652: break;
! 653: }
! 654: if (arg_arg > mode_arg) {
! 655: if (is_oper || is_machine || is_owner ||
! 656: is_admin || is_op || is_halfop) {
! 657: l = atol(Req->argv[arg_arg]);
! 658: if (l > 0 && l < 0xFFFF) {
! 659: Channel_ModeDel(Channel, 'l');
! 660: Channel_SetMaxUsers(Channel, l);
! 661: snprintf(argadd, sizeof(argadd),
! 662: "%ld", l);
! 663: x[0] = *mode_ptr;
! 664: }
! 665: } else {
! 666: connected = IRC_WriteErrClient(Origin,
! 667: ERR_CHANOPRIVSNEEDED_MSG,
! 668: Client_ID(Origin),
! 669: Channel_Name(Channel));
! 670: }
! 671: Req->argv[arg_arg][0] = '\0';
! 672: arg_arg++;
! 673: } else {
! 674: #ifdef STRICT_RFC
! 675: /* Only send error message in "strict" mode,
! 676: * this is how ircd2.11 and others behave ... */
! 677: connected = IRC_WriteErrClient(Origin,
! 678: ERR_NEEDMOREPARAMS_MSG,
! 679: Client_ID(Origin), Req->command);
! 680: #endif
! 681: goto chan_exit;
! 682: }
! 683: break;
! 684: case 'O': /* IRC operators only */
! 685: if (set) {
! 686: /* Only IRC operators are allowed to
! 687: * set the 'O' channel mode! */
! 688: if(is_oper || is_machine)
! 689: x[0] = 'O';
! 690: else
! 691: connected = IRC_WriteErrClient(Origin,
! 692: ERR_NOPRIVILEGES_MSG,
! 693: Client_ID(Origin));
! 694: } else if(is_oper || is_machine || is_owner ||
! 695: is_admin || is_op)
! 696: x[0] = 'O';
! 697: else
! 698: connected = IRC_WriteErrClient(Origin,
! 699: ERR_CHANOPRIVSNEEDED_MSG,
! 700: Client_ID(Origin),
! 701: Channel_Name(Channel));
! 702: break;
! 703: case 'P': /* Persistent channel */
! 704: if (set) {
! 705: /* Only IRC operators are allowed to
! 706: * set the 'P' channel mode! */
! 707: if(is_oper || is_machine)
! 708: x[0] = 'P';
! 709: else
! 710: connected = IRC_WriteErrClient(Origin,
! 711: ERR_NOPRIVILEGES_MSG,
! 712: Client_ID(Origin));
! 713: } else if(is_oper || is_machine || is_owner ||
! 714: is_admin || is_op)
! 715: x[0] = 'P';
! 716: else
! 717: connected = IRC_WriteErrClient(Origin,
! 718: ERR_CHANOPRIVSNEEDED_MSG,
! 719: Client_ID(Origin),
! 720: Channel_Name(Channel));
! 721: break;
! 722: /* --- Channel user modes --- */
! 723: case 'q': /* Owner */
! 724: case 'a': /* Channel admin */
! 725: if(!is_oper && !is_machine && !is_owner && !is_admin) {
! 726: connected = IRC_WriteErrClient(Origin,
! 727: ERR_CHANOPPRIVTOOLOW_MSG,
! 728: Client_ID(Origin),
! 729: Channel_Name(Channel));
! 730: goto chan_exit;
! 731: }
! 732: case 'o': /* Channel operator */
! 733: if(!is_oper && !is_machine && !is_owner &&
! 734: !is_admin && !is_op) {
! 735: connected = IRC_WriteErrClient(Origin,
! 736: ERR_CHANOPRIVSNEEDED_MSG,
! 737: Client_ID(Origin),
! 738: Channel_Name(Channel));
! 739: goto chan_exit;
! 740: }
! 741: case 'h': /* Half Op */
! 742: if(!is_oper && !is_machine && !is_owner &&
! 743: !is_admin && !is_op) {
! 744: connected = IRC_WriteErrClient(Origin,
! 745: ERR_CHANOPRIVSNEEDED_MSG,
! 746: Client_ID(Origin),
! 747: Channel_Name(Channel));
! 748: goto chan_exit;
! 749: }
! 750: case 'v': /* Voice */
! 751: if (arg_arg > mode_arg) {
! 752: if (is_oper || is_machine || is_owner ||
! 753: is_admin || is_op || is_halfop) {
! 754: client = Client_Search(Req->argv[arg_arg]);
! 755: if (client)
! 756: x[0] = *mode_ptr;
! 757: else
! 758: connected = IRC_WriteErrClient(Origin,
! 759: ERR_NOSUCHNICK_MSG,
! 760: Client_ID(Origin),
! 761: Req->argv[arg_arg]);
! 762: } else {
! 763: connected = IRC_WriteErrClient(Origin,
! 764: ERR_CHANOPRIVSNEEDED_MSG,
! 765: Client_ID(Origin),
! 766: Channel_Name(Channel));
! 767: }
! 768: Req->argv[arg_arg][0] = '\0';
! 769: arg_arg++;
! 770: } else {
! 771: #ifdef STRICT_RFC
! 772: /* Report an error to the client, when a user
! 773: * mode should be changed but no nickname is
! 774: * given. But don't do it when not in "strict"
! 775: * mode, because most other servers don't do
! 776: * it as well and some clients send "wired"
! 777: * MODE commands like "MODE #chan -ooo nick". */
! 778: connected = IRC_WriteErrClient(Origin,
! 779: ERR_NEEDMOREPARAMS_MSG,
! 780: Client_ID(Origin), Req->command);
! 781: #endif
! 782: goto chan_exit;
! 783: }
! 784: break;
! 785: /* --- Channel lists --- */
! 786: case 'I': /* Invite lists */
! 787: case 'b': /* Ban lists */
! 788: case 'e': /* Channel exception lists */
! 789: if (Mode_Limit_Reached(Client, mode_arg_count++))
! 790: goto chan_exit;
! 791: if (arg_arg > mode_arg) {
! 792: /* modify list */
! 793: if (is_oper || is_machine || is_owner ||
! 794: is_admin || is_op || is_halfop) {
! 795: connected = set
! 796: ? Add_To_List(*mode_ptr, Origin,
! 797: Client, Channel,
! 798: Req->argv[arg_arg])
! 799: : Del_From_List(*mode_ptr, Origin,
! 800: Client, Channel,
! 801: Req->argv[arg_arg]);
! 802: } else {
! 803: connected = IRC_WriteErrClient(Origin,
! 804: ERR_CHANOPRIVSNEEDED_MSG,
! 805: Client_ID(Origin),
! 806: Channel_Name(Channel));
! 807: }
! 808: Req->argv[arg_arg][0] = '\0';
! 809: arg_arg++;
! 810: } else {
! 811: switch (*mode_ptr) {
! 812: case 'I':
! 813: Channel_ShowInvites(Origin, Channel);
! 814: break;
! 815: case 'b':
! 816: Channel_ShowBans(Origin, Channel);
! 817: break;
! 818: case 'e':
! 819: Channel_ShowExcepts(Origin, Channel);
! 820: break;
! 821: }
! 822: }
! 823: break;
! 824: default:
! 825: if (Client_Type(Client) != CLIENT_SERVER) {
! 826: Log(LOG_DEBUG,
! 827: "Unknown mode \"%c%c\" from \"%s\" on %s!?",
! 828: set ? '+' : '-', *mode_ptr,
! 829: Client_ID(Origin), Channel_Name(Channel));
! 830: connected = IRC_WriteErrClient(Origin,
! 831: ERR_UNKNOWNMODE_MSG,
! 832: Client_ID(Origin), *mode_ptr,
! 833: Channel_Name(Channel));
! 834: x[0] = '\0';
! 835: } else {
! 836: Log(LOG_DEBUG,
! 837: "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
! 838: set ? '+' : '-', *mode_ptr,
! 839: Client_ID(Origin), Channel_Name(Channel));
! 840: x[0] = *mode_ptr;
! 841: }
! 842: }
! 843:
! 844: if (!connected)
! 845: break;
! 846:
! 847: /* Is there a valid mode change? */
! 848: if (!x[0])
! 849: continue;
! 850:
! 851: /* Validate target client */
! 852: if (client && (!Channel_IsMemberOf(Channel, client))) {
! 853: if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
! 854: Client_ID(Origin),
! 855: Client_ID(client),
! 856: Channel_Name(Channel)))
! 857: break;
! 858: continue;
! 859: }
! 860:
! 861: if (client) {
! 862: /* Channel-User-Mode */
! 863: retval = set
! 864: ? Channel_UserModeAdd(Channel, client, x[0])
! 865: : Channel_UserModeDel(Channel, client, x[0]);
! 866: if (retval) {
! 867: strlcat(the_args, " ", sizeof(the_args));
! 868: strlcat(the_args, Client_ID(client),
! 869: sizeof(the_args));
! 870: strlcat(the_modes, x, sizeof(the_modes));
! 871: LogDebug
! 872: ("User \"%s\": Mode change on %s, now \"%s\"",
! 873: Client_Mask(client), Channel_Name(Channel),
! 874: Channel_UserModes(Channel, client));
! 875: }
! 876: } else {
! 877: /* Channel-Mode */
! 878: retval = set
! 879: ? Channel_ModeAdd(Channel, x[0])
! 880: : Channel_ModeDel(Channel, x[0]);
! 881: if (retval) {
! 882: strlcat(the_modes, x, sizeof(the_modes));
! 883: LogDebug("Channel %s: Mode change, now \"%s\".",
! 884: Channel_Name(Channel),
! 885: Channel_Modes(Channel));
! 886: }
! 887: }
! 888:
! 889: /* Are there additional arguments to add? */
! 890: if (argadd[0]) {
! 891: strlcat(the_args, " ", sizeof(the_args));
! 892: strlcat(the_args, argadd, sizeof(the_args));
! 893: }
! 894: }
! 895:
! 896: chan_exit:
! 897: /* Are there changed modes? */
! 898: if (the_modes[1]) {
! 899: /* Clean up mode string */
! 900: len = strlen(the_modes) - 1;
! 901: if ((the_modes[len] == '+') || (the_modes[len] == '-'))
! 902: the_modes[len] = '\0';
! 903:
! 904: if (Client_Type(Client) == CLIENT_SERVER) {
! 905: /* MODE requests for local channels from other servers
! 906: * are definitely invalid! */
! 907: if (Channel_IsLocal(Channel)) {
! 908: Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
! 909: return CONNECTED;
! 910: }
! 911:
! 912: /* Forward mode changes to channel users and all the
! 913: * other remote servers: */
! 914: IRC_WriteStrServersPrefix(Client, Origin,
! 915: "MODE %s %s%s", Channel_Name(Channel),
! 916: the_modes, the_args);
! 917: IRC_WriteStrChannelPrefix(Client, Channel, Origin,
! 918: false, "MODE %s %s%s", Channel_Name(Channel),
! 919: the_modes, the_args);
! 920: } else {
! 921: if (use_servermode)
! 922: Origin = Client_ThisServer();
! 923: /* Send reply to client and inform other servers and channel users */
! 924: connected = IRC_WriteStrClientPrefix(Client, Origin,
! 925: "MODE %s %s%s", Channel_Name(Channel),
! 926: the_modes, the_args);
! 927: /* Only forward requests for non-local channels */
! 928: if (!Channel_IsLocal(Channel))
! 929: IRC_WriteStrServersPrefix(Client, Origin,
! 930: "MODE %s %s%s", Channel_Name(Channel),
! 931: the_modes, the_args);
! 932: IRC_WriteStrChannelPrefix(Client, Channel, Origin,
! 933: false, "MODE %s %s%s", Channel_Name(Channel),
! 934: the_modes, the_args);
! 935: }
! 936: }
! 937:
! 938: return connected;
! 939: } /* Channel_Mode */
! 940:
! 941: /**
! 942: * Handler for the IRC "AWAY" command.
! 943: *
! 944: * @param Client The client from which this command has been received.
! 945: * @param Req Request structure with prefix and all parameters.
! 946: * @return CONNECTED or DISCONNECTED.
! 947: */
! 948: GLOBAL bool
! 949: IRC_AWAY( CLIENT *Client, REQUEST *Req )
! 950: {
! 951: assert (Client != NULL);
! 952: assert (Req != NULL);
! 953:
! 954: if (Req->argc == 1 && Req->argv[0][0]) {
! 955: Client_SetAway(Client, Req->argv[0]);
! 956: Client_ModeAdd(Client, 'a');
! 957: IRC_WriteStrServersPrefix(Client, Client, "MODE %s :+a",
! 958: Client_ID( Client));
! 959: return IRC_WriteStrClient(Client, RPL_NOWAWAY_MSG,
! 960: Client_ID( Client));
! 961: } else {
! 962: Client_ModeDel(Client, 'a');
! 963: IRC_WriteStrServersPrefix(Client, Client, "MODE %s :-a",
! 964: Client_ID( Client));
! 965: return IRC_WriteStrClient(Client, RPL_UNAWAY_MSG,
! 966: Client_ID( Client));
! 967: }
! 968: } /* IRC_AWAY */
! 969:
! 970: /**
! 971: * Add entries to channel invite, ban and exception lists.
! 972: *
! 973: * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
! 974: * @param Prefix The originator of the command.
! 975: * @param Client The sender of the command.
! 976: * @param Channel The channel of which the list should be modified.
! 977: * @param Pattern The pattern to add to the list.
! 978: * @return CONNECTED or DISCONNECTED.
! 979: */
! 980: static bool
! 981: Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
! 982: const char *Pattern)
! 983: {
! 984: char mask[MASK_LEN];
! 985: struct list_head *list = NULL;
! 986: long int current_count;
! 987:
! 988: assert(Client != NULL);
! 989: assert(Channel != NULL);
! 990: assert(Pattern != NULL);
! 991: assert(what == 'I' || what == 'b' || what == 'e');
! 992:
! 993: Lists_MakeMask(Pattern, mask, sizeof(mask));
! 994: current_count = Lists_Count(Channel_GetListInvites(Channel))
! 995: + Lists_Count(Channel_GetListExcepts(Channel))
! 996: + Lists_Count(Channel_GetListBans(Channel));
! 997:
! 998: switch(what) {
! 999: case 'I':
! 1000: list = Channel_GetListInvites(Channel);
! 1001: break;
! 1002: case 'b':
! 1003: list = Channel_GetListBans(Channel);
! 1004: break;
! 1005: case 'e':
! 1006: list = Channel_GetListExcepts(Channel);
! 1007: break;
! 1008: }
! 1009:
! 1010: if (Lists_CheckDupeMask(list, mask))
! 1011: return CONNECTED;
! 1012: if (Client_Type(Client) == CLIENT_USER &&
! 1013: current_count >= MAX_HNDL_CHANNEL_LISTS)
! 1014: return IRC_WriteErrClient(Client, ERR_LISTFULL_MSG,
! 1015: Client_ID(Client),
! 1016: Channel_Name(Channel), mask,
! 1017: MAX_HNDL_CHANNEL_LISTS);
! 1018:
! 1019: switch (what) {
! 1020: case 'I':
! 1021: if (!Channel_AddInvite(Channel, mask, false, Client_ID(Client)))
! 1022: return CONNECTED;
! 1023: break;
! 1024: case 'b':
! 1025: if (!Channel_AddBan(Channel, mask, Client_ID(Client)))
! 1026: return CONNECTED;
! 1027: break;
! 1028: case 'e':
! 1029: if (!Channel_AddExcept(Channel, mask, Client_ID(Client)))
! 1030: return CONNECTED;
! 1031: break;
! 1032: }
! 1033: return Send_ListChange(true, what, Prefix, Client, Channel, mask);
! 1034: }
! 1035:
! 1036: /**
! 1037: * Delete entries from channel invite, ban and exception lists.
! 1038: *
! 1039: * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
! 1040: * @param Prefix The originator of the command.
! 1041: * @param Client The sender of the command.
! 1042: * @param Channel The channel of which the list should be modified.
! 1043: * @param Pattern The pattern to add to the list.
! 1044: * @return CONNECTED or DISCONNECTED.
! 1045: */
! 1046: static bool
! 1047: Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
! 1048: const char *Pattern)
! 1049: {
! 1050: char mask[MASK_LEN];
! 1051: struct list_head *list = NULL;
! 1052:
! 1053: assert(Client != NULL);
! 1054: assert(Channel != NULL);
! 1055: assert(Pattern != NULL);
! 1056: assert(what == 'I' || what == 'b' || what == 'e');
! 1057:
! 1058: Lists_MakeMask(Pattern, mask, sizeof(mask));
! 1059:
! 1060: switch (what) {
! 1061: case 'I':
! 1062: list = Channel_GetListInvites(Channel);
! 1063: break;
! 1064: case 'b':
! 1065: list = Channel_GetListBans(Channel);
! 1066: break;
! 1067: case 'e':
! 1068: list = Channel_GetListExcepts(Channel);
! 1069: break;
! 1070: }
! 1071:
! 1072: if (!Lists_CheckDupeMask(list, mask))
! 1073: return CONNECTED;
! 1074: Lists_Del(list, mask);
! 1075:
! 1076: return Send_ListChange(false, what, Prefix, Client, Channel, mask);
! 1077: }
! 1078:
! 1079: /**
! 1080: * Send information about changed channel invite/ban/exception lists to clients.
! 1081: *
! 1082: * @param IsAdd true if the list item has been added, false otherwise.
! 1083: * @param ModeChar The mode to use (e. g. 'b' or 'I')
! 1084: * @param Prefix The originator of the mode list change.
! 1085: * @param Client The sender of the command.
! 1086: * @param Channel The channel of which the list has been modified.
! 1087: * @param Mask The mask which has been added or removed.
! 1088: * @return CONNECTED or DISCONNECTED.
! 1089: */
! 1090: static bool
! 1091: Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix,
! 1092: CLIENT *Client, CHANNEL *Channel, const char *Mask)
! 1093: {
! 1094: bool ok = true;
! 1095:
! 1096: /* Send confirmation to the client */
! 1097: if (Client_Type(Client) == CLIENT_USER)
! 1098: ok = IRC_WriteStrClientPrefix(Client, Prefix, "MODE %s %c%c %s",
! 1099: Channel_Name(Channel),
! 1100: IsAdd ? '+' : '-',
! 1101: ModeChar, Mask);
! 1102:
! 1103: /* to other servers */
! 1104: IRC_WriteStrServersPrefix(Client, Prefix, "MODE %s %c%c %s",
! 1105: Channel_Name(Channel), IsAdd ? '+' : '-',
! 1106: ModeChar, Mask);
! 1107:
! 1108: /* and local users in channel */
! 1109: IRC_WriteStrChannelPrefix(Client, Channel, Prefix, false,
! 1110: "MODE %s %c%c %s", Channel_Name(Channel),
! 1111: IsAdd ? '+' : '-', ModeChar, Mask );
! 1112:
! 1113: return ok;
! 1114: } /* Send_ListChange */
! 1115:
! 1116: /* -eof- */
CVSweb