X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fchannel.c;h=e74cd6eb1dfcc4358668a1c5ec18ec040471d697;hp=d1f9c6c9790a17748b1a37dbed6ae3bb24e69a84;hb=10c7ba99e73b42ec8c4c191ccdae60bdd35bba7d;hpb=2a7dd06ebd9cc72d45a6a4becdbef5213d7b7800 diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c index d1f9c6c9..e74cd6eb 100644 --- a/src/ngircd/channel.c +++ b/src/ngircd/channel.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors. + * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,41 +18,32 @@ * Channel management */ -#include "imp.h" #include #include #include #include #include #include +#include -#include "defines.h" #include "conn-func.h" -#include "exp.h" #include "channel.h" -#include "imp.h" #include "irc-write.h" #include "conf.h" #include "hash.h" -#include "lists.h" #include "log.h" #include "messages.h" #include "match.h" -#include "exp.h" - - #define REMOVE_PART 0 #define REMOVE_QUIT 1 #define REMOVE_KICK 2 - static CHANNEL *My_Channels; static CL2CHAN *My_Cl2Chan; - static CL2CHAN *Get_Cl2Chan PARAMS(( CHANNEL *Chan, CLIENT *Client )); static CL2CHAN *Add_Client PARAMS(( CHANNEL *Chan, CLIENT *Client )); static bool Remove_Client PARAMS(( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const char *Reason, bool InformServer )); @@ -66,16 +57,8 @@ static void Set_KeyFile PARAMS((CHANNEL *Chan, const char *KeyFile)); GLOBAL void Channel_Init( void ) { - CHANNEL *sc; - My_Channels = NULL; My_Cl2Chan = NULL; - - sc = Channel_Create("&SERVER"); - if (sc) { - Channel_SetModes(sc, "mnPt"); - Channel_SetTopic(sc, Client_ThisServer(), "Server Messages"); - } } /* Channel_Init */ @@ -87,6 +70,14 @@ Channel_GetListBans(CHANNEL *c) } +GLOBAL struct list_head * +Channel_GetListExcepts(CHANNEL *c) +{ + assert(c != NULL); + return &c->list_excepts; +} + + GLOBAL struct list_head * Channel_GetListInvites(CHANNEL *c) { @@ -95,11 +86,12 @@ Channel_GetListInvites(CHANNEL *c) } +/** + * Generate predefined persistent channels and &SERVER + */ GLOBAL void Channel_InitPredefined( void ) { - /* Generate predefined persistent channels */ - CHANNEL *new_chan; const struct Conf_Channel *conf_chan; const char *c; @@ -110,9 +102,12 @@ Channel_InitPredefined( void ) assert(channel_count == 0 || conf_chan != NULL); for (i = 0; i < channel_count; i++, conf_chan++) { - if (!conf_chan->name[0] || !Channel_IsValidName(conf_chan->name)) { - Log(LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"", - conf_chan->name); + if (!conf_chan->name[0]) + continue; + if (!Channel_IsValidName(conf_chan->name)) { + Log(LOG_ERR, + "Can't create pre-defined channel: invalid name: \"%s\"", + conf_chan->name); continue; } @@ -127,11 +122,11 @@ Channel_InitPredefined( void ) new_chan = Channel_Create(conf_chan->name); if (!new_chan) { - Log(LOG_ERR, "Can't create pre-defined channel \"%s\"", + Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!", conf_chan->name); continue; } - Log(LOG_INFO, "Created pre-defined channel \"%s\"", + Log(LOG_INFO, "Created pre-defined channel \"%s\".", conf_chan->name); Channel_ModeAdd(new_chan, 'P'); @@ -149,6 +144,18 @@ Channel_InitPredefined( void ) } if (channel_count) array_free(&Conf_Channels); + + /* Make sure the local &SERVER channel exists */ + if (!Channel_Search("&SERVER")) { + new_chan = Channel_Create("&SERVER"); + if (new_chan) { + Channel_SetModes(new_chan, "mnPt"); + Channel_SetTopic(new_chan, Client_ThisServer(), + "Server Messages"); + } else + Log(LOG_ERR, "Failed to create \"&SERVER\" channel!"); + } else + LogDebug("Required channel \"&SERVER\" already exists, ok."); } /* Channel_InitPredefined */ @@ -158,6 +165,7 @@ Free_Channel(CHANNEL *chan) array_free(&chan->topic); array_free(&chan->keyfile); Lists_Free(&chan->list_bans); + Lists_Free(&chan->list_excepts); Lists_Free(&chan->list_invites); free(chan); @@ -206,7 +214,7 @@ Channel_Join( CLIENT *Client, const char *Name ) /* Check that the channel name is valid */ if (! Channel_IsValidName(Name)) { - IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG, + IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG, Client_ID(Client), Name); return false; } @@ -251,18 +259,21 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea /* Check that specified channel exists */ chan = Channel_Search(Name); if (!chan) { - IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG, + IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG, Client_ID(Client), Name); return false; } /* Check that the client is in the channel */ if (!Get_Cl2Chan(chan, Client)) { - IRC_WriteStrClient(Client, ERR_NOTONCHANNEL_MSG, + IRC_WriteErrClient(Client, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Name); return false; } + if (Conf_MorePrivacy) + Reason = ""; + /* Part client from channel */ if (!Remove_Client(REMOVE_PART, chan, Client, Origin, Reason, true)) return false; @@ -279,6 +290,7 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, const char *Reason ) { CHANNEL *chan; + bool can_kick = false; assert(Peer != NULL); assert(Target != NULL); @@ -288,9 +300,9 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, /* Check that channel exists */ chan = Channel_Search( Name ); - if( ! chan ) - { - IRC_WriteStrClient( Origin, ERR_NOSUCHCHANNEL_MSG, Client_ID( Origin ), Name ); + if (!chan) { + IRC_WriteErrClient(Origin, ERR_NOSUCHCHANNEL_MSG, + Client_ID(Origin), Name); return; } @@ -298,14 +310,7 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, Client_Type(Origin) != CLIENT_SERVICE) { /* Check that user is on the specified channel */ if (!Channel_IsMemberOf(chan, Origin)) { - IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, - Client_ID(Origin), Name); - return; - } - - /* Check if user has operator status */ - if (!strchr(Channel_UserModes(chan, Origin), 'o')) { - IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, + IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG, Client_ID(Origin), Name); return; } @@ -313,11 +318,60 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, /* Check that the client to be kicked is on the specified channel */ if (!Channel_IsMemberOf(chan, Target)) { - IRC_WriteStrClient(Origin, ERR_USERNOTINCHANNEL_MSG, + IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID(Origin), Client_ID(Target), Name ); return; } + if(Client_Type(Peer) == CLIENT_USER) { + /* Channel mode 'Q' and user mode 'q' on target: nobody but + * IRC Operators and servers can kick the target user */ + if ((Channel_HasMode(chan, 'Q') + || Client_HasMode(Target, 'q') + || Client_Type(Target) == CLIENT_SERVICE) + && !Client_HasMode(Origin, 'o')) { + IRC_WriteErrClient(Origin, ERR_KICKDENY_MSG, + Client_ID(Origin), Name, + Client_ID(Target)); + return; + } + + /* Check if client has the rights to kick target */ + + /* Owner can kick everyone */ + if (Channel_UserHasMode(chan, Peer, 'q')) + can_kick = true; + + /* Admin can't kick owner */ + else if (Channel_UserHasMode(chan, Peer, 'a') && + !Channel_UserHasMode(chan, Target, 'q')) + can_kick = true; + + /* Op can't kick owner | admin */ + else if (Channel_UserHasMode(chan, Peer, 'o') && + !Channel_UserHasMode(chan, Target, 'q') && + !Channel_UserHasMode(chan, Target, 'a')) + can_kick = true; + + /* Half Op can't kick owner | admin | op */ + else if (Channel_UserHasMode(chan, Peer, 'h') && + !Channel_UserHasMode(chan, Target, 'q') && + !Channel_UserHasMode(chan, Target, 'a') && + !Channel_UserHasMode(chan, Target, 'o')) + can_kick = true; + + /* IRC operators & IRCd with OperCanMode enabled + * can kick anyways regardless of privilege */ + else if(Client_HasMode(Origin, 'o') && Conf_OperCanMode) + can_kick = true; + + if(!can_kick) { + IRC_WriteErrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG, + Client_ID(Origin), Name); + return; + } + } + /* Kick Client from channel */ Remove_Client( REMOVE_KICK, chan, Target, Origin, Reason, true); } /* Channel_Kick */ @@ -331,6 +385,9 @@ Channel_Quit( CLIENT *Client, const char *Reason ) assert( Client != NULL ); assert( Reason != NULL ); + if (Conf_MorePrivacy) + Reason = ""; + IRC_WriteStrRelatedPrefix( Client, Client, false, "QUIT :%s", Reason ); c = My_Channels; @@ -343,20 +400,31 @@ Channel_Quit( CLIENT *Client, const char *Reason ) } /* Channel_Quit */ +/** + * Get number of channels this server knows and that are "visible" to + * the given client. If no client is given, all channels will be counted. + * + * @param Client The client to check or NULL. + * @return Number of channels visible to the client. + */ GLOBAL unsigned long -Channel_Count( void ) +Channel_CountVisible (CLIENT *Client) { CHANNEL *c; unsigned long count = 0; c = My_Channels; - while( c ) - { - count++; + while(c) { + if (Client) { + if (!Channel_HasMode(c, 's') + || Channel_IsMemberOf(c, Client)) + count++; + } else + count++; c = c->next; } return count; -} /* Channel_Count */ +} GLOBAL unsigned long @@ -414,6 +482,14 @@ Channel_Modes( CHANNEL *Chan ) } /* Channel_Modes */ +GLOBAL bool +Channel_HasMode( CHANNEL *Chan, char Mode ) +{ + assert( Chan != NULL ); + return strchr( Chan->modes, Mode ) != NULL; +} /* Channel_HasMode */ + + GLOBAL char * Channel_Key( CHANNEL *Chan ) { @@ -551,7 +627,7 @@ Channel_ModeAdd( CHANNEL *Chan, char Mode ) assert( Chan != NULL ); x[0] = Mode; x[1] = '\0'; - if( ! strchr( Chan->modes, x[0] )) + if( ! Channel_HasMode( Chan, x[0] )) { /* Channel does not have this mode yet, set it */ strlcat( Chan->modes, x, sizeof( Chan->modes )); @@ -660,6 +736,13 @@ Channel_UserModes( CHANNEL *Chan, CLIENT *Client ) } /* Channel_UserModes */ +GLOBAL bool +Channel_UserHasMode( CHANNEL *Chan, CLIENT *Client, char Mode ) +{ + return strchr(Channel_UserModes(Chan, Client), Mode) != NULL; +} /* Channel_UserHasMode */ + + GLOBAL bool Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client ) { @@ -768,12 +851,19 @@ Channel_SetMaxUsers(CHANNEL *Chan, unsigned long Count) } /* Channel_SetMaxUsers */ +/** + * Check if a client is allowed to send to a specific channel. + * + * @param Chan The channel to check. + * @param From The client that wants to send. + * @return true if the client is allowed to send, false otherwise. + */ static bool Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) { - bool is_member, has_voice, is_op; + bool is_member, has_voice, is_halfop, is_op, is_chanadmin, is_owner; - is_member = has_voice = is_op = false; + is_member = has_voice = is_halfop = is_op = is_chanadmin = is_owner = false; /* The server itself always can send messages :-) */ if (Client_ThisServer() == From) @@ -781,10 +871,16 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) if (Channel_IsMemberOf(Chan, From)) { is_member = true; - if (strchr(Channel_UserModes(Chan, From), 'v')) + if (Channel_UserHasMode(Chan, From, 'v')) has_voice = true; - if (strchr(Channel_UserModes(Chan, From), 'o')) + if (Channel_UserHasMode(Chan, From, 'h')) + is_halfop = true; + if (Channel_UserHasMode(Chan, From, 'o')) is_op = true; + if (Channel_UserHasMode(Chan, From, 'a')) + is_chanadmin = true; + if (Channel_UserHasMode(Chan, From, 'q')) + is_owner = true; } /* @@ -793,15 +889,22 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) * If channel mode n set: non-members cannot send to channel. * If channel mode m set: need voice. */ - if (strchr(Channel_Modes(Chan), 'n') && !is_member) + if (Channel_HasMode(Chan, 'n') && !is_member) + return false; + + if (Channel_HasMode(Chan, 'M') && !Client_HasMode(From, 'R') + && !Client_HasMode(From, 'o')) return false; - if (is_op || has_voice) + if (has_voice || is_halfop || is_op || is_chanadmin || is_owner) return true; - if (strchr(Channel_Modes(Chan), 'm')) + if (Channel_HasMode(Chan, 'm')) return false; + if (Lists_Check(&Chan->list_excepts, From)) + return true; + return !Lists_Check(&Chan->list_bans, From); } @@ -813,15 +916,20 @@ Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command, if (!Can_Send_To_Channel(Chan, From)) { if (! SendErrors) return CONNECTED; /* no error, see RFC 2812 */ - return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG, + if (Channel_HasMode(Chan, 'M')) + return IRC_WriteErrClient(From, ERR_NEEDREGGEDNICK_MSG, + Client_ID(From), Channel_Name(Chan)); + else + return IRC_WriteErrClient(From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID(From), Channel_Name(Chan)); } if (Client_Conn(From) > NONE) Conn_UpdateIdle(Client_Conn(From)); - return IRC_WriteStrChannelPrefix(Client, Chan, From, true, - "%s %s :%s", Command, Channel_Name(Chan), Text); + IRC_WriteStrChannelPrefix(Client, Chan, From, true, "%s %s :%s", + Command, Channel_Name(Chan), Text); + return CONNECTED; } @@ -961,6 +1069,9 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch Client_Mask( Client ), c->name, Client_ID(Origin), Reason); break; default: /* PART */ + if (Conf_MorePrivacy) + Reason = ""; + if (InformServer) IRC_WriteStrServersPrefix(Origin, Client, "PART %s :%s", c->name, Reason); @@ -977,7 +1088,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch } /* When channel is empty and is not pre-defined, delete */ - if( ! strchr( Channel_Modes( Chan ), 'P' )) + if( ! Channel_HasMode( Chan, 'P' )) { if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan ); } @@ -990,8 +1101,17 @@ GLOBAL bool Channel_AddBan(CHANNEL *c, const char *mask ) { struct list_head *h = Channel_GetListBans(c); - LogDebug("Adding \"%s\" to \"%s\" %s list", mask, Channel_Name(c), "ban"); - return Lists_Add(h, mask, false); + LogDebug("Adding \"%s\" to \"%s\" ban list", mask, Channel_Name(c)); + return Lists_Add(h, mask, false, NULL); +} + + +GLOBAL bool +Channel_AddExcept(CHANNEL *c, const char *mask ) +{ + struct list_head *h = Channel_GetListExcepts(c); + LogDebug("Adding \"%s\" to \"%s\" exception list", mask, Channel_Name(c)); + return Lists_Add(h, mask, false, NULL); } @@ -999,30 +1119,31 @@ GLOBAL bool Channel_AddInvite(CHANNEL *c, const char *mask, bool onlyonce) { struct list_head *h = Channel_GetListInvites(c); - LogDebug("Adding \"%s\" to \"%s\" %s list", mask, Channel_Name(c), "invite"); - return Lists_Add(h, mask, onlyonce); + LogDebug("Adding \"%s\" to \"%s\" invite list", mask, Channel_Name(c)); + return Lists_Add(h, mask, onlyonce, NULL); } static bool -ShowInvitesBans(struct list_head *head, CLIENT *Client, CHANNEL *Channel, bool invite) +ShowChannelList(struct list_head *head, CLIENT *Client, CHANNEL *Channel, + char *msg, char *msg_end) { struct list_elem *e; - char *msg = invite ? RPL_INVITELIST_MSG : RPL_BANLIST_MSG; - char *msg_end; - assert( Client != NULL ); - assert( Channel != NULL ); + assert (Client != NULL); + assert (Channel != NULL); e = Lists_GetFirst(head); while (e) { - if( ! IRC_WriteStrClient( Client, msg, Client_ID( Client ), - Channel_Name( Channel ), Lists_GetMask(e) )) return DISCONNECTED; + if (!IRC_WriteStrClient(Client, msg, Client_ID(Client), + Channel_Name(Channel), + Lists_GetMask(e))) + return DISCONNECTED; e = Lists_GetNext(e); } - msg_end = invite ? RPL_ENDOFINVITELIST_MSG : RPL_ENDOFBANLIST_MSG; - return IRC_WriteStrClient( Client, msg_end, Client_ID( Client ), Channel_Name( Channel )); + return IRC_WriteStrClient(Client, msg_end, Client_ID(Client), + Channel_Name(Channel)); } @@ -1034,7 +1155,21 @@ Channel_ShowBans( CLIENT *Client, CHANNEL *Channel ) assert( Channel != NULL ); h = Channel_GetListBans(Channel); - return ShowInvitesBans(h, Client, Channel, false); + return ShowChannelList(h, Client, Channel, RPL_BANLIST_MSG, + RPL_ENDOFBANLIST_MSG); +} + + +GLOBAL bool +Channel_ShowExcepts( CLIENT *Client, CHANNEL *Channel ) +{ + struct list_head *h; + + assert( Channel != NULL ); + + h = Channel_GetListExcepts(Channel); + return ShowChannelList(h, Client, Channel, RPL_EXCEPTLIST_MSG, + RPL_ENDOFEXCEPTLIST_MSG); } @@ -1046,7 +1181,8 @@ Channel_ShowInvites( CLIENT *Client, CHANNEL *Channel ) assert( Channel != NULL ); h = Channel_GetListInvites(Channel); - return ShowInvitesBans(h, Client, Channel, true); + return ShowChannelList(h, Client, Channel, RPL_INVITELIST_MSG, + RPL_ENDOFINVITELIST_MSG); } @@ -1080,12 +1216,12 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key) assert(Client != NULL); assert(Key != NULL); - if (!strchr(Chan->modes, 'k')) - return true; - if (strcmp(Chan->key, Key) == 0) + if (!Channel_HasMode(Chan, 'k')) return true; if (*Key == '\0') return false; + if (strcmp(Chan->key, Key) == 0) + return true; file_name = array_start(&Chan->keyfile); if (!file_name) @@ -1120,64 +1256,6 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key) } /* Channel_CheckKey */ -/** - * Check wether a client is allowed to administer a channel or not. - * - * @param Chan The channel to test. - * @param Client The client from which the command has been received. - * @param Origin The originator of the command (or NULL). - * @param OnChannel Set to true if the originator is member of the channel. - * @param AdminOk Set to true if the client is allowed to do - * administrative tasks on this channel. - * @param UseServerMode Set to true if ngIRCd should emulate "server mode", - * that is send commands as if originating from a server - * and not the originator of the command. - */ -GLOBAL void -Channel_CheckAdminRights(CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, - bool *OnChannel, bool *AdminOk, bool *UseServerMode) -{ - assert (Chan != NULL); - assert (Client != NULL); - assert (OnChannel != NULL); - assert (AdminOk != NULL); - assert (UseServerMode != NULL); - - /* Use the client as origin, if no origin has been given (no prefix?) */ - if (!Origin) - Origin = Client; - - *OnChannel = false; - *AdminOk = false; - *UseServerMode = false; - - if (Client_Type(Client) != CLIENT_USER - && Client_Type(Client) != CLIENT_SERVER - && Client_Type(Client) != CLIENT_SERVICE) - return; - - /* Allow channel administration if the client is a server or service */ - if (Client_Type(Client) != CLIENT_USER) { - *AdminOk = true; - return; - } - - *OnChannel = Channel_IsMemberOf(Chan, Origin); - - if (*OnChannel && strchr(Channel_UserModes(Chan, Origin), 'o')) { - /* User is a channel operator */ - *AdminOk = true; - } else if (Conf_OperCanMode) { - /* IRC operators are allowed to administer channels as well */ - if (Client_OperByMe(Origin)) { - *AdminOk = true; - if (Conf_OperServerMode) - *UseServerMode = true; - } - } -} /* Channel_CheckAdminRights */ - - static CL2CHAN * Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan ) {