X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc-info.c;h=883893fc5d2c4add0d9cf61a3ebb7e86dca84824;hp=301da5337d32430592cfe1fe9e88838064b1c232;hb=adf92302bf9bb6420acd1f1153515586794b1574;hpb=b80e115f3947eae39aba39d1647f0a81f3d95fa3 diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c index 301da533..883893fc 100644 --- a/src/ngircd/irc-info.c +++ b/src/ngircd/irc-info.c @@ -28,8 +28,10 @@ #include "conn-func.h" #include "conn-zip.h" #include "channel.h" +#include "class.h" #include "conf.h" #include "defines.h" +#include "lists.h" #include "log.h" #include "messages.h" #include "match.h" @@ -152,6 +154,15 @@ IRC_INFO(CLIENT * Client, REQUEST * Req) } /* IRC_INFO */ +/** + * Handler for the IRC "ISON" command. + * + * See RFC 2812, 4.9 "Ison message". + * + * @param Client The client from which this command has been received. + * @param Req Request structure with prefix and all parameters. + * @return CONNECTED or DISCONNECTED. + */ GLOBAL bool IRC_ISON( CLIENT *Client, REQUEST *Req ) { @@ -160,80 +171,103 @@ IRC_ISON( CLIENT *Client, REQUEST *Req ) char *ptr; int i; - assert( Client != NULL ); - assert( Req != NULL ); + assert(Client != NULL); + assert(Req != NULL); - /* Falsche Anzahl Parameter? */ - if(( Req->argc < 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + /* Bad number of arguments? */ + if (Req->argc < 1) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - strlcpy( rpl, RPL_ISON_MSG, sizeof rpl ); - for( i = 0; i < Req->argc; i++ ) - { - ptr = strtok( Req->argv[i], " " ); - while( ptr ) - { - ngt_TrimStr( ptr ); - c = Client_Search( ptr ); - if( c && ( Client_Type( c ) == CLIENT_USER )) - { - /* Dieser Nick ist "online" */ - strlcat( rpl, ptr, sizeof( rpl )); - strlcat( rpl, " ", sizeof( rpl )); + strlcpy(rpl, RPL_ISON_MSG, sizeof rpl); + for (i = 0; i < Req->argc; i++) { + /* "All" ircd even parse ": ..." arguments and split + * them up; so we do the same ... */ + ptr = strtok(Req->argv[i], " "); + while (ptr) { + ngt_TrimStr(ptr); + c = Client_Search(ptr); + if (c && Client_Type(c) == CLIENT_USER) { + strlcat(rpl, Client_ID(c), sizeof(rpl)); + strlcat(rpl, " ", sizeof(rpl)); } - ptr = strtok( NULL, " " ); + ptr = strtok(NULL, " "); } } ngt_TrimLastChr(rpl, ' '); - return IRC_WriteStrClient( Client, rpl, Client_ID( Client ) ); + return IRC_WriteStrClient(Client, rpl, Client_ID(Client)); } /* IRC_ISON */ +/** + * Handler for the IRC "LINKS" command. + * + * See RFC 2812, 3.4.5 "Links message". + * + * @param Client The client from which this command has been received. + * @param Req Request structure with prefix and all parameters. + * @return CONNECTED or DISCONNECTED. + */ GLOBAL bool -IRC_LINKS( CLIENT *Client, REQUEST *Req ) +IRC_LINKS(CLIENT *Client, REQUEST *Req) { CLIENT *target, *from, *c; char *mask; - assert( Client != NULL ); - assert( Req != NULL ); + assert(Client != NULL); + assert(Req != NULL); - if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + IRC_SetPenalty(Client, 1); - /* Server-Mask ermitteln */ - if( Req->argc > 0 ) mask = Req->argv[Req->argc - 1]; - else mask = "*"; + if (Req->argc > 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - /* Absender ermitteln */ - 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 ); + /* Get pointer to server mask or "*", if none given */ + if (Req->argc > 0) + mask = Req->argv[Req->argc - 1]; + else + mask = "*"; - /* An anderen Server forwarden? */ - if( Req->argc == 2 ) - { - target = Client_Search( Req->argv[0] ); - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] ); - else if( target != Client_ThisServer( )) return IRC_WriteStrClientPrefix( target, from, "LINKS %s %s", Req->argv[0], Req->argv[1] ); - } + 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); - /* 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 ); + /* Forward? */ + if (Req->argc == 2) { + target = Client_Search(Req->argv[0]); + if (! target || Client_Type(target) != CLIENT_SERVER) + return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG, + Client_ID(from), + Req->argv[0] ); + else + if (target != Client_ThisServer()) + return IRC_WriteStrClientPrefix(target, from, + "LINKS %s %s", Req->argv[0], + Req->argv[1]); + } - c = Client_First( ); - while( c ) - { - if( Client_Type( c ) == CLIENT_SERVER ) - { - if( ! IRC_WriteStrClient( target, RPL_LINKS_MSG, Client_ID( target ), Client_ID( c ), Client_ID( Client_TopServer( c ) ? Client_TopServer( c ) : Client_ThisServer( )), Client_Hops( c ), Client_Info( c ))) return DISCONNECTED; + c = Client_First(); + while (c) { + if (Client_Type(c) == CLIENT_SERVER + && MatchCaseInsensitive(mask, Client_ID(c))) { + if (!IRC_WriteStrClient(from, RPL_LINKS_MSG, + Client_ID(from), Client_ID(c), + Client_ID(Client_TopServer(c) + ? Client_TopServer(c) + : Client_ThisServer()), + Client_Hops(c), Client_Info(c))) + return DISCONNECTED; } - c = Client_Next( c ); + c = Client_Next(c); } - - IRC_SetPenalty( target, 1 ); - return IRC_WriteStrClient( target, RPL_ENDOFLINKS_MSG, Client_ID( target ), mask ); + return IRC_WriteStrClient(from, RPL_ENDOFLINKS_MSG, + Client_ID(from), mask); } /* IRC_LINKS */ @@ -478,6 +512,8 @@ IRC_STATS( CLIENT *Client, REQUEST *Req ) COMMAND *cmd; time_t time_now; unsigned int days, hrs, mins; + struct list_head *list; + struct list_elem *list_item; assert(Client != NULL); assert(Req != NULL); @@ -516,6 +552,28 @@ IRC_STATS( CLIENT *Client, REQUEST *Req ) query = '*'; switch (query) { + case 'g': /* Network-wide bans ("G-Lines") */ + case 'G': + case 'k': /* Server-local bans ("K-Lines") */ + case 'K': + if (!Client_HasMode(from, 'o')) + return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG, + Client_ID(from)); + if (query == 'g' || query == 'G') + list = Class_GetList(CLASS_GLINE); + else + list = Class_GetList(CLASS_KLINE); + list_item = Lists_GetFirst(list); + while (list_item) { + if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG, + Client_ID(from), query, + Lists_GetMask(list_item), + Lists_GetValidity(list_item), + Lists_GetReason(list_item))) + return DISCONNECTED; + list_item = Lists_GetNext(list_item); + } + break; case 'l': /* Link status (servers and own link) */ case 'L': time_now = time(NULL); @@ -758,8 +816,16 @@ who_flags_qualifier(const char *chan_user_modes) } +/** + * Send WHO reply for a "channel target" ("WHO #channel"). + * + * @param Client Client requesting the information. + * @param Chan Channel being requested. + * @param OnlyOps Only display IRC operators. + * @return CONNECTED or DISCONNECTED. + */ static bool -IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) +IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) { bool is_visible, is_member, is_ircop; CL2CHAN *cl2chan; @@ -775,7 +841,8 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) /* Secret channel? */ if (!is_member && strchr(Channel_Modes(Chan), 's')) - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan)); + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, + Client_ID(Client), Channel_Name(Chan)); cl2chan = Channel_FirstMember(Chan); for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) { @@ -793,126 +860,155 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) strlcat(flags, "*", sizeof(flags)); chan_user_modes = Channel_UserModes(Chan, c); - strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags)); + strlcat(flags, who_flags_qualifier(chan_user_modes), + sizeof(flags)); - if (!write_whoreply(Client, c, Channel_Name(Chan), flags)) + if (!write_whoreply(Client, c, Channel_Name(Chan), + flags)) return DISCONNECTED; } } - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan)); -} /* IRC_Send_WHO */ + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), + Channel_Name(Chan)); +} -GLOBAL bool -IRC_WHO( CLIENT *Client, REQUEST *Req ) +/** + * Send WHO reply for a "mask target" ("WHO m*sk"). + * + * @param Client Client requesting the information. + * @param Mask Mask being requested or NULL for "all" clients. + * @param OnlyOps Only display IRC operators. + * @return CONNECTED or DISCONNECTED. + */ +static bool +IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps) { - bool only_ops, have_arg, client_match; - const char *channelname, *client_modes, *chan_user_modes; - char pattern[COMMAND_LEN]; - char flags[4]; - CL2CHAN *cl2chan; - CHANNEL *chan, *cn; CLIENT *c; + CL2CHAN *cl2chan; + CHANNEL *chan; + bool client_match, is_visible; + char flags[4]; - assert( Client != NULL ); - assert( Req != NULL ); - - if (Req->argc > 2) - return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - only_ops = false; - have_arg = false; + assert (Client != NULL); - if (Req->argc == 2) { - if (strcmp(Req->argv[1], "o") == 0) - only_ops = true; -#ifdef STRICT_RFC - else return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); -#endif - } - - IRC_SetPenalty(Client, 1); - if (Req->argc >= 1) { /* Channel or Mask. */ - chan = Channel_Search(Req->argv[0]); - if (chan) - return IRC_Send_WHO(Client, chan, only_ops); - if (strcmp(Req->argv[0], "0") != 0) { /* RFC stupidity, same as no arguments */ - have_arg = true; - strlcpy(pattern, Req->argv[0], sizeof(pattern)); - ngt_LowerStr(pattern); - IRC_SetPenalty(Client, 3); - } - } + if (Mask) + ngt_LowerStr(Mask); for (c = Client_First(); c != NULL; c = Client_Next(c)) { if (Client_Type(c) != CLIENT_USER) continue; - /* - * RFC 2812, 3.6.1: - * In the absence of the parameter, all visible (users who aren't - * invisible (user mode +i) and who don't have a common channel - * with the requesting client) are listed. - * - * The same result can be achieved by using a [sic] of "0" - * or any wildcard which will end up matching every visible user. - * - * The [sic] passed to WHO is matched against users' host, server, real name and - * nickname if the channel cannot be found. - */ - client_modes = Client_Modes(c); - if (strchr(client_modes, 'i')) - continue; - if (only_ops && !strchr(client_modes, 'o')) + if (OnlyOps && !Client_HasMode(c, 'o')) continue; - if (have_arg) { /* match pattern against user host/server/name/nick */ - client_match = MatchCaseInsensitive(pattern, Client_Hostname(c)); /* user's host */ + if (Mask) { + /* Match pattern against user host/server/name/nick */ + client_match = MatchCaseInsensitive(Mask, + Client_Hostname(c)); if (!client_match) - client_match = MatchCaseInsensitive(pattern, Client_ID(Client_Introducer(c))); /* server */ + client_match = MatchCaseInsensitive(Mask, + Client_ID(Client_Introducer(c))); if (!client_match) - client_match = Match(Req->argv[0], Client_Info(c)); /* realname */ + client_match = MatchCaseInsensitive(Mask, + Client_Info(c)); if (!client_match) - client_match = MatchCaseInsensitive(pattern, Client_ID(c)); /* nick name */ - - if (!client_match) /* This isn't the client you're looking for */ - continue; + client_match = MatchCaseInsensitive(Mask, + Client_ID(c)); + if (!client_match) + continue; /* no match: skip this client */ } - strcpy(flags, who_flags_status(client_modes)); + is_visible = !Client_HasMode(c, 'i'); - if (strchr(client_modes, 'o')) /* this client is an operator */ - strlcat(flags, "*", sizeof(flags)); + /* Target client is invisible, but mask matches exactly? */ + if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0) + is_visible = true; - /* Search suitable channel */ - cl2chan = Channel_FirstChannelOf(c); - while (cl2chan) { - cn = Channel_GetChannel(cl2chan); - if (Channel_IsMemberOf(cn, Client) || - !strchr(Channel_Modes(cn), 's')) - { - channelname = Channel_Name(cn); - break; + /* Target still invisible, but are both on the same channel? */ + if (!is_visible) { + cl2chan = Channel_FirstChannelOf(Client); + while (cl2chan && !is_visible) { + chan = Channel_GetChannel(cl2chan); + if (Channel_IsMemberOf(chan, c)) + is_visible = true; + cl2chan = Channel_NextChannelOf(Client, cl2chan); } - cl2chan = Channel_NextChannelOf(c, cl2chan); } - if (cl2chan) { - chan = Channel_GetChannel(cl2chan); - chan_user_modes = Channel_UserModes(chan, c); - strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags)); - } else - channelname = "*"; - - if (!write_whoreply(Client, c, channelname, flags)) + + if (!is_visible) /* target user is not visible */ + continue; + + strcpy(flags, who_flags_status(Client_Modes(c))); + if (strchr(Client_Modes(c), 'o')) + strlcat(flags, "*", sizeof(flags)); + + if (!write_whoreply(Client, c, "*", flags)) return DISCONNECTED; + } - if (Req->argc > 0) - channelname = Req->argv[0]; - else - channelname = "*"; + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), + Mask ? Mask : "*"); +} - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), channelname); + +/** + * Handler for the IRC "WHO" command. + * + * See RFC 2812, 3.6.1 "Who query". + * + * @param Client The client from which this command has been received. + * @param Req Request structure with prefix and all parameters. + * @return CONNECTED or DISCONNECTED. + */ +GLOBAL bool +IRC_WHO(CLIENT *Client, REQUEST *Req) +{ + bool only_ops; + CHANNEL *chan; + + assert (Client != NULL); + assert (Req != NULL); + + if (Req->argc > 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + only_ops = false; + + if (Req->argc == 2) { + if (strcmp(Req->argv[1], "o") == 0) + only_ops = true; +#ifdef STRICT_RFC + else + return IRC_WriteStrClient(Client, + ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), + Req->command); +#endif + } + + IRC_SetPenalty(Client, 1); + if (Req->argc >= 1) { + /* Channel or mask given */ + chan = Channel_Search(Req->argv[0]); + if (chan) { + /* Members of a channel have been requested */ + IRC_SetPenalty(Client, 1); + return IRC_WHO_Channel(Client, chan, only_ops); + } + if (strcmp(Req->argv[0], "0") != 0) { + /* A mask has been given. But please note this RFC + * stupidity: "0" is same as no arguments ... */ + IRC_SetPenalty(Client, 3); + return IRC_WHO_Mask(Client, Req->argv[0], only_ops); + } + } + + /* No channel or (valid) mask given */ + IRC_SetPenalty(Client, 2); + return IRC_WHO_Mask(Client, NULL, only_ops); } /* IRC_WHO */ @@ -1013,8 +1109,7 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c) Client_Away(c))) return DISCONNECTED; - return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG, - Client_ID(from), Client_ID(c)); + return CONNECTED; } /* IRC_WHOIS_SendReply */ @@ -1090,7 +1185,7 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) */ if (!has_wildcards || is_remote) { c = Client_Search(query); - if (c) { + if (c && Client_Type(c) == CLIENT_USER) { if (!IRC_WHOIS_SendReply(Client, from, c)) return DISCONNECTED; } else { @@ -1123,10 +1218,12 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) } if (match_count == 0) - return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, - Client_ID(Client), Req->argv[Req->argc - 1]); + IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), + Req->argv[Req->argc - 1]); } - return CONNECTED; + return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG, + Client_ID(from), Req->argv[Req->argc - 1]); } /* IRC_WHOIS */ @@ -1251,38 +1348,55 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req ) } /* IRC_WHOWAS */ +/** + * Send LUSERS reply to a client. + * + * @param Client The receipient of the information. + * @return CONNECTED or DISCONNECTED. + */ GLOBAL bool -IRC_Send_LUSERS( CLIENT *Client ) +IRC_Send_LUSERS(CLIENT *Client) { unsigned long cnt; #ifndef STRICT_RFC unsigned long max; #endif - assert( Client != NULL ); + assert(Client != NULL); /* Users, services and serevers in the network */ - if( ! IRC_WriteStrClient( Client, RPL_LUSERCLIENT_MSG, Client_ID( Client ), Client_UserCount( ), Client_ServiceCount( ), Client_ServerCount( ))) return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client), + Client_UserCount(), Client_ServiceCount(), + Client_ServerCount())) + return DISCONNECTED; /* Number of IRC operators */ cnt = Client_OperCount( ); - if( cnt > 0 ) - { - if( ! IRC_WriteStrClient( Client, RPL_LUSEROP_MSG, Client_ID( Client ), cnt )) return DISCONNECTED; + if (cnt > 0) { + if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG, + Client_ID(Client), cnt)) + return DISCONNECTED; } /* Unknown connections */ cnt = Client_UnknownCount( ); - if( cnt > 0 ) - { - if( ! IRC_WriteStrClient( Client, RPL_LUSERUNKNOWN_MSG, Client_ID( Client ), cnt )) return DISCONNECTED; + if (cnt > 0) { + if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG, + Client_ID(Client), cnt)) + return DISCONNECTED; } /* Number of created channels */ - if( ! IRC_WriteStrClient( Client, RPL_LUSERCHANNELS_MSG, Client_ID( Client ), Channel_Count( ))) return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG, + Client_ID(Client), + Channel_CountVisible(Client))) + return DISCONNECTED; /* Number of local users, services and servers */ - if( ! IRC_WriteStrClient( Client, RPL_LUSERME_MSG, Client_ID( Client ), Client_MyUserCount( ), Client_MyServiceCount( ), Client_MyServerCount( ))) return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client), + Client_MyUserCount(), Client_MyServiceCount(), + Client_MyServerCount())) + return DISCONNECTED; #ifndef STRICT_RFC /* Maximum number of local users */