X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc-channel.c;h=a7a273d88935776b3b008a6bd854ec7ca427f24a;hp=3f76452e48f93fafdf5a68b67686476d649e78fa;hb=2a7dd06ebd9cc72d45a6a4becdbef5213d7b7800;hpb=530112b114ffa7d5352c0733790ddf90253f41f9 diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c index 3f76452e..a7a273d8 100644 --- a/src/ngircd/irc-channel.c +++ b/src/ngircd/irc-channel.c @@ -1,20 +1,20 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2010 Alexander Barton (alex@barton.de) * * 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. - * - * IRC channel commands */ - #include "portab.h" -static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp $"; +/** + * @file + * IRC channel commands + */ #include "imp.h" #include @@ -24,8 +24,8 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp #include "defines.h" #include "conn.h" -#include "client.h" #include "channel.h" +#include "conn-func.h" #include "lists.h" #include "log.h" #include "match.h" @@ -33,7 +33,6 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp #include "parse.h" #include "irc-info.h" #include "irc-write.h" -#include "resolve.h" #include "conf.h" #include "exp.h" @@ -63,40 +62,75 @@ part_from_all_channels(CLIENT* client, CLIENT *target) } +/** + * Check weather a local client is allowed to join an already existing + * channel or not. + * @param Client Client that sent the JOIN command + * @param chan Channel to check + * @param channame Name of the channel + * @param key Provided channel key (or NULL if none has been provided) + * @return true if client is allowed to join channel, false otherwise + */ static bool -join_allowed(CLIENT *Client, CLIENT *target, CHANNEL *chan, - const char *channame, const char *key) +join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame, + const char *key) { bool is_invited, is_banned; const char *channel_modes; - is_banned = Lists_Check(Channel_GetListBans(chan), target); - is_invited = Lists_Check(Channel_GetListInvites(chan), target); + /* Allow IRC operators to overwrite channel limits */ + if (strchr(Client_Modes(Client), 'o')) + return true; + + is_banned = Lists_Check(Channel_GetListBans(chan), Client); + is_invited = Lists_Check(Channel_GetListInvites(chan), Client); if (is_banned && !is_invited) { - IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame); + /* Client is banned from channel (and not on invite list) */ + IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, + Client_ID(Client), channame); return false; } channel_modes = Channel_Modes(chan); - if ((strchr(channel_modes, 'i')) && !is_invited) { - /* Channel is "invite-only" (and Client wasn't invited) */ - IRC_WriteStrClient(Client, ERR_INVITEONLYCHAN_MSG, Client_ID(Client), channame); + if (strchr(channel_modes, 'i') && !is_invited) { + /* Channel is "invite-only" and client is not on invite list */ + IRC_WriteStrClient(Client, ERR_INVITEONLYCHAN_MSG, + Client_ID(Client), channame); return false; } - /* Is the channel protected by a key? */ - if (strchr(channel_modes, 'k') && - strcmp(Channel_Key(chan), key ? key : "")) - { - IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG, Client_ID(Client), channame); + if (!Channel_CheckKey(chan, Client, key ? key : "")) { + /* Channel is protected by a channel key and the client + * didn't specify the correct one */ + IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG, + Client_ID(Client), channame); + return false; + } + + if (strchr(channel_modes, 'l') && + (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) { + /* There are more clints joined to this channel than allowed */ + IRC_WriteStrClient(Client, ERR_CHANNELISFULL_MSG, + Client_ID(Client), channame); return false; } - /* Are there already too many members? */ - if ((strchr(channel_modes, 'l')) && (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) { - IRC_WriteStrClient(Client, ERR_CHANNELISFULL_MSG, Client_ID(Client), channame); + + if (strchr(channel_modes, 'z') && !Conn_UsesSSL(Client_Conn(Client))) { + /* Only "secure" clients are allowed, but clients doesn't + * use SSL encryption */ + IRC_WriteStrClient(Client, ERR_SECURECHANNEL_MSG, + Client_ID(Client), channame); + return false; + } + + if (strchr(channel_modes, 'O') && !Client_OperByMe(Client)) { + /* Only IRC operators are allowed! */ + IRC_WriteStrClient(Client, ERR_OPONLYCHANNEL_MSG, + Client_ID(Client), channame); return false; } + return true; } @@ -117,27 +151,60 @@ join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags) } +static void +cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data) +{ + CONN_ID conn; + char str[COMMAND_LEN], *ptr = NULL; + + strlcpy(str, (char *)Data, sizeof(str)); + conn = Client_Conn(To); + + if (Conn_Options(conn) & CONN_RFC1459) { + /* RFC 1459 compatibility mode, appended modes are NOT + * supported, so strip them off! */ + ptr = strchr(str, 0x7); + if (ptr) + *ptr++ = '\0'; + } + + IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str); + if (ptr && *ptr) + IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr, + Client_ID(Prefix)); +} /* cb_join_forward */ + + static void join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan, const char *channame) { - char modes[8]; + char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN]; strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1); - if (modes[1]) modes[0] = 0x7; else modes[0] = '\0'; - /* forward to other servers */ - IRC_WriteStrServersPrefix(Client, target, "JOIN :%s%s", channame, modes); + + /* forward to other servers (if it is not a local channel) */ + if (!Channel_IsLocal(chan)) { + snprintf(str, sizeof(str), "%s%s", channame, modes); + IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0', + cb_join_forward, str); + } /* tell users in this channel about the new client */ - IRC_WriteStrChannelPrefix(Client, chan, target, false, "JOIN :%s", channame); - if (modes[1]) - IRC_WriteStrChannelPrefix(Client, chan, target, false, "MODE %s +%s %s", - channame, &modes[1], Client_ID(target)); -} + IRC_WriteStrChannelPrefix(Client, chan, target, false, + "JOIN :%s", channame); + + /* syncronize channel modes */ + if (modes[1]) { + IRC_WriteStrChannelPrefix(Client, chan, target, false, + "MODE %s +%s %s", channame, + &modes[1], Client_ID(target)); + } +} /* join_forward */ static bool @@ -177,7 +244,7 @@ join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan, GLOBAL bool IRC_JOIN( CLIENT *Client, REQUEST *Req ) { - char *channame, *channame_ptr, *key, *key_ptr, *flags; + char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL; CLIENT *target; CHANNEL *chan; @@ -203,16 +270,16 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) return part_from_all_channels(Client, target); /* Are channel keys given? */ - if (Req->argc > 1) { - key = Req->argv[1]; - key_ptr = strchr(key, ','); - if (key_ptr) *key_ptr = '\0'; - } else { - key = key_ptr = NULL; - } + if (Req->argc > 1) + key = strtok_r(Req->argv[1], ",", &lastkey); + channame = Req->argv[0]; - channame_ptr = strchr(channame, ','); - if (channame_ptr) *channame_ptr = '\0'; + channame = strtok_r(channame, ",", &lastchan); + + /* Make sure that "channame" is not the empty string ("JOIN :") */ + if (! channame) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); while (channame) { flags = NULL; @@ -235,28 +302,35 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) /* Local client? */ if (Client_Type(Client) == CLIENT_USER) { - /* Test if the user has reached his maximum channel count */ - if ((Conf_MaxJoins > 0) && (Channel_CountForUser(Client) >= Conf_MaxJoins)) - return IRC_WriteStrClient(Client, ERR_TOOMANYCHANNELS_MSG, - Client_ID(Client), channame); - if (!chan) { - /* - * New Channel: first user will be channel operator - * unless this is a modeless channel... */ -#ifndef STRICT_RFC + /* Test if the user has reached the channel limit */ + if ((Conf_MaxJoins > 0) && + (Channel_CountForUser(Client) >= Conf_MaxJoins)) + return IRC_WriteStrClient(Client, + ERR_TOOMANYCHANNELS_MSG, + Client_ID(Client), channame); + if (chan) { + /* Already existing channel: check if the + * client is allowed to join */ + if (!join_allowed(Client, chan, channame, key)) + break; + } else { + /* New channel: first user will become channel + * operator unless this is a modeless channel */ if (*channame != '+') -#endif flags = "o"; - } else - if (!join_allowed(Client, target, chan, channame, key)) - break; + } + + /* Local client: update idle time */ + Conn_UpdateIdle(Client_Conn(Client)); } else { /* Remote server: we don't need to know whether the * client is invited or not, but we have to make sure * that the "one shot" entries (generated by INVITE * commands) in this list become deleted when a user * joins a channel this way. */ - if (chan) (void)Lists_Check(Channel_GetListInvites(chan), target); + if (chan) + (void)Lists_Check(Channel_GetListInvites(chan), + target); } /* Join channel (and create channel if it doesn't exist) */ @@ -266,7 +340,7 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) if (!chan) { /* channel is new; it has been created above */ chan = Channel_Search(channame); assert(chan != NULL); - if (*channame == '+') { /* modeless channel... */ + if (Channel_IsModeless(chan)) { Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */ Channel_ModeAdd(chan, 'n'); /* no external msgs */ } @@ -281,18 +355,9 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) break; /* write error */ /* next channel? */ - channame = channame_ptr; - if (channame) { - channame++; - channame_ptr = strchr(channame, ','); - if (channame_ptr) *channame_ptr = '\0'; - - if (key_ptr) { - key = ++key_ptr; - key_ptr = strchr(key, ','); - if (key_ptr) *key_ptr = '\0'; - } - } + channame = strtok_r(NULL, ",", &lastchan); + if (channame && key) + key = strtok_r(NULL, ",", &lastkey); } return CONNECTED; } /* IRC_JOIN */ @@ -325,11 +390,22 @@ IRC_PART(CLIENT * Client, REQUEST * Req) /* Loop over all the given channel names */ chan = strtok(Req->argv[0], ","); + + /* Make sure that "chan" is not the empty string ("PART :") */ + if (! chan) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + while (chan) { Channel_Part(target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID(target)); chan = strtok(NULL, ","); } + + /* Update idle time, if local client */ + if (Client_Conn(Client) > NONE) + Conn_UpdateIdle(Client_Conn(Client)); + return CONNECTED; } /* IRC_PART */ @@ -340,63 +416,87 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) CHANNEL *chan; CLIENT *from; char *topic; - bool r; + bool onchannel, topicok, use_servermode, r; assert( Client != NULL ); assert( Req != NULL ); - if ((Req->argc < 1) || (Req->argc > 2)) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); + if (Req->argc < 1 || Req->argc > 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); - else from = Client; - if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + if (Client_Type(Client) == CLIENT_SERVER) + from = Client_Search(Req->prefix); + else + from = Client; - /* Welcher Channel? */ - chan = Channel_Search( Req->argv[0] ); - if( ! chan ) return IRC_WriteStrClient( from, ERR_NOSUCHCHANNEL_MSG, Client_ID( from ), Req->argv[0] ); + if (!from) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); - /* Ist der User Mitglied in dem Channel? */ - if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] ); + chan = Channel_Search(Req->argv[0]); + if (!chan) + return IRC_WriteStrClient(from, ERR_NOSUCHCHANNEL_MSG, + Client_ID(from), Req->argv[0]); - if( Req->argc == 1 ) - { + Channel_CheckAdminRights(chan, Client, from, + &onchannel, &topicok, &use_servermode); + + if (!onchannel && !topicok) + return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, + Client_ID(from), Req->argv[0]); + + if (Req->argc == 1) { /* Request actual topic */ topic = Channel_Topic(chan); if (*topic) { r = IRC_WriteStrClient(from, RPL_TOPIC_MSG, - Client_ID(Client), Channel_Name(chan), topic); + Client_ID(Client), + Channel_Name(chan), topic); #ifndef STRICT_RFC r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG, - Client_ID(Client), Channel_Name(chan), - Channel_TopicWho(chan), - Channel_TopicTime(chan)); + Client_ID(Client), + Channel_Name(chan), + Channel_TopicWho(chan), + Channel_TopicTime(chan)); #endif return r; } else - return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG, - Client_ID(from), Channel_Name(chan)); + return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG, + Client_ID(from), + Channel_Name(chan)); } - if( strchr( Channel_Modes( chan ), 't' )) - { - /* Topic Lock. Ist der User ein Channel Operator? */ - if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan )); + if (strchr(Channel_Modes(chan), 't')) { + /* Topic Lock. Is the user a channel or IRC operator? */ + if (!topicok) + return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(from), + Channel_Name(chan)); } /* Set new topic */ Channel_SetTopic(chan, from, Req->argv[1]); - Log(LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s", - Client_Mask(from), Channel_Name(chan), - Req->argv[1][0] ? Req->argv[1] : ""); - - /* im Channel bekannt machen und an Server weiterleiten */ - IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - IRC_WriteStrChannelPrefix( Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - - if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - else return CONNECTED; + LogDebug("%s \"%s\" set topic on \"%s\": %s", + Client_TypeText(from), Client_Mask(from), Channel_Name(chan), + Req->argv[1][0] ? Req->argv[1] : ""); + + if (use_servermode) + from = Client_ThisServer(); + + /* Update channel and forward new topic to other servers */ + if (!Channel_IsLocal(chan)) + IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + + if (Client_Type(Client) == CLIENT_USER) + return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + else + return CONNECTED; } /* IRC_TOPIC */ @@ -451,7 +551,7 @@ IRC_LIST( CLIENT *Client, REQUEST *Req ) Req->argv[1] ); } } - + while( pattern ) { /* Loop through all the channels */ @@ -475,14 +575,14 @@ IRC_LIST( CLIENT *Client, REQUEST *Req ) } chan = Channel_Next( chan ); } - + /* Get next name ... */ if( Req->argc > 0 ) pattern = strtok( NULL, "," ); else pattern = NULL; } - + return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from )); } /* IRC_LIST */ @@ -551,7 +651,7 @@ IRC_CHANINFO( CLIENT *Client, REQUEST *Req ) } ptr++; } - + /* Inform members of this channel */ IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add ); }