2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2005 Alexander Barton (alex@barton.de)
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.
11 * IRC commands for mode changes (MODE, AWAY, ...)
17 static char UNUSED id[] = "$Id: irc-mode.c,v 1.48 2006/12/07 17:57:20 fw Exp $";
29 #include "irc-write.h"
41 static bool Client_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target ));
42 static bool Channel_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel ));
44 static bool Add_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern ));
45 static bool Add_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern ));
47 static bool Del_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern ));
48 static bool Del_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern ));
50 static bool Send_ListChange PARAMS(( char *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Mask ));
54 IRC_MODE( CLIENT *Client, REQUEST *Req )
59 assert( Client != NULL );
60 assert( Req != NULL );
63 if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
65 /* Origin for answers */
66 if( Client_Type( Client ) == CLIENT_SERVER )
68 origin = Client_Search( Req->prefix );
69 if( ! origin ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
73 /* Channel or user mode? */
74 cl = NULL; chan = NULL;
75 if (Client_IsValidNick(Req->argv[0]))
76 cl = Client_Search(Req->argv[0]);
77 if (Channel_IsValidName(Req->argv[0]))
78 chan = Channel_Search(Req->argv[0]);
81 return Client_Mode(Client, Req, origin, cl);
83 return Channel_Mode(Client, Req, origin, chan);
85 /* No target found! */
86 return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
87 Client_ID(Client), Req->argv[0]);
92 Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
94 /* Handle client mode requests */
96 char the_modes[COMMAND_LEN], x[2], *mode_ptr;
101 /* Is the client allowed to request or change the modes? */
102 if( Client_Type( Client ) == CLIENT_USER )
104 /* Users are only allowed to manipulate their own modes! */
105 if( Target != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client ));
108 /* Mode request: let's answer it :-) */
109 if( Req->argc == 1 ) return IRC_WriteStrClient( Origin, RPL_UMODEIS_MSG, Client_ID( Origin ), Client_Modes( Target ));
112 mode_ptr = Req->argv[mode_arg];
114 /* Initial state: set or unset modes? */
115 if( *mode_ptr == '+' ) set = true;
116 else if( *mode_ptr == '-' ) set = false;
117 else return IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Origin ));
119 /* Prepare reply string */
120 if( set ) strcpy( the_modes, "+" );
121 else strcpy( the_modes, "-" );
130 /* Try next argument if there's any */
132 if( mode_arg < Req->argc ) mode_ptr = Req->argv[mode_arg];
140 if((( *mode_ptr == '+' ) && ( ! set )) || (( *mode_ptr == '-' ) && ( set )))
142 /* Action modifier ("+"/"-") must be changed ... */
143 len = strlen( the_modes ) - 1;
144 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' ))
146 /* Adjust last action modifier in result */
147 the_modes[len] = *mode_ptr;
151 /* Append modifier character to result string */
153 strlcat( the_modes, x, sizeof( the_modes ));
155 if( *mode_ptr == '+' ) set = true;
165 case 'i': /* Invisible */
166 case 's': /* Server messages */
171 if( Client_Type( Client ) == CLIENT_SERVER )
174 Client_SetAway( Origin, DEFAULT_AWAY_MSG );
176 else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
179 case 'o': /* IRC operator (only unsettable!) */
180 if(( ! set ) || ( Client_Type( Client ) == CLIENT_SERVER ))
182 Client_SetOperByMe( Target, false );
185 else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
188 case 'r': /* Restricted (only settable) */
189 if(( set ) || ( Client_Type( Client ) == CLIENT_SERVER )) x[0] = 'r';
190 else ok = IRC_WriteStrClient( Origin, ERR_RESTRICTED_MSG, Client_ID( Origin ));
194 Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Origin ));
195 if( Client_Type( Client ) != CLIENT_SERVER ) ok = IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Origin ), set ? '+' : '-', *mode_ptr );
201 /* Is there a valid mode change? */
202 if( ! x[0] ) continue;
207 if( Client_ModeAdd( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
213 if( Client_ModeDel( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
218 /* Are there changed modes? */
221 /* Remoce needless action modifier characters */
222 len = strlen( the_modes ) - 1;
223 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' )) the_modes[len] = '\0';
225 if( Client_Type( Client ) == CLIENT_SERVER )
227 /* Forward modes to other servers */
228 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
232 /* Send reply to client and inform other servers */
233 ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
234 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
236 Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( Target ), Client_Modes( Target ));
239 IRC_SetPenalty( Client, 1 );
245 Channel_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel )
247 /* Handle channel and channel-user modes */
249 char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2], argadd[CLIENT_PASS_LEN], *mode_ptr;
250 bool ok, set, modeok = false, skiponce, use_servermode = false;
251 int mode_arg, arg_arg;
256 /* Mode request: let's answer it :-) */
259 /* Member or not? -- That's the question! */
260 if( ! Channel_IsMemberOf( Channel, Origin )) return IRC_WriteStrClient( Origin, RPL_CHANNELMODEIS_MSG, Client_ID( Origin ), Channel_Name( Channel ), Channel_Modes( Channel ));
262 /* The sender is a member: generate extended reply */
263 strlcpy( the_modes, Channel_Modes( Channel ), sizeof( the_modes ));
264 mode_ptr = the_modes;
271 snprintf( argadd, sizeof( argadd ), " %lu", Channel_MaxUsers( Channel ));
272 strlcat( the_args, argadd, sizeof( the_args ));
275 strlcat( the_args, " ", sizeof( the_args ));
276 strlcat( the_args, Channel_Key( Channel ), sizeof( the_args ));
281 if( the_args[0] ) strlcat( the_modes, the_args, sizeof( the_modes ));
283 return IRC_WriteStrClient( Origin, RPL_CHANNELMODEIS_MSG, Client_ID( Origin ), Channel_Name( Channel ), the_modes );
286 /* Is the user allowed to change modes? */
287 if( Client_Type( Client ) == CLIENT_USER )
289 /* Is the originating user on that channel? */
290 if( ! Channel_IsMemberOf( Channel, Origin )) return IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, Client_ID( Origin ), Channel_Name( Channel ));
292 /* Is he channel operator? */
293 if( strchr( Channel_UserModes( Channel, Origin ), 'o' )) modeok = true;
294 else if( Conf_OperCanMode )
296 /* IRC-Operators can use MODE as well */
297 if( Client_OperByMe( Origin )) {
299 if ( Conf_OperServerMode ) use_servermode = true; /* Change Origin to Server */
306 mode_ptr = Req->argv[mode_arg];
307 if( Req->argc > mode_arg + 1 ) arg_arg = mode_arg + 1;
310 /* Initial state: set or unset modes? */
312 if( *mode_ptr == '-' ) set = false;
313 else if( *mode_ptr == '+' ) set = true;
314 else set = skiponce = true;
316 /* Prepare reply string */
317 if( set ) strcpy( the_modes, "+" );
318 else strcpy( the_modes, "-" );
319 strcpy( the_args, " " );
325 if( ! skiponce ) mode_ptr++;
328 /* Try next argument if there's any */
329 if( arg_arg > mode_arg ) mode_arg = arg_arg;
331 if( mode_arg < Req->argc ) mode_ptr = Req->argv[mode_arg];
333 if( Req->argc > mode_arg + 1 ) arg_arg = mode_arg + 1;
342 if((( *mode_ptr == '+' ) && ( ! set )) || (( *mode_ptr == '-' ) && ( set )))
344 /* Action modifier ("+"/"-") must be changed ... */
345 len = strlen( the_modes ) - 1;
346 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' ))
348 /* Adjust last action modifier in result */
349 the_modes[len] = *mode_ptr;
353 /* Append modifier character to result string */
355 strlcat( the_modes, x, sizeof( the_modes ));
357 if( *mode_ptr == '+' ) set = true;
363 /* Are there arguments left? */
364 if( arg_arg >= Req->argc ) arg_arg = -1;
372 /* --- Channel modes --- */
374 case 'i': /* Invite only */
375 case 'm': /* Moderated */
376 case 'n': /* Only members can write */
377 case 's': /* Secret channel */
378 case 't': /* Topic locked */
379 if( modeok ) x[0] = *mode_ptr;
380 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
383 case 'k': /* Channel key */
386 if( modeok ) x[0] = *mode_ptr;
387 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
390 if( arg_arg > mode_arg )
394 Channel_ModeDel( Channel, 'k' );
395 Channel_SetKey( Channel, Req->argv[arg_arg] );
396 strlcpy( argadd, Channel_Key( Channel ), sizeof( argadd ));
399 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
400 Req->argv[arg_arg][0] = '\0';
403 else ok = IRC_WriteStrClient( Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID( Origin ), Req->command );
406 case 'l': /* Member limit */
409 if( modeok ) x[0] = *mode_ptr;
410 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
413 if( arg_arg > mode_arg )
417 l = atol( Req->argv[arg_arg] );
418 if( l > 0 && l < 0xFFFF )
420 Channel_ModeDel( Channel, 'l' );
421 Channel_SetMaxUsers( Channel, l );
422 snprintf( argadd, sizeof( argadd ), "%ld", l );
426 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
427 Req->argv[arg_arg][0] = '\0';
430 else ok = IRC_WriteStrClient( Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID( Origin ), Req->command );
433 case 'P': /* Persistent channel */
436 if( set && ( ! Client_OperByMe( Client )))
438 /* Only IRC operators are allowed to set P mode */
439 ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
443 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
446 /* --- Channel user modes --- */
448 case 'o': /* Channel operator */
449 case 'v': /* Voice */
450 if( arg_arg > mode_arg )
454 client = Client_Search( Req->argv[arg_arg] );
455 if( client ) x[0] = *mode_ptr;
456 else ok = IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[arg_arg] );
458 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
459 Req->argv[arg_arg][0] = '\0';
462 else ok = IRC_WriteStrClient( Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID( Origin ), Req->command );
465 /* --- Channel lists --- */
467 case 'I': /* Invite lists */
468 if( arg_arg > mode_arg )
473 if( set ) Add_Invite( Origin, Client, Channel, Req->argv[arg_arg] );
474 else Del_Invite( Origin, Client, Channel, Req->argv[arg_arg] );
476 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
477 Req->argv[arg_arg][0] = '\0';
480 else Channel_ShowInvites( Origin, Channel );
483 case 'b': /* Ban lists */
484 if( arg_arg > mode_arg )
489 if( set ) Add_Ban( Origin, Client, Channel, Req->argv[arg_arg] );
490 else Del_Ban( Origin, Client, Channel, Req->argv[arg_arg] );
492 else ok = IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Channel_Name( Channel ));
493 Req->argv[arg_arg][0] = '\0';
496 else Channel_ShowBans( Origin, Channel );
500 Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\" on %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Origin ), Channel_Name( Channel ));
501 if( Client_Type( Client ) != CLIENT_SERVER ) ok = IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Origin ), set ? '+' : '-', *mode_ptr );
507 /* Is there a valid mode change? */
508 if( ! x[0] ) continue;
510 /* Validate target client */
511 if( client && ( ! Channel_IsMemberOf( Channel, client )))
513 if( ! IRC_WriteStrClient( Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID( Origin ), Client_ID( client ), Channel_Name( Channel ))) break;
522 /* Channel-User-Mode */
523 if( Channel_UserModeAdd( Channel, client, x[0] ))
525 strlcat( the_args, Client_ID( client ), sizeof( the_args ));
526 strlcat( the_args, " ", sizeof( the_args ));
527 strlcat( the_modes, x, sizeof( the_modes ));
528 Log( LOG_DEBUG, "User \"%s\": Mode change on %s, now \"%s\"", Client_Mask( client ), Channel_Name( Channel ), Channel_UserModes( Channel, client ));
534 if( Channel_ModeAdd( Channel, x[0] ))
536 strlcat( the_modes, x, sizeof( the_modes ));
537 Log( LOG_DEBUG, "Channel %s: Mode change, now \"%s\".", Channel_Name( Channel ), Channel_Modes( Channel ));
546 /* Channel-User-Mode */
547 if( Channel_UserModeDel( Channel, client, x[0] ))
549 strlcat( the_args, Client_ID( client ), sizeof( the_args ));
550 strlcat( the_args, " ", sizeof( the_args ));
551 strlcat( the_modes, x, sizeof( the_modes ));
552 Log( LOG_DEBUG, "User \"%s\": Mode change on %s, now \"%s\"", Client_Mask( client ), Channel_Name( Channel ), Channel_UserModes( Channel, client ));
558 if( Channel_ModeDel( Channel, x[0] ))
560 strlcat( the_modes, x, sizeof( the_modes ));
561 Log( LOG_DEBUG, "Channel %s: Mode change, now \"%s\".", Channel_Name( Channel ), Channel_Modes( Channel ));
566 /* Are there additional arguments to add? */
569 len = strlen( the_args ) - 1;
570 if( the_args[len] != ' ' ) strlcat( the_args, " ", sizeof( the_args ));
571 strlcat( the_args, argadd, sizeof( the_args ));
576 /* Are there changed modes? */
579 /* Clean up mode string */
580 len = strlen( the_modes ) - 1;
581 if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' )) the_modes[len] = '\0';
583 /* Clean up argument string if there are none */
584 if( ! the_args[1] ) the_args[0] = '\0';
586 if( Client_Type( Client ) == CLIENT_SERVER )
588 /* Forward mode changes to channel users and other servers */
589 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args );
590 IRC_WriteStrChannelPrefix( Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args );
594 if ( use_servermode ) Origin = Client_ThisServer();
596 /* Send reply to client and inform other servers and channel users */
597 ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args );
598 IRC_WriteStrServersPrefix( Client, Origin, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args );
599 IRC_WriteStrChannelPrefix( Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args );
603 IRC_SetPenalty( Client, 1 );
609 IRC_AWAY( CLIENT *Client, REQUEST *Req )
611 assert( Client != NULL );
612 assert( Req != NULL );
614 /* Falsche Anzahl Parameter? */
615 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
617 if(( Req->argc == 1 ) && (Req->argv[0][0] ))
620 Client_SetAway( Client, Req->argv[0] );
621 Client_ModeAdd( Client, 'a' );
622 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
623 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
628 Client_ModeDel( Client, 'a' );
629 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
630 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
636 Add_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern )
641 assert( Client != NULL );
642 assert( Channel != NULL );
643 assert( Pattern != NULL );
645 mask = Lists_MakeMask( Pattern );
647 already = Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask );
649 if( ! Channel_AddInvite(Channel, mask, false ))
652 if ( already && ( Client_Type( Prefix ) == CLIENT_SERVER ))
655 return Send_ListChange( "+I", Prefix, Client, Channel, mask );
660 Add_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern )
665 assert( Client != NULL );
666 assert( Channel != NULL );
667 assert( Pattern != NULL );
669 mask = Lists_MakeMask( Pattern );
671 already = Lists_CheckDupeMask(Channel_GetListBans(Channel), mask );
673 if( ! Channel_AddBan(Channel, mask))
676 if ( already && ( Client_Type( Prefix ) == CLIENT_SERVER ))
679 return Send_ListChange( "+b", Prefix, Client, Channel, mask );
684 Del_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern )
688 assert( Client != NULL );
689 assert( Channel != NULL );
690 assert( Pattern != NULL );
692 mask = Lists_MakeMask( Pattern );
693 Lists_Del(Channel_GetListInvites(Channel), mask);
694 return Send_ListChange( "-I", Prefix, Client, Channel, mask );
699 Del_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Pattern )
703 assert( Client != NULL );
704 assert( Channel != NULL );
705 assert( Pattern != NULL );
707 mask = Lists_MakeMask( Pattern );
708 Lists_Del(Channel_GetListBans(Channel), mask);
709 return Send_ListChange( "-b", Prefix, Client, Channel, mask );
714 Send_ListChange( char *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, char *Mask )
716 /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
720 if( Client_Type( Client ) == CLIENT_USER )
722 /* Bestaetigung an Client */
723 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
727 /* an andere Server */
728 IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
730 /* und lokale User im Channel */
731 IRC_WriteStrChannelPrefix( Client, Channel, Prefix, false, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
734 } /* Send_ListChange */