2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
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.
16 * IRC channel commands
27 #include "conn-func.h"
35 #include "irc-macros.h"
36 #include "irc-write.h"
39 #include "irc-channel.h"
42 * Part from all channels.
44 * RFC 2812, (3.2.1 Join message Command):
45 * Note that this message accepts a special argument ("0"), which is a
46 * special request to leave all channels the user is currently a member of.
47 * The server will process this message as if the user had sent a PART
48 * command (See Section 3.2.2) for each channel he is a member of.
50 * @param client Client that initiated the part request
51 * @param target Client that should part all joined channels
52 * @returns CONNECTED or DISCONNECTED
55 part_from_all_channels(CLIENT* client, CLIENT *target)
60 while ((cl2chan = Channel_FirstChannelOf(target))) {
61 chan = Channel_GetChannel(cl2chan);
62 assert( chan != NULL );
63 Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
66 } /* part_from_all_channels */
69 * Check weather a local client is allowed to join an already existing
72 * @param Client Client that sent the JOIN command
73 * @param chan Channel to check
74 * @param channame Name of the channel
75 * @param key Provided channel key (or NULL)
76 * @returns true if client is allowed to join, false otherwise
79 join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
82 bool is_invited, is_banned, is_exception;
84 /* Allow IRC operators to overwrite channel limits */
85 if (Client_HasMode(Client, 'o'))
88 is_banned = Lists_Check(Channel_GetListBans(chan), Client);
89 is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
90 is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
92 if (is_banned && !is_invited && !is_exception) {
93 /* Client is banned from channel (and not on invite list) */
94 IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
95 Client_ID(Client), channame);
99 if (Channel_HasMode(chan, 'i') && !is_invited) {
100 /* Channel is "invite-only" and client is not on invite list */
101 IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
102 Client_ID(Client), channame);
106 if (!Channel_CheckKey(chan, Client, key ? key : "")) {
107 /* Channel is protected by a channel key and the client
108 * didn't specify the correct one */
109 IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
110 Client_ID(Client), channame);
114 if (Channel_HasMode(chan, 'l') &&
115 (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
116 /* There are more clints joined to this channel than allowed */
117 IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
118 Client_ID(Client), channame);
122 if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
123 /* Only "secure" clients are allowed, but clients doesn't
124 * use SSL encryption */
125 IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
126 Client_ID(Client), channame);
130 if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
131 /* Only IRC operators are allowed! */
132 IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
133 Client_ID(Client), channame);
137 if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
138 /* Only registered users are allowed! */
139 IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
140 Client_ID(Client), channame);
148 * Set user channel modes.
150 * @param chan Channel
151 * @param target User to set modes for
152 * @param flags Channel modes to add
155 join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
159 Channel_UserModeAdd(chan, target, *flags);
164 /* If the channel is persistent (+P) and client is an IRC op:
165 * make client chanop, if not disabled in configuration. */
166 if (Channel_HasMode(chan, 'P') && Conf_OperChanPAutoOp
167 && Client_HasMode(target, 'o'))
168 Channel_UserModeAdd(chan, target, 'o');
169 } /* join_set_channelmodes */
172 * Forward JOIN command to a specific server
174 * This function differentiates between servers using RFC 2813 mode that
175 * support the JOIN command with appended ASCII 7 character and channel
176 * modes, and servers using RFC 1459 protocol which require separate JOIN
179 * @param To Forward JOIN (and MODE) command to this peer server
180 * @param Prefix Client used to prefix the genrated commands
181 * @param Data Parameters of JOIN command to forward, probably
182 * containing channel modes separated by ASCII 7.
185 cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
188 char str[COMMAND_LEN], *ptr = NULL;
190 strlcpy(str, (char *)Data, sizeof(str));
191 conn = Client_Conn(To);
193 if (Conn_Options(conn) & CONN_RFC1459) {
194 /* RFC 1459 compatibility mode, appended modes are NOT
195 * supported, so strip them off! */
196 ptr = strchr(str, 0x7);
201 IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
203 IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
205 } /* cb_join_forward */
208 * Forward JOIN command to all servers
210 * This function calls cb_join_forward(), which differentiates between
211 * protocol implementations (e.g. RFC 2812, RFC 1459).
213 * @param Client Client used to prefix the genrated commands
214 * @param target Forward JOIN (and MODE) command to this peer server
215 * @param chan Channel structure
216 * @param channame Channel name
219 join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
220 const char *channame)
222 char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN];
224 /* RFC 2813, 4.2.1: channel modes are separated from the channel
225 * name with ASCII 7, if any, and not spaces: */
226 strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1);
232 /* forward to other servers (if it is not a local channel) */
233 if (!Channel_IsLocal(chan)) {
234 snprintf(str, sizeof(str), "%s%s", channame, modes);
235 IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0',
236 cb_join_forward, str);
239 /* tell users in this channel about the new client */
240 IRC_WriteStrChannelPrefix(Client, chan, target, false,
241 "JOIN :%s", channame);
243 /* synchronize channel modes */
245 IRC_WriteStrChannelPrefix(Client, chan, target, false,
246 "MODE %s +%s %s", channame,
247 &modes[1], Client_ID(target));
252 * Acknowledge user JOIN request and send "channel info" numerics.
254 * @param Client Client used to prefix the genrated commands
255 * @param target Forward commands/numerics to this user
256 * @param chan Channel structure
257 * @param channame Channel name
260 join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
261 const char *channame)
265 if (Client_Type(Client) != CLIENT_USER)
267 /* acknowledge join */
268 if (!IRC_WriteStrClientPrefix(Client, target, "JOIN :%s", channame))
271 /* Send topic to client, if any */
272 topic = Channel_Topic(chan);
273 assert(topic != NULL);
275 if (!IRC_WriteStrClient(Client, RPL_TOPIC_MSG,
276 Client_ID(Client), channame, topic))
279 if (!IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG,
280 Client_ID(Client), channame,
281 Channel_TopicWho(chan),
282 Channel_TopicTime(chan)))
286 /* send list of channel members to client */
287 if (!IRC_Send_NAMES(Client, chan))
289 return IRC_WriteStrClient(Client, RPL_ENDOFNAMES_MSG, Client_ID(Client),
291 } /* join_send_topic */
294 * Handler for the IRC "JOIN" command.
296 * @param Client The client from which this command has been received.
297 * @param Req Request structure with prefix and all parameters.
298 * @return CONNECTED or DISCONNECTED.
301 IRC_JOIN( CLIENT *Client, REQUEST *Req )
303 char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL;
307 assert (Client != NULL);
308 assert (Req != NULL);
310 _IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
312 /* Is argument "0"? */
313 if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
314 return part_from_all_channels(Client, target);
316 /* Are channel keys given? */
318 key = strtok_r(Req->argv[1], ",", &lastkey);
320 channame = Req->argv[0];
321 channame = strtok_r(channame, ",", &lastchan);
323 /* Make sure that "channame" is not the empty string ("JOIN :") */
325 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
326 Client_ID(Client), Req->command);
331 /* Did the server include channel-user-modes? */
332 if (Client_Type(Client) == CLIENT_SERVER) {
333 flags = strchr(channame, 0x7);
340 chan = Channel_Search(channame);
343 if (Client_Type(Client) == CLIENT_USER) {
345 /* Already existing channel: already member? */
346 if (Channel_IsMemberOf(chan, Client))
349 /* Channel must be created */
350 if (!strchr(Conf_AllowedChannelTypes, channame[0])) {
351 /* ... but channel type is not allowed! */
352 IRC_WriteErrClient(Client,
353 ERR_NOSUCHCHANNEL_MSG,
354 Client_ID(Client), channame);
359 /* Test if the user has reached the channel limit */
360 if ((Conf_MaxJoins > 0) &&
361 (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
362 if (!IRC_WriteErrClient(Client,
363 ERR_TOOMANYCHANNELS_MSG,
364 Client_ID(Client), channame))
370 /* Already existing channel: check if the
371 * client is allowed to join */
372 if (!join_allowed(Client, chan, channame, key))
375 /* New channel: first user will become channel
376 * operator unless this is a modeless channel */
377 if (*channame != '+')
381 /* Local client: update idle time */
382 Conn_UpdateIdle(Client_Conn(Client));
384 /* Remote server: we don't need to know whether the
385 * client is invited or not, but we have to make sure
386 * that the "one shot" entries (generated by INVITE
387 * commands) in this list become deleted when a user
388 * joins a channel this way. */
390 (void)Lists_Check(Channel_GetListInvites(chan),
394 /* Join channel (and create channel if it doesn't exist) */
395 if (!Channel_Join(target, channame))
398 if (!chan) { /* channel is new; it has been created above */
399 chan = Channel_Search(channame);
400 assert(chan != NULL);
401 if (Channel_IsModeless(chan)) {
402 Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
403 Channel_ModeAdd(chan, 'n'); /* no external msgs */
406 assert(chan != NULL);
408 join_set_channelmodes(chan, target, flags);
410 join_forward(Client, target, chan, channame);
412 if (!join_send_topic(Client, target, chan, channame))
413 break; /* write error */
417 channame = strtok_r(NULL, ",", &lastchan);
419 key = strtok_r(NULL, ",", &lastkey);
425 * Handler for the IRC "PART" command.
427 * @param Client The client from which this command has been received.
428 * @param Req Request structure with prefix and all parameters.
429 * @return CONNECTED or DISCONNECTED.
432 IRC_PART(CLIENT * Client, REQUEST * Req)
437 assert(Client != NULL);
440 _IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
442 /* Loop over all the given channel names */
443 chan = strtok(Req->argv[0], ",");
445 /* Make sure that "chan" is not the empty string ("PART :") */
447 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
448 Client_ID(Client), Req->command);
451 Channel_Part(target, Client, chan,
452 Req->argc > 1 ? Req->argv[1] : Client_ID(target));
453 chan = strtok(NULL, ",");
456 /* Update idle time, if local client */
457 if (Client_Conn(Client) > NONE)
458 Conn_UpdateIdle(Client_Conn(Client));
464 * Handler for the IRC "TOPIC" command.
466 * @param Client The client from which this command has been received.
467 * @param Req Request structure with prefix and all parameters.
468 * @return CONNECTED or DISCONNECTED.
471 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
478 assert( Client != NULL );
479 assert( Req != NULL );
481 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
483 chan = Channel_Search(Req->argv[0]);
485 return IRC_WriteErrClient(from, ERR_NOSUCHCHANNEL_MSG,
486 Client_ID(from), Req->argv[0]);
488 /* Only remote servers and channel members are allowed to change the
489 * channel topic, and IRC operators when the Conf_OperCanMode option
490 * is set in the server configuration. */
491 if (Client_Type(Client) != CLIENT_SERVER) {
492 topic_power = Client_HasMode(from, 'o');
493 if (!Channel_IsMemberOf(chan, from)
494 && !(Conf_OperCanMode && topic_power))
495 return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
496 Client_ID(from), Req->argv[0]);
500 if (Req->argc == 1) {
501 /* Request actual topic */
502 topic = Channel_Topic(chan);
504 r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
506 Channel_Name(chan), topic);
510 r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
513 Channel_TopicWho(chan),
514 Channel_TopicTime(chan));
519 return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
524 if (Channel_HasMode(chan, 't')) {
525 /* Topic Lock. Is the user a channel op or IRC operator? */
527 !Channel_UserHasMode(chan, from, 'h') &&
528 !Channel_UserHasMode(chan, from, 'o') &&
529 !Channel_UserHasMode(chan, from, 'a') &&
530 !Channel_UserHasMode(chan, from, 'q'))
531 return IRC_WriteErrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
537 Channel_SetTopic(chan, from, Req->argv[1]);
538 LogDebug("%s \"%s\" set topic on \"%s\": %s",
539 Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
540 Req->argv[1][0] ? Req->argv[1] : "<none>");
542 if (Conf_OperServerMode)
543 from = Client_ThisServer();
545 /* Update channel and forward new topic to other servers */
546 if (!Channel_IsLocal(chan))
547 IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
548 Req->argv[0], Req->argv[1]);
549 IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s",
550 Req->argv[0], Req->argv[1]);
552 if (Client_Type(Client) == CLIENT_USER)
553 return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
554 Req->argv[0], Req->argv[1]);
560 * Handler for the IRC "LIST" command.
562 * @param Client The client from which this command has been received.
563 * @param Req Request structure with prefix and all parameters.
564 * @return CONNECTED or DISCONNECTED.
567 IRC_LIST( CLIENT *Client, REQUEST *Req )
571 CLIENT *from, *target;
574 assert(Client != NULL);
577 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
580 pattern = strtok(Req->argv[0], ",");
584 if (Req->argc == 2) {
585 /* Forward to other server? */
586 target = Client_Search(Req->argv[1]);
587 if (! target || Client_Type(target) != CLIENT_SERVER)
588 return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
592 if (target != Client_ThisServer()) {
593 /* Target is indeed an other server, forward it! */
594 return IRC_WriteStrClientPrefix(target, from,
602 /* Loop through all the channels */
604 ngt_LowerStr(pattern);
605 chan = Channel_First();
607 /* Check search pattern */
608 if (MatchCaseInsensitive(pattern, Channel_Name(chan))) {
610 if (!Channel_HasMode(chan, 's')
611 || Channel_IsMemberOf(chan, from)
612 || (!Conf_MorePrivacy
613 && Client_HasMode(Client, 'o')
614 && Client_Conn(Client) > NONE))
616 if ((Conf_MaxListSize > 0)
617 && IRC_CheckListTooBig(from, count,
621 if (!IRC_WriteStrClient(from,
622 RPL_LIST_MSG, Client_ID(from),
624 Channel_MemberCount(chan),
625 Channel_Topic( chan )))
630 chan = Channel_Next(chan);
633 /* Get next name ... */
635 pattern = strtok(NULL, ",");
640 return IRC_WriteStrClient(from, RPL_LISTEND_MSG, Client_ID(from));
644 * Handler for the IRC+ "CHANINFO" command.
646 * @param Client The client from which this command has been received.
647 * @param Req Request structure with prefix and all parameters.
648 * @return CONNECTED or DISCONNECTED.
651 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
653 char modes_add[COMMAND_LEN], l[16];
658 assert( Client != NULL );
659 assert( Req != NULL );
661 /* Bad number of parameters? */
662 if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5)
663 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
664 Client_ID(Client), Req->command);
666 /* Compatibility kludge */
669 else if(Req->argc == 3)
674 _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
676 /* Search or create channel */
677 chan = Channel_Search( Req->argv[0] );
679 chan = Channel_Create( Req->argv[0] );
683 if (Req->argv[1][0] == '+') {
684 if (!*Channel_Modes(chan)) {
685 /* OK, this channel doesn't have modes yet,
686 * set the received ones: */
687 Channel_SetModes(chan, &Req->argv[1][1]);
690 if(Channel_HasMode(chan, 'k'))
691 Channel_SetKey(chan, Req->argv[2]);
692 if(Channel_HasMode(chan, 'l'))
693 Channel_SetMaxUsers(chan, atol(Req->argv[3]));
695 /* Delete modes which we never want to inherit */
696 Channel_ModeDel(chan, 'l');
697 Channel_ModeDel(chan, 'k');
700 strcpy(modes_add, "");
701 if (Channel_HasMode(chan, 'l')) {
702 snprintf(l, sizeof(l), " %lu",
703 Channel_MaxUsers(chan));
704 strlcat(modes_add, l, sizeof(modes_add));
706 if (Channel_HasMode(chan, 'k')) {
707 strlcat(modes_add, " ", sizeof(modes_add));
708 strlcat(modes_add, Channel_Key(chan),
712 /* Inform members of this channel */
713 IRC_WriteStrChannelPrefix(Client, chan, from, false,
714 "MODE %s +%s%s", Req->argv[0],
715 Channel_Modes(chan), modes_add);
719 Log(LOG_WARNING, "CHANINFO: invalid MODE format ignored!");
723 if (!*Channel_Topic(chan) && Req->argv[arg_topic][0]) {
724 /* OK, there is no topic jet */
725 Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
726 IRC_WriteStrChannelPrefix(Client, chan, from, false,
727 "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
731 /* Forward CHANINFO to other servers */
733 IRC_WriteStrServersPrefixFlag(Client, from, 'C',
734 "CHANINFO %s %s %s %s :%s",
735 Req->argv[0], Req->argv[1],
736 Req->argv[2], Req->argv[3],
738 else if (Req->argc == 3)
739 IRC_WriteStrServersPrefixFlag(Client, from, 'C',
740 "CHANINFO %s %s :%s",
741 Req->argv[0], Req->argv[1],
744 IRC_WriteStrServersPrefixFlag(Client, from, 'C',
746 Req->argv[0], Req->argv[1]);