X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd.git;a=blobdiff_plain;f=src%2Fngircd%2Fchannel.c;h=90d2efab3e191f7ef1f3931bb1f2de6e90e5837d;hp=0fa38debc2c1d56e60f9477798076a2ed7ffc10d;hb=7b01bb833f5bc3386611dc0c015a99c236e941f6;hpb=cc336b75580d012c1caee58a252d1ad14b2cee23 diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c index 0fa38deb..90d2efab 100644 --- a/src/ngircd/channel.c +++ b/src/ngircd/channel.c @@ -1,22 +1,23 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2009 Alexander Barton (alex@barton.de) + * Copyright (c)2001-2012 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. - * - * Channel management */ - #define __channel_c__ - #include "portab.h" +/** + * @file + * Channel management + */ + #include "imp.h" #include #include @@ -27,7 +28,6 @@ #include "defines.h" #include "conn-func.h" -#include "client.h" #include "exp.h" #include "channel.h" @@ -87,6 +87,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) { @@ -110,9 +118,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; } @@ -158,6 +169,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); @@ -263,6 +275,9 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea return false; } + if (Conf_MorePrivacy) + Reason = ""; + /* Part client from channel */ if (!Remove_Client(REMOVE_PART, chan, Client, Origin, Reason, true)) return false; @@ -279,6 +294,8 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, const char *Reason ) { CHANNEL *chan; + char *ptr, *target_modes; + bool can_kick = false; assert(Peer != NULL); assert(Target != NULL); @@ -299,14 +316,51 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, /* Check that user is on the specified channel */ if (!Channel_IsMemberOf(chan, Origin)) { IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, - Client_ID(Origin), Name); + Client_ID(Origin), Name); return; } + } - /* Check if user has operator status */ - if (!strchr(Channel_UserModes(chan, Origin), 'o')) { - IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, - Client_ID(Origin), Name); + if(Client_Type(Peer) == CLIENT_USER) { + /* Check if client has the rights to kick target */ + ptr = Channel_UserModes(chan, Peer); + target_modes = Channel_UserModes(chan, Target); + while(*ptr) { + /* Owner can kick everyone */ + if ( *ptr == 'q') { + can_kick = true; + break; + } + /* Admin can't kick owner */ + if ( *ptr == 'a' ) { + if (!strchr(target_modes, 'q')) { + can_kick = true; + break; + } + } + /* Op can't kick owner | admin */ + if ( *ptr == 'o' ) { + if (!strchr(target_modes, 'q') && + !strchr(target_modes, 'a')) { + can_kick = true; + break; + } + } + /* Half Op can't kick owner | admin | op */ + if ( *ptr == 'h' ) { + if (!strchr(target_modes, 'q') && + !strchr(target_modes, 'a') && + !strchr(target_modes, 'o')) { + can_kick = true; + break; + } + } + ptr++; + } + + if(!can_kick) { + IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOLOW_MSG, + Client_ID(Origin), Name); return; } } @@ -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 (!strchr(Channel_Modes(c), 's') + || Channel_IsMemberOf(c, Client)) + count++; + } else + count++; c = c->next; } return count; -} /* Channel_Count */ +} GLOBAL unsigned long @@ -698,6 +766,14 @@ Channel_TopicWho(CHANNEL *Chan) return Chan->topic_who; } /* Channel_TopicWho */ + +GLOBAL unsigned int +Channel_CreationTime(CHANNEL *Chan) +{ + assert(Chan != NULL); + return (unsigned int) Chan->creation_time; +} /* Channel_CreationTime */ + #endif @@ -760,12 +836,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) @@ -775,8 +858,14 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) is_member = true; if (strchr(Channel_UserModes(Chan, From), 'v')) has_voice = true; + if (strchr(Channel_UserModes(Chan, From), 'h')) + is_halfop = true; if (strchr(Channel_UserModes(Chan, From), 'o')) is_op = true; + if (strchr(Channel_UserModes(Chan, From), 'a')) + is_chanadmin = true; + if (strchr(Channel_UserModes(Chan, From), 'q')) + is_owner = true; } /* @@ -788,12 +877,15 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) if (strchr(Channel_Modes(Chan), 'n') && !is_member) 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')) return false; + if (Lists_Check(&Chan->list_excepts, From)) + return true; + return !Lists_Check(&Chan->list_bans, From); } @@ -835,6 +927,9 @@ Channel_Create( const char *Name ) strlcpy( c->name, Name, sizeof( c->name )); c->hash = Hash( c->name ); c->next = My_Channels; +#ifndef STRICT_RFC + c->creation_time = time(NULL); +#endif My_Channels = c; LogDebug("Created new channel structure for \"%s\".", Name); return c; @@ -950,6 +1045,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); @@ -979,7 +1077,17 @@ GLOBAL bool Channel_AddBan(CHANNEL *c, const char *mask ) { struct list_head *h = Channel_GetListBans(c); - 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); } @@ -987,29 +1095,31 @@ GLOBAL bool Channel_AddInvite(CHANNEL *c, const char *mask, bool onlyonce) { struct list_head *h = Channel_GetListInvites(c); - 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)); } @@ -1021,7 +1131,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); } @@ -1033,7 +1157,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); } @@ -1069,10 +1194,10 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key) if (!strchr(Chan->modes, 'k')) return true; - if (strcmp(Chan->key, Key) == 0) - return true; if (*Key == '\0') return false; + if (strcmp(Chan->key, Key) == 0) + return true; file_name = array_start(&Chan->keyfile); if (!file_name)