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=138a94ab2883f4fb875cc186762c37309d84e51f;hp=3ffd85a0196ce035cff2e73d848bbe733bf29954;hb=cc336b75580d012c1caee58a252d1ad14b2cee23;hpb=634ef8c13164321abeee37e9516cbfb71e0d0f2e diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c index 3ffd85a0..138a94ab 100644 --- a/src/ngircd/irc-channel.c +++ b/src/ngircd/irc-channel.c @@ -1,6 +1,6 @@ /* * 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 @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: irc-channel.c,v 1.44 2008/02/16 11:21:33 fw Exp $"; - #include "imp.h" #include #include @@ -26,6 +24,7 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.44 2008/02/16 11:21:33 fw Exp #include "conn.h" #include "client.h" #include "channel.h" +#include "conn-func.h" #include "lists.h" #include "log.h" #include "match.h" @@ -33,7 +32,6 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.44 2008/02/16 11:21:33 fw Exp #include "parse.h" #include "irc-info.h" #include "irc-write.h" -#include "resolve.h" #include "conf.h" #include "exp.h" @@ -63,40 +61,68 @@ 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; } - /* 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, '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; + } + + 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; } + return true; } @@ -117,27 +143,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 +236,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,27 +262,20 @@ 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; - chan = Channel_Search(channame); - if (!chan && Conf_PredefChannelsOnly) { - /* channel must be created, but server does not allow this */ - IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame); - break; - } - /* Did the server include channel-user-modes? */ if (Client_Type(Client) == CLIENT_SERVER) { flags = strchr(channame, 0x7); @@ -233,32 +285,58 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) } } + chan = Channel_Search(channame); + if (!chan && Conf_PredefChannelsOnly) { + /* channel must be created, but server does not allow this */ + IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame); + break; + } + /* 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 */ - flags = "o"; - else - if (!join_allowed(Client, target, chan, channame, key)) + /* 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 != '+') + flags = "o"; + } + + /* 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) */ if (!Channel_Join(target, channame)) break; - if (!chan) /* channel is new; it has been created above */ + if (!chan) { /* channel is new; it has been created above */ chan = Channel_Search(channame); + assert(chan != NULL); + if (Channel_IsModeless(chan)) { + Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */ + Channel_ModeAdd(chan, 'n'); /* no external msgs */ + } + } assert(chan != NULL); join_set_channelmodes(chan, target, flags); @@ -269,48 +347,57 @@ 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 */ +/** + * Handler for the IRC "PART" command. + */ GLOBAL bool -IRC_PART( CLIENT *Client, REQUEST *Req ) +IRC_PART(CLIENT * Client, REQUEST * Req) { CLIENT *target; char *chan; - assert( Client != NULL ); - assert( Req != NULL ); + 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); + Client_ID(Client), Req->command); - /* Wer ist der Absender? */ - if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix ); - else target = Client; - if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + /* Get the sender */ + if (Client_Type(Client) == CLIENT_SERVER) + target = Client_Search(Req->prefix); + else + target = Client; + if (!target) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); - /* Channel-Namen durchgehen */ + /* Loop over all the given channel names */ chan = strtok(Req->argv[0], ","); - while (chan) { - Channel_Part(target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID(target)); + /* 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 */ @@ -326,8 +413,8 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - /* Falsche Anzahl Parameter? */ - 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; @@ -368,16 +455,22 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) /* 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] : ""); + + /* 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 */ @@ -432,7 +525,7 @@ IRC_LIST( CLIENT *Client, REQUEST *Req ) Req->argv[1] ); } } - + while( pattern ) { /* Loop through all the channels */ @@ -456,14 +549,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 */ @@ -532,7 +625,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 ); }