2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2011 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.
17 * IRC commands for mode changes (like MODE, AWAY, etc.)
29 #include "irc-write.h"
40 static bool Client_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin,
42 static bool Channel_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin,
45 static bool Add_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
46 CHANNEL *Channel, const char *Pattern));
47 static bool Del_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
48 CHANNEL *Channel, const char *Pattern));
50 static bool Send_ListChange PARAMS((const char *Mode, CLIENT *Prefix,
51 CLIENT *Client, CHANNEL *Channel, const char *Mask));
55 IRC_MODE( CLIENT *Client, REQUEST *Req )
60 assert( Client != NULL );
61 assert( Req != NULL );
64 if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
66 /* Origin for answers */
67 if( Client_Type( Client ) == CLIENT_SERVER )
69 origin = Client_Search( Req->prefix );
70 if( ! origin ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
74 /* Channel or user mode? */
75 cl = NULL; chan = NULL;
76 if (Client_IsValidNick(Req->argv[0]))
77 cl = Client_Search(Req->argv[0]);
78 if (Channel_IsValidName(Req->argv[0]))
79 chan = Channel_Search(Req->argv[0]);
82 return Client_Mode(Client, Req, origin, cl);
84 return Channel_Mode(Client, Req, origin, chan);
86 /* No target found! */
87 return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
88 Client_ID(Client), Req->argv[0]);
93 Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
95 /* Handle client mode requests */
97 char the_modes[COMMAND_LEN], x[2], *mode_ptr;
102 /* Is the client allowed to request or change the modes? */
103 if( Client_Type( Client ) == CLIENT_USER )
105 /* Users are only allowed to manipulate their own modes! */
106 if( Target != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client ));
109 /* Mode request: let's answer it :-) */
110 if( Req->argc == 1 ) return IRC_WriteStrClient( Origin, RPL_UMODEIS_MSG, Client_ID( Origin ), Client_Modes( Target ));
113 mode_ptr = Req->argv[mode_arg];
115 /* Initial state: set or unset modes? */
116 if( *mode_ptr == '+' ) set = true;
117 else if( *mode_ptr == '-' ) set = false;
118 else return IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Origin ));
120 /* Prepare reply string */
121 if( set ) strcpy( the_modes, "+" );
122 else strcpy( the_modes, "-" );
131 /* Try next argument if there's any */
133 if( mode_arg < Req->argc ) mode_ptr = Req->argv[mode_arg];
141 if((( *mode_ptr == '+' ) && ( ! set )) || (( *mode_ptr == '-' ) && ( set )))
143 /* Action modifier ("+"/"-") must be changed ... */
144 len = strlen( the_modes ) - 1;
145 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' ))
147 /* Adjust last action modifier in result */
148 the_modes[len] = *mode_ptr;
152 /* Append modifier character to result string */
154 strlcat( the_modes, x, sizeof( the_modes ));
156 if( *mode_ptr == '+' ) set = true;
166 case 'i': /* Invisible */
167 case 's': /* Server messages */
168 case 'w': /* Wallops messages */
173 if( Client_Type( Client ) == CLIENT_SERVER )
176 Client_SetAway( Origin, DEFAULT_AWAY_MSG );
178 else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
181 case 'c': /* Receive connect notices
182 * (only settable by IRC operators!) */
183 if(!set || Client_OperByMe(Origin)
184 || Client_Type(Client) == CLIENT_SERVER)
187 ok = IRC_WriteStrClient(Origin,
188 ERR_NOPRIVILEGES_MSG,
192 case 'o': /* IRC operator (only unsettable!) */
193 if(( ! set ) || ( Client_Type( Client ) == CLIENT_SERVER ))
195 Client_SetOperByMe( Target, false );
198 else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
201 case 'r': /* Restricted (only settable) */
202 if(( set ) || ( Client_Type( Client ) == CLIENT_SERVER )) x[0] = 'r';
203 else ok = IRC_WriteStrClient( Origin, ERR_RESTRICTED_MSG, Client_ID( Origin ));
206 case 'x': /* Cloak hostname */
207 if (Client_HasMode(Client, 'r'))
208 IRC_WriteStrClient(Origin,
216 Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Origin ));
217 if( Client_Type( Client ) != CLIENT_SERVER ) ok = IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Origin ), set ? '+' : '-', *mode_ptr );
223 /* Is there a valid mode change? */
224 if( ! x[0] ) continue;
229 if( Client_ModeAdd( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
235 if( Client_ModeDel( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
240 /* Are there changed modes? */
243 /* Remoce needless action modifier characters */
244 len = strlen( the_modes ) - 1;
245 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' )) the_modes[len] = '\0';
247 if( Client_Type( Client ) == CLIENT_SERVER )
249 /* Forward modes to other servers */
250 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
254 /* Send reply to client and inform other servers */
255 ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
256 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
258 LogDebug("%s \"%s\": Mode change, now \"%s\".",
259 Client_TypeText(Target), Client_Mask(Target),
260 Client_Modes(Target));
263 IRC_SetPenalty( Client, 1 );
269 Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
271 char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
272 const char *mode_ptr;
274 /* Member or not? -- That's the question! */
275 if (!Channel_IsMemberOf(Channel, Origin))
276 return IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
277 Client_ID(Origin), Channel_Name(Channel), Channel_Modes(Channel));
279 /* The sender is a member: generate extended reply */
280 strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
281 mode_ptr = the_modes;
287 snprintf(argadd, sizeof(argadd), " %lu", Channel_MaxUsers(Channel));
288 strlcat(the_args, argadd, sizeof(the_args));
291 strlcat(the_args, " ", sizeof(the_args));
292 strlcat(the_args, Channel_Key(Channel), sizeof(the_args));
298 strlcat(the_modes, the_args, sizeof(the_modes));
300 if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
301 Client_ID(Origin), Channel_Name(Channel),
305 if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
306 Client_ID(Origin), Channel_Name(Channel),
307 Channel_CreationTime(Channel)))
315 * Handle channel mode and channel-user mode changes
318 Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
320 char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
321 argadd[CLIENT_PASS_LEN], *mode_ptr;
322 bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
323 int mode_arg, arg_arg;
328 if (Channel_IsModeless(Channel))
329 return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
330 Client_ID(Client), Channel_Name(Channel));
332 /* Mode request: let's answer it :-) */
334 return Channel_Mode_Answer_Request(Origin, Channel);
336 Channel_CheckAdminRights(Channel, Client, Origin,
337 &onchannel, &modeok, &use_servermode);
339 if (!onchannel && !modeok)
340 return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
342 Channel_Name(Channel));
345 mode_ptr = Req->argv[mode_arg];
346 if (Req->argc > mode_arg + 1)
347 arg_arg = mode_arg + 1;
351 /* Initial state: set or unset modes? */
365 /* Prepare reply string */
366 strcpy(the_modes, set ? "+" : "-");
370 connected = CONNECTED;
375 /* Try next argument if there's any */
376 if (arg_arg > mode_arg)
381 if (mode_arg >= Req->argc)
383 mode_ptr = Req->argv[mode_arg];
385 if (Req->argc > mode_arg + 1)
386 arg_arg = mode_arg + 1;
395 if (((*mode_ptr == '+') && !set)
396 || ((*mode_ptr == '-') && set)) {
397 /* Action modifier ("+"/"-") must be changed ... */
398 len = strlen(the_modes) - 1;
399 if (the_modes[len] == '+' || the_modes[len] == '-') {
400 /* Adjust last action modifier in result */
401 the_modes[len] = *mode_ptr;
403 /* Append modifier character to result string */
405 strlcat(the_modes, x, sizeof(the_modes));
407 set = *mode_ptr == '+';
412 /* Are there arguments left? */
413 if (arg_arg >= Req->argc)
421 /* --- Channel modes --- */
422 case 'i': /* Invite only */
423 case 'm': /* Moderated */
424 case 'n': /* Only members can write */
425 case 's': /* Secret channel */
426 case 't': /* Topic locked */
427 case 'z': /* Secure connections only */
431 connected = IRC_WriteStrClient(Origin,
432 ERR_CHANOPRIVSNEEDED_MSG,
433 Client_ID(Origin), Channel_Name(Channel));
435 case 'k': /* Channel key */
440 connected = IRC_WriteStrClient(Origin,
441 ERR_CHANOPRIVSNEEDED_MSG,
443 Channel_Name(Channel));
446 if (arg_arg > mode_arg) {
448 Channel_ModeDel(Channel, 'k');
449 Channel_SetKey(Channel,
451 strlcpy(argadd, Channel_Key(Channel),
455 connected = IRC_WriteStrClient(Origin,
456 ERR_CHANOPRIVSNEEDED_MSG,
458 Channel_Name(Channel));
460 Req->argv[arg_arg][0] = '\0';
463 connected = IRC_WriteStrClient(Origin,
464 ERR_NEEDMOREPARAMS_MSG,
465 Client_ID(Origin), Req->command);
469 case 'l': /* Member limit */
474 connected = IRC_WriteStrClient(Origin,
475 ERR_CHANOPRIVSNEEDED_MSG,
477 Channel_Name(Channel));
480 if (arg_arg > mode_arg) {
482 l = atol(Req->argv[arg_arg]);
483 if (l > 0 && l < 0xFFFF) {
484 Channel_ModeDel(Channel, 'l');
485 Channel_SetMaxUsers(Channel, l);
486 snprintf(argadd, sizeof(argadd),
491 connected = IRC_WriteStrClient(Origin,
492 ERR_CHANOPRIVSNEEDED_MSG,
494 Channel_Name(Channel));
496 Req->argv[arg_arg][0] = '\0';
499 connected = IRC_WriteStrClient(Origin,
500 ERR_NEEDMOREPARAMS_MSG,
501 Client_ID(Origin), Req->command);
505 case 'O': /* IRC operators only */
507 /* Only IRC operators are allowed to
508 * set the 'O' channel mode! */
509 if (set && !(Client_OperByMe(Client)
510 || Client_Type(Client) == CLIENT_SERVER))
511 connected = IRC_WriteStrClient(Origin,
512 ERR_NOPRIVILEGES_MSG,
517 connected = IRC_WriteStrClient(Origin,
518 ERR_CHANOPRIVSNEEDED_MSG,
520 Channel_Name(Channel));
522 case 'P': /* Persistent channel */
524 /* Only IRC operators are allowed to
525 * set the 'P' channel mode! */
526 if (set && !(Client_OperByMe(Client)
527 || Client_Type(Client) == CLIENT_SERVER))
528 connected = IRC_WriteStrClient(Origin,
529 ERR_NOPRIVILEGES_MSG,
534 connected = IRC_WriteStrClient(Origin,
535 ERR_CHANOPRIVSNEEDED_MSG,
537 Channel_Name(Channel));
539 /* --- Channel user modes --- */
540 case 'o': /* Channel operator */
541 case 'v': /* Voice */
542 if (arg_arg > mode_arg) {
544 client = Client_Search(Req->argv[arg_arg]);
548 connected = IRC_WriteStrClient(Client,
553 connected = IRC_WriteStrClient(Origin,
554 ERR_CHANOPRIVSNEEDED_MSG,
556 Channel_Name(Channel));
558 Req->argv[arg_arg][0] = '\0';
561 connected = IRC_WriteStrClient(Origin,
562 ERR_NEEDMOREPARAMS_MSG,
563 Client_ID(Origin), Req->command);
567 /* --- Channel lists --- */
568 case 'I': /* Invite lists */
569 case 'b': /* Ban lists */
570 if (arg_arg > mode_arg) {
574 ? Add_Ban_Invite(*mode_ptr, Origin,
577 : Del_Ban_Invite(*mode_ptr, Origin,
581 connected = IRC_WriteStrClient(Origin,
582 ERR_CHANOPRIVSNEEDED_MSG,
584 Channel_Name(Channel));
586 Req->argv[arg_arg][0] = '\0';
589 if (*mode_ptr == 'I')
590 Channel_ShowInvites(Origin, Channel);
592 Channel_ShowBans(Origin, Channel);
597 "Unknown mode \"%c%c\" from \"%s\" on %s!?",
598 set ? '+' : '-', *mode_ptr, Client_ID(Origin),
599 Channel_Name(Channel));
600 if (Client_Type(Client) != CLIENT_SERVER)
601 connected = IRC_WriteStrClient(Origin,
602 ERR_UMODEUNKNOWNFLAG2_MSG,
604 set ? '+' : '-', *mode_ptr);
612 /* Is there a valid mode change? */
616 /* Validate target client */
617 if (client && (!Channel_IsMemberOf(Channel, client))) {
618 if (!IRC_WriteStrClient
619 (Origin, ERR_USERNOTINCHANNEL_MSG,
620 Client_ID(Origin), Client_ID(client),
621 Channel_Name(Channel)))
628 /* Channel-User-Mode */
630 ? Channel_UserModeAdd(Channel, client, x[0])
631 : Channel_UserModeDel(Channel, client, x[0]);
633 strlcat(the_args, " ", sizeof(the_args));
634 strlcat(the_args, Client_ID(client),
636 strlcat(the_modes, x, sizeof(the_modes));
638 ("User \"%s\": Mode change on %s, now \"%s\"",
639 Client_Mask(client), Channel_Name(Channel),
640 Channel_UserModes(Channel, client));
645 ? Channel_ModeAdd(Channel, x[0])
646 : Channel_ModeDel(Channel, x[0]);
648 strlcat(the_modes, x, sizeof(the_modes));
649 LogDebug("Channel %s: Mode change, now \"%s\".",
650 Channel_Name(Channel),
651 Channel_Modes(Channel));
655 /* Are there additional arguments to add? */
657 strlcat(the_args, " ", sizeof(the_args));
658 strlcat(the_args, argadd, sizeof(the_args));
663 /* Are there changed modes? */
665 /* Clean up mode string */
666 len = strlen(the_modes) - 1;
667 if ((the_modes[len] == '+') || (the_modes[len] == '-'))
668 the_modes[len] = '\0';
670 if (Client_Type(Client) == CLIENT_SERVER) {
671 /* MODE requests for local channels from other servers
672 * are definitely invalid! */
673 if (Channel_IsLocal(Channel)) {
674 Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
678 /* Forward mode changes to channel users and all the
679 * other remote servers: */
680 IRC_WriteStrServersPrefix(Client, Origin,
681 "MODE %s %s%s", Channel_Name(Channel),
682 the_modes, the_args);
683 IRC_WriteStrChannelPrefix(Client, Channel, Origin,
684 false, "MODE %s %s%s", Channel_Name(Channel),
685 the_modes, the_args);
688 Origin = Client_ThisServer();
689 /* Send reply to client and inform other servers and channel users */
690 connected = IRC_WriteStrClientPrefix(Client, Origin,
691 "MODE %s %s%s", Channel_Name(Channel),
692 the_modes, the_args);
693 /* Only forward requests for non-local channels */
694 if (!Channel_IsLocal(Channel))
695 IRC_WriteStrServersPrefix(Client, Origin,
696 "MODE %s %s%s", Channel_Name(Channel),
697 the_modes, the_args);
698 IRC_WriteStrChannelPrefix(Client, Channel, Origin,
699 false, "MODE %s %s%s", Channel_Name(Channel),
700 the_modes, the_args);
704 IRC_SetPenalty(Client, 1);
710 IRC_AWAY( CLIENT *Client, REQUEST *Req )
712 assert( Client != NULL );
713 assert( Req != NULL );
715 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
717 if(( Req->argc == 1 ) && (Req->argv[0][0] ))
719 Client_SetAway( Client, Req->argv[0] );
720 Client_ModeAdd( Client, 'a' );
721 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
722 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
726 Client_ModeDel( Client, 'a' );
727 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
728 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
734 Add_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
740 assert( Client != NULL );
741 assert( Channel != NULL );
742 assert( Pattern != NULL );
743 assert(what == 'I' || what == 'b');
745 mask = Lists_MakeMask(Pattern);
747 already = Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask);
750 ret = Channel_AddInvite(Channel, mask, false);
752 ret = Channel_AddBan(Channel, mask);
756 if (already && (Client_Type(Prefix) == CLIENT_SERVER))
760 return Send_ListChange("+I", Prefix, Client, Channel, mask);
761 return Send_ListChange("+b", Prefix, Client, Channel, mask);
766 Del_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
769 struct list_head *list;
771 assert( Client != NULL );
772 assert( Channel != NULL );
773 assert( Pattern != NULL );
774 assert(what == 'I' || what == 'b');
776 mask = Lists_MakeMask( Pattern );
779 list = Channel_GetListInvites(Channel);
781 list = Channel_GetListBans(Channel);
783 Lists_Del(list, mask);
785 return Send_ListChange( "-I", Prefix, Client, Channel, mask );
786 return Send_ListChange( "-b", Prefix, Client, Channel, mask );
791 Send_ListChange(const char *Mode, CLIENT *Prefix, CLIENT *Client,
792 CHANNEL *Channel, const char *Mask)
796 if( Client_Type( Client ) == CLIENT_USER )
798 /* send confirmation to client */
799 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
803 /* to other servers */
804 IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
806 /* and local users in channel */
807 IRC_WriteStrChannelPrefix( Client, Channel, Prefix, false, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
810 } /* Send_ListChange */