Annotation of ircnowd/src/ngircd/irc-channel.c, Revision 1.1
1.1 ! tomglok 1: /*
! 2: * ngIRCd -- The Next Generation IRC Daemon
! 3: * Copyright (c)2001-2018 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 channel commands
! 17: */
! 18:
! 19: #include <assert.h>
! 20: #include <stdlib.h>
! 21: #include <stdio.h>
! 22: #include <string.h>
! 23:
! 24: #include "conn.h"
! 25: #include "channel.h"
! 26: #include "conn-func.h"
! 27: #include "lists.h"
! 28: #include "log.h"
! 29: #include "match.h"
! 30: #include "messages.h"
! 31: #include "parse.h"
! 32: #include "irc.h"
! 33: #include "irc-info.h"
! 34: #include "irc-macros.h"
! 35: #include "irc-write.h"
! 36: #include "conf.h"
! 37:
! 38: #include "irc-channel.h"
! 39:
! 40: /**
! 41: * Part from all channels.
! 42: *
! 43: * RFC 2812, (3.2.1 Join message Command):
! 44: * Note that this message accepts a special argument ("0"), which is a
! 45: * special request to leave all channels the user is currently a member of.
! 46: * The server will process this message as if the user had sent a PART
! 47: * command (See Section 3.2.2) for each channel he is a member of.
! 48: *
! 49: * @param client Client that initiated the part request
! 50: * @param target Client that should part all joined channels
! 51: * @returns CONNECTED or DISCONNECTED
! 52: */
! 53: static bool
! 54: part_from_all_channels(CLIENT* client, CLIENT *target)
! 55: {
! 56: CL2CHAN *cl2chan;
! 57: CHANNEL *chan;
! 58:
! 59: while ((cl2chan = Channel_FirstChannelOf(target))) {
! 60: chan = Channel_GetChannel(cl2chan);
! 61: assert( chan != NULL );
! 62: Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
! 63: }
! 64: return CONNECTED;
! 65: } /* part_from_all_channels */
! 66:
! 67: /**
! 68: * Check weather a local client is allowed to join an already existing
! 69: * channel or not.
! 70: *
! 71: * @param Client Client that sent the JOIN command
! 72: * @param chan Channel to check
! 73: * @param channame Name of the channel
! 74: * @param key Provided channel key (or NULL)
! 75: * @returns true if client is allowed to join, false otherwise
! 76: */
! 77: static bool
! 78: join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
! 79: const char *key)
! 80: {
! 81: bool is_invited, is_banned, is_exception;
! 82:
! 83: /* Allow IRC operators to overwrite channel limits */
! 84: if (Client_HasMode(Client, 'o'))
! 85: return true;
! 86:
! 87: is_banned = Lists_Check(Channel_GetListBans(chan), Client);
! 88: is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
! 89: is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
! 90:
! 91: if (is_banned && !is_invited && !is_exception) {
! 92: /* Client is banned from channel (and not on invite list) */
! 93: IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
! 94: Client_ID(Client), channame);
! 95: return false;
! 96: }
! 97:
! 98: if (Channel_HasMode(chan, 'i') && !is_invited) {
! 99: /* Channel is "invite-only" and client is not on invite list */
! 100: IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
! 101: Client_ID(Client), channame);
! 102: return false;
! 103: }
! 104:
! 105: if (!Channel_CheckKey(chan, Client, key ? key : "")) {
! 106: /* Channel is protected by a channel key and the client
! 107: * didn't specify the correct one */
! 108: IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
! 109: Client_ID(Client), channame);
! 110: return false;
! 111: }
! 112:
! 113: if (Channel_HasMode(chan, 'l') &&
! 114: (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
! 115: /* There are more clints joined to this channel than allowed */
! 116: IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
! 117: Client_ID(Client), channame);
! 118: return false;
! 119: }
! 120:
! 121: if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
! 122: /* Only "secure" clients are allowed, but clients doesn't
! 123: * use SSL encryption */
! 124: IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
! 125: Client_ID(Client), channame);
! 126: return false;
! 127: }
! 128:
! 129: if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
! 130: /* Only IRC operators are allowed! */
! 131: IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
! 132: Client_ID(Client), channame);
! 133: return false;
! 134: }
! 135:
! 136: if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
! 137: /* Only registered users are allowed! */
! 138: IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
! 139: Client_ID(Client), channame);
! 140: return false;
! 141: }
! 142:
! 143: return true;
! 144: } /* join_allowed */
! 145:
! 146: /**
! 147: * Set user channel modes.
! 148: *
! 149: * @param chan Channel
! 150: * @param target User to set modes for
! 151: * @param flags Channel modes to add
! 152: */
! 153: static void
! 154: join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
! 155: {
! 156: if (flags) {
! 157: while (*flags) {
! 158: Channel_UserModeAdd(chan, target, *flags);
! 159: flags++;
! 160: }
! 161: }
! 162:
! 163: /* If the channel is persistent (+P) and client is an IRC op:
! 164: * make client chanop, if not disabled in configuration. */
! 165: if (Channel_HasMode(chan, 'P') && Conf_OperChanPAutoOp
! 166: && Client_HasMode(target, 'o'))
! 167: Channel_UserModeAdd(chan, target, 'o');
! 168: } /* join_set_channelmodes */
! 169:
! 170: /**
! 171: * Forward JOIN command to a specific server
! 172: *
! 173: * This function differentiates between servers using RFC 2813 mode that
! 174: * support the JOIN command with appended ASCII 7 character and channel
! 175: * modes, and servers using RFC 1459 protocol which require separate JOIN
! 176: * and MODE commands.
! 177: *
! 178: * @param To Forward JOIN (and MODE) command to this peer server
! 179: * @param Prefix Client used to prefix the genrated commands
! 180: * @param Data Parameters of JOIN command to forward, probably
! 181: * containing channel modes separated by ASCII 7.
! 182: */
! 183: static void
! 184: cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
! 185: {
! 186: CONN_ID conn;
! 187: char str[COMMAND_LEN], *ptr = NULL;
! 188:
! 189: strlcpy(str, (char *)Data, sizeof(str));
! 190: conn = Client_Conn(To);
! 191:
! 192: if (Conn_Options(conn) & CONN_RFC1459) {
! 193: /* RFC 1459 compatibility mode, appended modes are NOT
! 194: * supported, so strip them off! */
! 195: ptr = strchr(str, 0x7);
! 196: if (ptr)
! 197: *ptr++ = '\0';
! 198: }
! 199:
! 200: IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
! 201: if (ptr && *ptr)
! 202: IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
! 203: Client_ID(Prefix));
! 204: } /* cb_join_forward */
! 205:
! 206: /**
! 207: * Forward JOIN command to all servers
! 208: *
! 209: * This function calls cb_join_forward(), which differentiates between
! 210: * protocol implementations (e.g. RFC 2812, RFC 1459).
! 211: *
! 212: * @param Client Client used to prefix the genrated commands
! 213: * @param target Forward JOIN (and MODE) command to this peer server
! 214: * @param chan Channel structure
! 215: * @param channame Channel name
! 216: */
! 217: static void
! 218: join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
! 219: const char *channame)
! 220: {
! 221: char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN];
! 222:
! 223: /* RFC 2813, 4.2.1: channel modes are separated from the channel
! 224: * name with ASCII 7, if any, and not spaces: */
! 225: strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1);
! 226: if (modes[1])
! 227: modes[0] = 0x7;
! 228: else
! 229: modes[0] = '\0';
! 230:
! 231: /* forward to other servers (if it is not a local channel) */
! 232: if (!Channel_IsLocal(chan)) {
! 233: snprintf(str, sizeof(str), "%s%s", channame, modes);
! 234: IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0',
! 235: cb_join_forward, str);
! 236: }
! 237:
! 238: /* tell users in this channel about the new client */
! 239: IRC_WriteStrChannelPrefix(Client, chan, target, false,
! 240: "JOIN :%s", channame);
! 241:
! 242: /* synchronize channel modes */
! 243: if (modes[1]) {
! 244: IRC_WriteStrChannelPrefix(Client, chan, target, false,
! 245: "MODE %s +%s %s", channame,
! 246: &modes[1], Client_ID(target));
! 247: }
! 248: } /* join_forward */
! 249:
! 250: /**
! 251: * Acknowledge user JOIN request and send "channel info" numerics.
! 252: *
! 253: * @param Client Client used to prefix the genrated commands
! 254: * @param target Forward commands/numerics to this user
! 255: * @param chan Channel structure
! 256: * @param channame Channel name
! 257: */
! 258: static bool
! 259: join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
! 260: const char *channame)
! 261: {
! 262: const char *topic;
! 263:
! 264: if (Client_Type(Client) != CLIENT_USER)
! 265: return true;
! 266: /* acknowledge join */
! 267: if (!IRC_WriteStrClientPrefix(Client, target, "JOIN :%s", channame))
! 268: return false;
! 269:
! 270: /* Send topic to client, if any */
! 271: topic = Channel_Topic(chan);
! 272: assert(topic != NULL);
! 273: if (*topic) {
! 274: if (!IRC_WriteStrClient(Client, RPL_TOPIC_MSG,
! 275: Client_ID(Client), channame, topic))
! 276: return false;
! 277: #ifndef STRICT_RFC
! 278: if (!IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG,
! 279: Client_ID(Client), channame,
! 280: Channel_TopicWho(chan),
! 281: Channel_TopicTime(chan)))
! 282: return false;
! 283: #endif
! 284: }
! 285: /* send list of channel members to client */
! 286: if (!IRC_Send_NAMES(Client, chan))
! 287: return false;
! 288: return IRC_WriteStrClient(Client, RPL_ENDOFNAMES_MSG, Client_ID(Client),
! 289: Channel_Name(chan));
! 290: } /* join_send_topic */
! 291:
! 292: /**
! 293: * Handler for the IRC "JOIN" command.
! 294: *
! 295: * @param Client The client from which this command has been received.
! 296: * @param Req Request structure with prefix and all parameters.
! 297: * @return CONNECTED or DISCONNECTED.
! 298: */
! 299: GLOBAL bool
! 300: IRC_JOIN( CLIENT *Client, REQUEST *Req )
! 301: {
! 302: char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL;
! 303: CLIENT *target;
! 304: CHANNEL *chan;
! 305:
! 306: assert (Client != NULL);
! 307: assert (Req != NULL);
! 308:
! 309: _IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
! 310:
! 311: /* Is argument "0"? */
! 312: if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
! 313: return part_from_all_channels(Client, target);
! 314:
! 315: /* Are channel keys given? */
! 316: if (Req->argc > 1)
! 317: key = strtok_r(Req->argv[1], ",", &lastkey);
! 318:
! 319: channame = Req->argv[0];
! 320: channame = strtok_r(channame, ",", &lastchan);
! 321:
! 322: /* Make sure that "channame" is not the empty string ("JOIN :") */
! 323: if (!channame)
! 324: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 325: Client_ID(Client), Req->command);
! 326:
! 327: while (channame) {
! 328: flags = NULL;
! 329:
! 330: /* Did the server include channel-user-modes? */
! 331: if (Client_Type(Client) == CLIENT_SERVER) {
! 332: flags = strchr(channame, 0x7);
! 333: if (flags) {
! 334: *flags = '\0';
! 335: flags++;
! 336: }
! 337: }
! 338:
! 339: chan = Channel_Search(channame);
! 340:
! 341: /* Local client? */
! 342: if (Client_Type(Client) == CLIENT_USER) {
! 343: if (chan) {
! 344: /* Already existing channel: already member? */
! 345: if (Channel_IsMemberOf(chan, Client))
! 346: goto join_next;
! 347: } else {
! 348: /* Channel must be created */
! 349: if (!strchr(Conf_AllowedChannelTypes, channame[0])) {
! 350: /* ... but channel type is not allowed! */
! 351: IRC_WriteErrClient(Client,
! 352: ERR_NOSUCHCHANNEL_MSG,
! 353: Client_ID(Client), channame);
! 354: goto join_next;
! 355: }
! 356: }
! 357:
! 358: /* Test if the user has reached the channel limit */
! 359: if ((Conf_MaxJoins > 0) &&
! 360: (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
! 361: if (!IRC_WriteErrClient(Client,
! 362: ERR_TOOMANYCHANNELS_MSG,
! 363: Client_ID(Client), channame))
! 364: return DISCONNECTED;
! 365: goto join_next;
! 366: }
! 367:
! 368: if (chan) {
! 369: /* Already existing channel: check if the
! 370: * client is allowed to join */
! 371: if (!join_allowed(Client, chan, channame, key))
! 372: goto join_next;
! 373: } else {
! 374: /* New channel: first user will become channel
! 375: * operator unless this is a modeless channel */
! 376: if (*channame != '+')
! 377: flags = "o";
! 378: }
! 379:
! 380: /* Local client: update idle time */
! 381: Conn_UpdateIdle(Client_Conn(Client));
! 382: } else {
! 383: /* Remote server: we don't need to know whether the
! 384: * client is invited or not, but we have to make sure
! 385: * that the "one shot" entries (generated by INVITE
! 386: * commands) in this list become deleted when a user
! 387: * joins a channel this way. */
! 388: if (chan)
! 389: (void)Lists_Check(Channel_GetListInvites(chan),
! 390: target);
! 391: }
! 392:
! 393: /* Join channel (and create channel if it doesn't exist) */
! 394: if (!Channel_Join(target, channame))
! 395: goto join_next;
! 396:
! 397: if (!chan) { /* channel is new; it has been created above */
! 398: chan = Channel_Search(channame);
! 399: assert(chan != NULL);
! 400: if (Channel_IsModeless(chan)) {
! 401: Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
! 402: Channel_ModeAdd(chan, 'n'); /* no external msgs */
! 403: }
! 404: }
! 405: assert(chan != NULL);
! 406:
! 407: join_set_channelmodes(chan, target, flags);
! 408:
! 409: join_forward(Client, target, chan, channame);
! 410:
! 411: if (!join_send_topic(Client, target, chan, channame))
! 412: break; /* write error */
! 413:
! 414: join_next:
! 415: /* next channel? */
! 416: channame = strtok_r(NULL, ",", &lastchan);
! 417: if (channame && key)
! 418: key = strtok_r(NULL, ",", &lastkey);
! 419: }
! 420: return CONNECTED;
! 421: } /* IRC_JOIN */
! 422:
! 423: /**
! 424: * Handler for the IRC "PART" command.
! 425: *
! 426: * @param Client The client from which this command has been received.
! 427: * @param Req Request structure with prefix and all parameters.
! 428: * @return CONNECTED or DISCONNECTED.
! 429: */
! 430: GLOBAL bool
! 431: IRC_PART(CLIENT * Client, REQUEST * Req)
! 432: {
! 433: CLIENT *target;
! 434: char *chan;
! 435:
! 436: assert(Client != NULL);
! 437: assert(Req != NULL);
! 438:
! 439: _IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
! 440:
! 441: /* Loop over all the given channel names */
! 442: chan = strtok(Req->argv[0], ",");
! 443:
! 444: /* Make sure that "chan" is not the empty string ("PART :") */
! 445: if (!chan)
! 446: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 447: Client_ID(Client), Req->command);
! 448:
! 449: while (chan) {
! 450: Channel_Part(target, Client, chan,
! 451: Req->argc > 1 ? Req->argv[1] : "");
! 452: chan = strtok(NULL, ",");
! 453: }
! 454:
! 455: /* Update idle time, if local client */
! 456: if (Client_Conn(Client) > NONE)
! 457: Conn_UpdateIdle(Client_Conn(Client));
! 458:
! 459: return CONNECTED;
! 460: } /* IRC_PART */
! 461:
! 462: /**
! 463: * Handler for the IRC "TOPIC" command.
! 464: *
! 465: * @param Client The client from which this command has been received.
! 466: * @param Req Request structure with prefix and all parameters.
! 467: * @return CONNECTED or DISCONNECTED.
! 468: */
! 469: GLOBAL bool
! 470: IRC_TOPIC( CLIENT *Client, REQUEST *Req )
! 471: {
! 472: CHANNEL *chan;
! 473: CLIENT *from;
! 474: char *topic;
! 475: bool r, topic_power;
! 476:
! 477: assert( Client != NULL );
! 478: assert( Req != NULL );
! 479:
! 480: _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
! 481:
! 482: chan = Channel_Search(Req->argv[0]);
! 483: if (!chan)
! 484: return IRC_WriteErrClient(from, ERR_NOSUCHCHANNEL_MSG,
! 485: Client_ID(from), Req->argv[0]);
! 486:
! 487: /* Only remote servers and channel members are allowed to change the
! 488: * channel topic, and IRC operators when the Conf_OperCanMode option
! 489: * is set in the server configuration. */
! 490: if (Client_Type(Client) != CLIENT_SERVER) {
! 491: topic_power = Client_HasMode(from, 'o');
! 492: if (!Channel_IsMemberOf(chan, from)
! 493: && !(Conf_OperCanMode && topic_power))
! 494: return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
! 495: Client_ID(from), Req->argv[0]);
! 496: } else
! 497: topic_power = true;
! 498:
! 499: if (Req->argc == 1) {
! 500: /* Request current topic */
! 501: topic = Channel_Topic(chan);
! 502: if (*topic) {
! 503: r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
! 504: Client_ID(Client),
! 505: Channel_Name(chan), topic);
! 506: #ifndef STRICT_RFC
! 507: if (!r)
! 508: return r;
! 509: r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
! 510: Client_ID(Client),
! 511: Channel_Name(chan),
! 512: Channel_TopicWho(chan),
! 513: Channel_TopicTime(chan));
! 514: #endif
! 515: return r;
! 516: }
! 517: else
! 518: return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
! 519: Client_ID(from),
! 520: Channel_Name(chan));
! 521: }
! 522:
! 523: if (Channel_HasMode(chan, 't')) {
! 524: /* Topic Lock. Is the user a channel op or IRC operator? */
! 525: if(!topic_power &&
! 526: !Channel_UserHasMode(chan, from, 'h') &&
! 527: !Channel_UserHasMode(chan, from, 'o') &&
! 528: !Channel_UserHasMode(chan, from, 'a') &&
! 529: !Channel_UserHasMode(chan, from, 'q'))
! 530: return IRC_WriteErrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
! 531: Client_ID(from),
! 532: Channel_Name(chan));
! 533: }
! 534:
! 535: LogDebug("%s \"%s\" set topic on \"%s\": %s",
! 536: Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
! 537: Req->argv[1][0] ? Req->argv[1] : "<none>");
! 538:
! 539: if (Conf_OperServerMode)
! 540: from = Client_ThisServer();
! 541:
! 542: /* Update channel and forward new topic to other servers */
! 543: if (!Channel_IsLocal(chan))
! 544: IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
! 545: Req->argv[0], Req->argv[1]);
! 546:
! 547: /* Infrom local clients, but only when the topic really changed. */
! 548: if (strcmp(Req->argv[1], Channel_Topic(chan)) != 0)
! 549: IRC_WriteStrChannelPrefix(Client, chan, from, false,
! 550: "TOPIC %s :%s", Req->argv[0],
! 551: Req->argv[1]);
! 552:
! 553: /* Update topic, setter, and timestamp. */
! 554: Channel_SetTopic(chan, from, Req->argv[1]);
! 555:
! 556: /* Send confirmation when the local client is a user. */
! 557: if (Client_Type(Client) == CLIENT_USER)
! 558: return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
! 559: Req->argv[0], Req->argv[1]);
! 560: else
! 561: return CONNECTED;
! 562: } /* IRC_TOPIC */
! 563:
! 564: /**
! 565: * Handler for the IRC "LIST" command.
! 566: *
! 567: * @param Client The client from which this command has been received.
! 568: * @param Req Request structure with prefix and all parameters.
! 569: * @return CONNECTED or DISCONNECTED.
! 570: */
! 571: GLOBAL bool
! 572: IRC_LIST( CLIENT *Client, REQUEST *Req )
! 573: {
! 574: char *pattern;
! 575: CHANNEL *chan;
! 576: CLIENT *from, *target;
! 577: int count = 0;
! 578:
! 579: assert(Client != NULL);
! 580: assert(Req != NULL);
! 581:
! 582: _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
! 583:
! 584: if (Req->argc > 0)
! 585: pattern = strtok(Req->argv[0], ",");
! 586: else
! 587: pattern = "*";
! 588:
! 589: if (Req->argc == 2) {
! 590: /* Forward to other server? */
! 591: target = Client_Search(Req->argv[1]);
! 592: if (! target || Client_Type(target) != CLIENT_SERVER)
! 593: return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
! 594: Client_ID(Client),
! 595: Req->argv[1]);
! 596:
! 597: if (target != Client_ThisServer()) {
! 598: /* Target is indeed an other server, forward it! */
! 599: return IRC_WriteStrClientPrefix(target, from,
! 600: "LIST %s :%s",
! 601: Req->argv[0],
! 602: Req->argv[1]);
! 603: }
! 604: }
! 605:
! 606: /* Send list head */
! 607: if (!IRC_WriteStrClient(from, RPL_LISTSTART_MSG, Client_ID(from)))
! 608: return DISCONNECTED;
! 609:
! 610: while (pattern) {
! 611: /* Loop through all the channels */
! 612: if (Req->argc > 0)
! 613: ngt_LowerStr(pattern);
! 614: chan = Channel_First();
! 615: while (chan) {
! 616: /* Check search pattern */
! 617: if (MatchCaseInsensitive(pattern, Channel_Name(chan))) {
! 618: /* Gotcha! */
! 619: if (!Channel_HasMode(chan, 's')
! 620: || Channel_IsMemberOf(chan, from)
! 621: || Client_HasMode(from, 'o'))
! 622: {
! 623: if ((Conf_MaxListSize > 0)
! 624: && IRC_CheckListTooBig(from, count,
! 625: Conf_MaxListSize,
! 626: "LIST"))
! 627: break;
! 628: if (!IRC_WriteStrClient(from,
! 629: RPL_LIST_MSG, Client_ID(from),
! 630: Channel_Name(chan),
! 631: Channel_MemberCount(chan),
! 632: Channel_Topic( chan )))
! 633: return DISCONNECTED;
! 634: count++;
! 635: }
! 636: }
! 637: chan = Channel_Next(chan);
! 638: }
! 639:
! 640: /* Get next name ... */
! 641: if(Req->argc > 0)
! 642: pattern = strtok(NULL, ",");
! 643: else
! 644: pattern = NULL;
! 645: }
! 646:
! 647: return IRC_WriteStrClient(from, RPL_LISTEND_MSG, Client_ID(from));
! 648: } /* IRC_LIST */
! 649:
! 650: /**
! 651: * Handler for the IRC+ "CHANINFO" command.
! 652: *
! 653: * @param Client The client from which this command has been received.
! 654: * @param Req Request structure with prefix and all parameters.
! 655: * @return CONNECTED or DISCONNECTED.
! 656: */
! 657: GLOBAL bool
! 658: IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
! 659: {
! 660: char modes_add[COMMAND_LEN], l[16];
! 661: CLIENT *from;
! 662: CHANNEL *chan;
! 663: int arg_topic;
! 664:
! 665: assert( Client != NULL );
! 666: assert( Req != NULL );
! 667:
! 668: /* Bad number of parameters? */
! 669: if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5)
! 670: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 671: Client_ID(Client), Req->command);
! 672:
! 673: /* Compatibility kludge */
! 674: if (Req->argc == 5)
! 675: arg_topic = 4;
! 676: else if(Req->argc == 3)
! 677: arg_topic = 2;
! 678: else
! 679: arg_topic = -1;
! 680:
! 681: _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
! 682:
! 683: /* Search or create channel */
! 684: chan = Channel_Search( Req->argv[0] );
! 685: if (!chan)
! 686: chan = Channel_Create( Req->argv[0] );
! 687: if (!chan)
! 688: return CONNECTED;
! 689:
! 690: if (Req->argv[1][0] == '+') {
! 691: if (!*Channel_Modes(chan)) {
! 692: /* OK, this channel doesn't have modes yet,
! 693: * set the received ones: */
! 694: Channel_SetModes(chan, &Req->argv[1][1]);
! 695:
! 696: if(Req->argc == 5) {
! 697: if(Channel_HasMode(chan, 'k'))
! 698: Channel_SetKey(chan, Req->argv[2]);
! 699: if(Channel_HasMode(chan, 'l'))
! 700: Channel_SetMaxUsers(chan, atol(Req->argv[3]));
! 701: } else {
! 702: /* Delete modes which we never want to inherit */
! 703: Channel_ModeDel(chan, 'l');
! 704: Channel_ModeDel(chan, 'k');
! 705: }
! 706:
! 707: strcpy(modes_add, "");
! 708: if (Channel_HasMode(chan, 'l')) {
! 709: snprintf(l, sizeof(l), " %lu",
! 710: Channel_MaxUsers(chan));
! 711: strlcat(modes_add, l, sizeof(modes_add));
! 712: }
! 713: if (Channel_HasMode(chan, 'k')) {
! 714: strlcat(modes_add, " ", sizeof(modes_add));
! 715: strlcat(modes_add, Channel_Key(chan),
! 716: sizeof(modes_add));
! 717: }
! 718:
! 719: /* Inform members of this channel */
! 720: IRC_WriteStrChannelPrefix(Client, chan, from, false,
! 721: "MODE %s +%s%s", Req->argv[0],
! 722: Channel_Modes(chan), modes_add);
! 723: }
! 724: }
! 725: else
! 726: Log(LOG_WARNING, "CHANINFO: invalid MODE format ignored!");
! 727:
! 728: if (arg_topic > 0) {
! 729: /* We got a topic */
! 730: if (!*Channel_Topic(chan) && Req->argv[arg_topic][0]) {
! 731: /* OK, there is no topic jet */
! 732: Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
! 733: IRC_WriteStrChannelPrefix(Client, chan, from, false,
! 734: "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
! 735: }
! 736: }
! 737:
! 738: /* Forward CHANINFO to other servers */
! 739: if (Req->argc == 5)
! 740: IRC_WriteStrServersPrefixFlag(Client, from, 'C',
! 741: "CHANINFO %s %s %s %s :%s",
! 742: Req->argv[0], Req->argv[1],
! 743: Req->argv[2], Req->argv[3],
! 744: Req->argv[4]);
! 745: else if (Req->argc == 3)
! 746: IRC_WriteStrServersPrefixFlag(Client, from, 'C',
! 747: "CHANINFO %s %s :%s",
! 748: Req->argv[0], Req->argv[1],
! 749: Req->argv[2]);
! 750: else
! 751: IRC_WriteStrServersPrefixFlag(Client, from, 'C',
! 752: "CHANINFO %s %s",
! 753: Req->argv[0], Req->argv[1]);
! 754:
! 755: return CONNECTED;
! 756: } /* IRC_CHANINFO */
! 757:
! 758: /* -eof- */
CVSweb