X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fchannel.c;h=6b8d74530c7c07c71c532e2379387c97bf1596a3;hp=1b0e4424b2ff8882cbbfafd7b04aca65b0924b53;hb=8ae2cdfce9f8ba75fe72d65603d4c05c87f6d9c2;hpb=f7c2e8223f95fd984e7b96308905eef505c01680 diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c index 1b0e4424..6b8d7453 100644 --- a/src/ngircd/channel.c +++ b/src/ngircd/channel.c @@ -1,46 +1,45 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2013 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" -static char UNUSED id[] = "$Id: channel.c,v 1.65 2008/02/05 16:31:35 fw Exp $"; +/** + * @file + * Channel management + */ #include "imp.h" #include #include #include #include +#include #include #include "defines.h" #include "conn-func.h" -#include "client.h" #include "exp.h" #include "channel.h" #include "imp.h" #include "irc-write.h" -#include "resolve.h" #include "conf.h" #include "hash.h" #include "lists.h" #include "log.h" #include "messages.h" +#include "match.h" #include "exp.h" @@ -59,7 +58,9 @@ 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 )); static CL2CHAN *Get_First_Cl2Chan PARAMS(( CLIENT *Client, CHANNEL *Chan )); static CL2CHAN *Get_Next_Cl2Chan PARAMS(( CL2CHAN *Start, CLIENT *Client, CHANNEL *Chan )); -static bool Delete_Channel PARAMS(( CHANNEL *Chan )); +static void Delete_Channel PARAMS(( CHANNEL *Chan )); +static void Free_Channel PARAMS(( CHANNEL *Chan )); +static void Set_KeyFile PARAMS((CHANNEL *Chan, const char *KeyFile)); GLOBAL void @@ -78,6 +79,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) { @@ -86,124 +95,163 @@ Channel_GetListInvites(CHANNEL *c) } +/** + * Generate predefined persistent channels and &SERVER + */ GLOBAL void Channel_InitPredefined( void ) { - /* Vordefinierte persistente Channels erzeugen */ + CHANNEL *new_chan; + const struct Conf_Channel *conf_chan; + const char *c; + size_t i, channel_count = array_length(&Conf_Channels, sizeof(*conf_chan)); - CHANNEL *chan; - char *c; - unsigned int i; - - for( i = 0; i < Conf_Channel_Count; i++ ) - { - /* Ist ein Name konfiguriert? */ - if( ! Conf_Channel[i].name[0] ) continue; + conf_chan = array_start(&Conf_Channels); - /* Gueltiger Channel-Name? */ - if( ! Channel_IsValidName( Conf_Channel[i].name )) - { - Log( LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"!", Conf_Channel[i].name ); - array_free(&Conf_Channel[i].topic); + assert(channel_count == 0 || conf_chan != NULL); + + for (i = 0; i < channel_count; i++, conf_chan++) { + 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; } - /* Gibt es den Channel bereits? */ - chan = Channel_Search( Conf_Channel[i].name ); - if( chan ) - { - Log( LOG_INFO, "Can't create pre-defined channel \"%s\": name already in use.", Conf_Channel[i].name ); - array_free(&Conf_Channel[i].topic); + new_chan = Channel_Search(conf_chan->name); + if (new_chan) { + Log(LOG_INFO, + "Can't create pre-defined channel \"%s\": name already in use.", + conf_chan->name); + Set_KeyFile(new_chan, conf_chan->keyfile); continue; } - /* Create channel */ - chan = Channel_Create(Conf_Channel[i].name); - if (chan) { - Channel_ModeAdd(chan, 'P'); + new_chan = Channel_Create(conf_chan->name); + if (!new_chan) { + Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!", + conf_chan->name); + continue; + } + Log(LOG_INFO, "Created pre-defined channel \"%s\".", + conf_chan->name); - if (array_start(&Conf_Channel[i].topic) != NULL) - Channel_SetTopic(chan, NULL, - array_start(&Conf_Channel[i].topic)); - array_free(&Conf_Channel[i].topic); + Channel_ModeAdd(new_chan, 'P'); - c = Conf_Channel[i].modes; - while (*c) - Channel_ModeAdd(chan, *c++); + if (conf_chan->topic[0]) + Channel_SetTopic(new_chan, NULL, conf_chan->topic); - Channel_SetKey(chan, Conf_Channel[i].key); - Channel_SetMaxUsers(chan, Conf_Channel[i].maxusers); + c = conf_chan->modes; + while (*c) + Channel_ModeAdd(new_chan, *c++); - Log(LOG_INFO, "Created pre-defined channel \"%s\".", - Conf_Channel[i].name ); - } - else Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!", - Conf_Channel[i].name ); + Channel_SetKey(new_chan, conf_chan->key); + Channel_SetMaxUsers(new_chan, conf_chan->maxusers); + Set_KeyFile(new_chan, conf_chan->keyfile); } + 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 */ +static void +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); +} + + GLOBAL void Channel_Exit( void ) { CHANNEL *c, *c_next; CL2CHAN *cl2chan, *cl2chan_next; - /* Channel-Strukturen freigeben */ + /* free struct Channel */ c = My_Channels; - while( c ) - { + while (c) { c_next = c->next; - array_free(&c->topic); - free( c ); + Free_Channel(c); c = c_next; } - /* Channel-Zuordnungstabelle freigeben */ + /* Free Channel allocation table */ cl2chan = My_Cl2Chan; - while( c ) - { + while (cl2chan) { cl2chan_next = cl2chan->next; - free( cl2chan ); + free(cl2chan); cl2chan = cl2chan_next; } } /* Channel_Exit */ +/** + * Join Channel + * This function lets a client join a channel. First, the function + * checks that the specified channel name is valid and that the client + * isn't already a member. If the specified channel doesn't exist, + * a new channel is created. Client is added to channel by function + * Add_Client(). + */ GLOBAL bool -Channel_Join( CLIENT *Client, char *Name ) +Channel_Join( CLIENT *Client, const char *Name ) { CHANNEL *chan; - - assert( Client != NULL ); - assert( Name != NULL ); - if( ! Channel_IsValidName( Name )) { - IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name ); + assert(Client != NULL); + assert(Name != NULL); + + /* Check that the channel name is valid */ + if (! Channel_IsValidName(Name)) { + IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG, + Client_ID(Client), Name); return false; } - chan = Channel_Search( Name ); - if( chan ) { - /* Ist der Client bereits Mitglied? */ - if( Get_Cl2Chan( chan, Client )) return false; - } - else - { - /* Gibt es noch nicht? Dann neu anlegen: */ - chan = Channel_Create( Name ); - if (!chan) return false; + chan = Channel_Search(Name); + if(chan) { + /* Check if the client is already in the channel */ + if (Get_Cl2Chan(chan, Client)) + return false; + } else { + /* If the specified channel does not exist, the channel + * is now created */ + chan = Channel_Create(Name); + if (!chan) + return false; } - /* User dem Channel hinzufuegen */ - if( ! Add_Client( chan, Client )) return false; - else return true; + /* Add user to Channel */ + if (! Add_Client(chan, Client)) + return false; + + return true; } /* Channel_Join */ /** - * Remove client from channel. - * This function lets a client lead a channel. First, the function checks + * Part client from channel. + * This function lets a client part from a channel. First, the function checks * if the channel exists and the client is a member of it and sends out * appropriate error messages if not. The real work is done by the function * Remove_Client(). @@ -217,18 +265,25 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea assert(Name != NULL); assert(Reason != NULL); + /* 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; else @@ -236,55 +291,107 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea } /* Channel_Part */ +/** + * Kick user from Channel + */ GLOBAL void -Channel_Kick( CLIENT *Client, CLIENT *Origin, char *Name, char *Reason ) +Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, + const char *Reason ) { CHANNEL *chan; + bool can_kick = false; - assert( Client != NULL ); - assert( Origin != NULL ); - assert( Name != NULL ); - assert( Reason != NULL ); + assert(Peer != NULL); + assert(Target != NULL); + assert(Origin != NULL); + assert(Name != NULL); + assert(Reason != NULL); + /* 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; } - if( ! Channel_IsMemberOf( chan, Origin )) - { - IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, Client_ID( Origin ), Name ); - return; + if (Client_Type(Peer) != CLIENT_SERVER && + Client_Type(Origin) != CLIENT_SERVICE) { + /* Check that user is on the specified channel */ + if (!Channel_IsMemberOf(chan, Origin)) { + IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG, + Client_ID(Origin), Name); + return; + } } - /* Is User Channel-Operator? */ - if( ! strchr( Channel_UserModes( chan, Origin ), 'o' )) - { - IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Name); + /* Check that the client to be kicked is on the specified channel */ + if (!Channel_IsMemberOf(chan, Target)) { + IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG, + Client_ID(Origin), Client_ID(Target), Name ); return; } - /* Ist the kickED User member of channel? */ - if( ! Channel_IsMemberOf( chan, Client )) - { - IRC_WriteStrClient( Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID( Origin ), Client_ID( Client ), 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; + + if(!can_kick) { + IRC_WriteErrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG, + Client_ID(Origin), Name); + return; + } } - Remove_Client( REMOVE_KICK, chan, Client, Origin, Reason, true); + /* Kick Client from channel */ + Remove_Client( REMOVE_KICK, chan, Target, Origin, Reason, true); } /* Channel_Kick */ GLOBAL void -Channel_Quit( CLIENT *Client, char *Reason ) +Channel_Quit( CLIENT *Client, const char *Reason ) { CHANNEL *c, *next_c; assert( Client != NULL ); assert( Reason != NULL ); + if (Conf_MorePrivacy) + Reason = ""; + IRC_WriteStrRelatedPrefix( Client, Client, false, "QUIT :%s", Reason ); c = My_Channels; @@ -297,20 +404,31 @@ Channel_Quit( CLIENT *Client, 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 @@ -338,9 +456,9 @@ Channel_CountForUser( CLIENT *Client ) CL2CHAN *cl2chan; int count = 0; - + assert( Client != NULL ); - + cl2chan = My_Cl2Chan; while( cl2chan ) { @@ -352,7 +470,6 @@ Channel_CountForUser( CLIENT *Client ) } /* Channel_CountForUser */ - GLOBAL const char * Channel_Name( const CHANNEL *Chan ) { @@ -369,6 +486,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 ) { @@ -403,8 +528,8 @@ Channel_Next( CHANNEL *Chan ) GLOBAL CHANNEL * Channel_Search( const char *Name ) { - /* Channel-Struktur suchen */ - + /* Search channel structure */ + CHANNEL *c; UINT32 search_hash; @@ -416,7 +541,7 @@ Channel_Search( const char *Name ) { if( search_hash == c->hash ) { - /* lt. Hash-Wert: Treffer! */ + /* hash hit */ if( strcasecmp( Name, c->name ) == 0 ) return c; } c = c->next; @@ -480,7 +605,11 @@ Channel_IsValidName( const char *Name ) { assert( Name != NULL ); - if (strchr("+#", Name[0]) == NULL) +#ifdef STRICT_RFC + if (strlen(Name) <= 1) + return false; +#endif + if (strchr("#&+", Name[0]) == NULL) return false; if (strlen(Name) >= CHANNEL_NAME_LEN) return false; @@ -502,7 +631,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 )); @@ -611,6 +740,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 ) { @@ -631,7 +767,7 @@ Channel_Topic( CHANNEL *Chan ) return ret ? ret : ""; } /* Channel_Topic */ - + #ifndef STRICT_RFC GLOBAL unsigned int @@ -649,11 +785,19 @@ 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 GLOBAL void -Channel_SetTopic(CHANNEL *Chan, CLIENT *Client, char *Topic) +Channel_SetTopic(CHANNEL *Chan, CLIENT *Client, const char *Topic) { size_t len; assert( Chan != NULL ); @@ -681,7 +825,7 @@ Channel_SetTopic(CHANNEL *Chan, CLIENT *Client, char *Topic) GLOBAL void -Channel_SetModes( CHANNEL *Chan, char *Modes ) +Channel_SetModes( CHANNEL *Chan, const char *Modes ) { assert( Chan != NULL ); assert( Modes != NULL ); @@ -691,7 +835,7 @@ Channel_SetModes( CHANNEL *Chan, char *Modes ) GLOBAL void -Channel_SetKey( CHANNEL *Chan, char *Key ) +Channel_SetKey( CHANNEL *Chan, const char *Key ) { assert( Chan != NULL ); assert( Key != NULL ); @@ -711,19 +855,36 @@ 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) + return true; 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; } /* @@ -732,49 +893,52 @@ 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 (is_op || has_voice) - return true; - - if (strchr(Channel_Modes(Chan), 'm')) + if (Channel_HasMode(Chan, 'M') && !Client_HasMode(From, 'R') + && !Client_HasMode(From, 'o')) return false; - return !Lists_Check(&Chan->list_bans, From); -} - + if (has_voice || is_halfop || is_op || is_chanadmin || is_owner) + return true; -GLOBAL bool -Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text) -{ - if (!Can_Send_To_Channel(Chan, From)) - return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID(From), Channel_Name(Chan)); + if (Channel_HasMode(Chan, 'm')) + return false; - if (Client_Conn(From) > NONE) - Conn_UpdateIdle(Client_Conn(From)); + if (Lists_Check(&Chan->list_excepts, From)) + return true; - return IRC_WriteStrChannelPrefix(Client, Chan, From, true, - "PRIVMSG %s :%s", Channel_Name(Chan), Text); + return !Lists_Check(&Chan->list_bans, From); } GLOBAL bool -Channel_Notice(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text) +Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command, + bool SendErrors, const char *Text) { - if (!Can_Send_To_Channel(Chan, From)) - return true; /* no error, see RFC 2812 */ + if (!Can_Send_To_Channel(Chan, From)) { + if (! SendErrors) + return CONNECTED; /* no error, see RFC 2812 */ + 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, - "NOTICE %s :%s", Channel_Name(Chan), Text); + IRC_WriteStrChannelPrefix(Client, Chan, From, true, "%s %s :%s", + Command, Channel_Name(Chan), Text); + return CONNECTED; } GLOBAL CHANNEL * -Channel_Create( char *Name ) +Channel_Create( const char *Name ) { /* Create new CHANNEL structure and add it to linked list */ CHANNEL *c; @@ -791,6 +955,9 @@ Channel_Create( 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; @@ -823,7 +990,7 @@ Add_Client( CHANNEL *Chan, CLIENT *Client ) assert( Chan != NULL ); assert( Client != NULL ); - /* neue CL2CHAN-Struktur anlegen */ + /* Create new CL2CHAN structure */ cl2chan = (CL2CHAN *)malloc( sizeof( CL2CHAN )); if( ! cl2chan ) { @@ -834,11 +1001,11 @@ Add_Client( CHANNEL *Chan, CLIENT *Client ) cl2chan->client = Client; strcpy( cl2chan->modes, "" ); - /* Verketten */ + /* concatenate */ cl2chan->next = My_Cl2Chan; My_Cl2Chan = cl2chan; - Log( LOG_DEBUG, "User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name ); + LogDebug("User \"%s\" joined channel \"%s\".", Client_Mask(Client), Chan->name); return cl2chan; } /* Add_Client */ @@ -849,12 +1016,17 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch { CL2CHAN *cl2chan, *last_cl2chan; CHANNEL *c; - + assert( Chan != NULL ); assert( Client != NULL ); assert( Origin != NULL ); assert( Reason != NULL ); + /* Do not inform other servers if the channel is local to this server, + * regardless of what the caller requested! */ + if(InformServer) + InformServer = !Channel_IsLocal(Chan); + last_cl2chan = NULL; cl2chan = My_Cl2Chan; while( cl2chan ) @@ -868,7 +1040,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch c = cl2chan->channel; assert( c != NULL ); - /* Aus Verkettung loesen und freigeben */ + /* maintain cl2chan list */ if( last_cl2chan ) last_cl2chan->next = cl2chan->next; else My_Cl2Chan = cl2chan->next; free( cl2chan ); @@ -876,14 +1048,16 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch switch( Type ) { case REMOVE_QUIT: - /* QUIT: other servers have already been notified, see Client_Destroy(); - * so only inform other clients in same channel. */ + /* QUIT: other servers have already been notified, + * see Client_Destroy(); so only inform other clients + * in same channel. */ assert( InformServer == false ); LogDebug("User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason ); break; case REMOVE_KICK: - /* User was KICKed: inform other servers and all users in channel */ + /* User was KICKed: inform other servers (public + * channels) and all users in the channel */ if( InformServer ) IRC_WriteStrServersPrefix( Client_NextHop( Origin ), Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason); @@ -899,6 +1073,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); @@ -914,8 +1091,8 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch } } - /* Wenn Channel nun leer und nicht pre-defined: loeschen */ - if( ! strchr( Channel_Modes( Chan ), 'P' )) + /* When channel is empty and is not pre-defined, delete */ + if( ! Channel_HasMode( Chan, 'P' )) { if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan ); } @@ -928,7 +1105,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); } @@ -936,29 +1123,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)); } @@ -970,7 +1159,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); } @@ -982,10 +1185,81 @@ 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); } +/** + * Log a message to the local &SERVER channel, if it exists. + */ +GLOBAL void +Channel_LogServer(const char *msg) +{ + CHANNEL *sc; + CLIENT *c; + + assert(msg != NULL); + + sc = Channel_Search("&SERVER"); + if (!sc) + return; + + c = Client_ThisServer(); + Channel_Write(sc, c, c, "PRIVMSG", false, msg); +} /* Channel_LogServer */ + + +GLOBAL bool +Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key) +{ + char *file_name, line[COMMAND_LEN], *nick, *pass; + FILE *fd; + + assert(Chan != NULL); + assert(Client != NULL); + assert(Key != NULL); + + 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) + return false; + fd = fopen(file_name, "r"); + if (!fd) { + Log(LOG_ERR, "Can't open channel key file \"%s\" for %s: %s", + file_name, Chan->name, strerror(errno)); + return false; + } + + while (fgets(line, (int)sizeof(line), fd) != NULL) { + ngt_TrimStr(line); + if (! (nick = strchr(line, ':'))) + continue; + *nick++ = '\0'; + if (!Match(line, Client_User(Client))) + continue; + if (! (pass = strchr(nick, ':'))) + continue; + *pass++ = '\0'; + if (!Match(nick, Client_ID(Client))) + continue; + if (strcmp(Key, pass) != 0) + continue; + + fclose(fd); + return true; + } + fclose(fd); + return false; +} /* Channel_CheckKey */ + + static CL2CHAN * Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan ) { @@ -999,7 +1273,7 @@ Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel ) CL2CHAN *cl2chan; assert( Client != NULL || Channel != NULL ); - + cl2chan = Start; while( cl2chan ) { @@ -1011,36 +1285,64 @@ Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel ) } /* Get_Next_Cl2Chan */ -static bool -Delete_Channel( CHANNEL *Chan ) +/** + * Remove a channel and free all of its data structures. + */ +static void +Delete_Channel(CHANNEL *Chan) { - /* Channel-Struktur loeschen */ - CHANNEL *chan, *last_chan; last_chan = NULL; chan = My_Channels; - while( chan ) - { - if( chan == Chan ) break; + while (chan) { + if (chan == Chan) + break; last_chan = chan; chan = chan->next; } - if( ! chan ) return false; - - Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name ); - /* Invite- und Ban-Lists aufraeumen */ - Lists_Free( &chan->list_bans ); - Lists_Free( &chan->list_invites ); + assert(chan != NULL); + if (!chan) + return; - /* Neu verketten und freigeben */ - if( last_chan ) last_chan->next = chan->next; - else My_Channels = chan->next; - free( chan ); + /* maintain channel list */ + if (last_chan) + last_chan->next = chan->next; + else + My_Channels = chan->next; - return true; + LogDebug("Freed channel structure for \"%s\".", Chan->name); + Free_Channel(Chan); } /* Delete_Channel */ +static void +Set_KeyFile(CHANNEL *Chan, const char *KeyFile) +{ + size_t len; + + assert(Chan != NULL); + assert(KeyFile != NULL); + + len = strlen(KeyFile); + if (len < array_bytes(&Chan->keyfile)) { + Log(LOG_INFO, "Channel key file of %s removed.", Chan->name); + array_free(&Chan->keyfile); + } + + if (len < 1) + return; + + if (!array_copyb(&Chan->keyfile, KeyFile, len+1)) + Log(LOG_WARNING, + "Could not set new channel key file \"%s\" for %s: %s", + KeyFile, Chan->name, strerror(errno)); + else + Log(LOG_INFO|LOG_snotice, + "New local channel key file \"%s\" for %s activated.", + KeyFile, Chan->name); +} /* Set_KeyFile */ + + /* -eof- */