From: Alexander Barton Date: Fri, 15 Feb 2013 20:59:06 +0000 (+0100) Subject: Merge branch 'bug153-ServerMask' into master X-Git-Tag: rel-21-rc1~133 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=commitdiff_plain;h=65359ff8f722efdf24700ce05011afc0fef28924;hp=d5763937ad8d482f3e62299428f8cb4b601c6ace Merge branch 'bug153-ServerMask' into master * bug153-ServerMask: Update irc-info.c to use irc-macros.h Add new irc-macros.h to project irc-info.c: add/streamline function documentation comments irc-info: move static functions at the top of the file Implement new function Client_SearchServer() Conflicts: src/ngircd/irc-info.c --- diff --git a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj index a2cad27c..02502cc6 100644 --- a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj +++ b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj @@ -66,10 +66,11 @@ /* Begin PBXFileReference section */ FA1A6BBC0D6857BB00AA8F71 /* misc-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "misc-test.e"; sourceTree = ""; }; FA1A6BBD0D6857D900AA8F71 /* who-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "who-test.e"; sourceTree = ""; }; + FA1DBB6716C707D200D4F838 /* irc-macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "irc-macros.h"; sourceTree = ""; }; FA2D564811EA158B00D37A35 /* pam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pam.h; sourceTree = ""; }; FA2D564911EA158B00D37A35 /* pam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pam.c; sourceTree = ""; }; FA2D567A11EA1AB300D37A35 /* libpam.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpam.dylib; path = usr/lib/libpam.dylib; sourceTree = SDKROOT; }; - FA322BBA0CEF72E4001761B3 /* ngIRCd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ngIRCd; sourceTree = BUILT_PRODUCTS_DIR; }; + FA322BBA0CEF72E4001761B3 /* ngIRCd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; name = ngIRCd; path = ngircd; sourceTree = BUILT_PRODUCTS_DIR; }; FA322CD60CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = ""; }; FA322CD90CEF74B1001761B3 /* array.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = array.c; sourceTree = ""; }; FA322CDA0CEF74B1001761B3 /* array.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = array.h; sourceTree = ""; }; @@ -368,6 +369,7 @@ FA322CEF0CEF74B1001761B3 /* irc-info.h */, FA322CF00CEF74B1001761B3 /* irc-login.c */, FA322CF10CEF74B1001761B3 /* irc-login.h */, + FA1DBB6716C707D200D4F838 /* irc-macros.h */, FA4F1659164836B100DBD011 /* irc-metadata.c */, FA4F165C164836BF00DBD011 /* irc-metadata.h */, FA322CF20CEF74B1001761B3 /* irc-mode.c */, diff --git a/src/ngircd/Makefile.ng b/src/ngircd/Makefile.ng index cdbd27c1..21e606d7 100644 --- a/src/ngircd/Makefile.ng +++ b/src/ngircd/Makefile.ng @@ -86,6 +86,7 @@ noinst_HEADERS = \ irc-encoding.h \ irc-info.h \ irc-login.h \ + irc-macros.h \ irc-metadata.h \ irc-mode.h \ irc-op.h \ diff --git a/src/ngircd/client.c b/src/ngircd/client.c index d10775a8..37b168ff 100644 --- a/src/ngircd/client.c +++ b/src/ngircd/client.c @@ -41,6 +41,7 @@ #include "hash.h" #include "irc-write.h" #include "log.h" +#include "match.h" #include "messages.h" #include @@ -556,13 +557,14 @@ Client_ModeDel( CLIENT *Client, char Mode ) } /* Client_ModeDel */ +/** + * Search CLIENT structure of a given nick name. + * + * @return Pointer to CLIENT structure or NULL if not found. + */ GLOBAL CLIENT * Client_Search( const char *Nick ) { - /* return Client-Structure that has the corresponding Nick. - * If none is found, return NULL. - */ - char search_id[CLIENT_ID_LEN], *ptr; CLIENT *c = NULL; UINT32 search_hash; @@ -583,7 +585,39 @@ Client_Search( const char *Nick ) c = (CLIENT *)c->next; } return NULL; -} /* Client_Search */ +} + + +/** + * Serach first CLIENT structure matching a given mask of a server. + * + * The order of servers is arbitrary, but this function makes sure that the + * local server is always returned if the mask matches it. + * + * @return Pointer to CLIENT structure or NULL if no server could be found. + */ +GLOBAL CLIENT * +Client_SearchServer(const char *Mask) +{ + CLIENT *c; + + assert(Mask != NULL); + + /* First check if mask matches the local server */ + if (MatchCaseInsensitive(Mask, Client_ID(Client_ThisServer()))) + return Client_ThisServer(); + + c = My_Clients; + while (c) { + if (Client_Type(c) == CLIENT_SERVER) { + /* This is a server: check if Mask matches */ + if (MatchCaseInsensitive(Mask, c->id)) + return c; + } + c = (CLIENT *)c->next; + } + return NULL; +} /** diff --git a/src/ngircd/client.h b/src/ngircd/client.h index ebbd06cb..c248d1ba 100644 --- a/src/ngircd/client.h +++ b/src/ngircd/client.h @@ -94,6 +94,7 @@ GLOBAL CLIENT *Client_ThisServer PARAMS(( void )); GLOBAL CLIENT *Client_GetFromToken PARAMS(( CLIENT *Client, int Token )); GLOBAL CLIENT *Client_Search PARAMS(( const char *ID )); +GLOBAL CLIENT *Client_SearchServer PARAMS(( const char *ID )); GLOBAL CLIENT *Client_First PARAMS(( void )); GLOBAL CLIENT *Client_Next PARAMS(( CLIENT *c )); diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c index f9c9b0d2..24d65651 100644 --- a/src/ngircd/irc-info.c +++ b/src/ngircd/irc-info.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors. + * 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 @@ -38,13 +38,456 @@ #include "tool.h" #include "parse.h" #include "irc.h" +#include "irc-macros.h" #include "irc-write.h" #include "client-cap.h" #include "exp.h" #include "irc-info.h" +/* Local functions */ +static unsigned int +t_diff(time_t *t, const time_t d) +{ + time_t diff, remain; + + diff = *t / d; + remain = diff * d; + *t -= remain; + + return (unsigned int)diff; +} + +static unsigned int +uptime_days(time_t *now) +{ + return t_diff(now, 60 * 60 * 24); +} + +static unsigned int +uptime_hrs(time_t *now) +{ + return t_diff(now, 60 * 60); +} + +static unsigned int +uptime_mins(time_t *now) +{ + return t_diff(now, 60); +} + +static bool +write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags) +{ + return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client), + channelname, Client_User(c), + Client_HostnameDisplayed(c), + Client_ID(Client_Introducer(c)), Client_ID(c), + flags, Client_Hops(c), Client_Info(c)); +} + +static const char * +who_flags_status(const char *client_modes) +{ + if (strchr(client_modes, 'a')) + return "G"; /* away */ + return "H"; +} + +/** + * Return channel user mode prefix(es). + * + * @param Client The client requesting the mode prefixes. + * @param chan_user_modes String with channel user modes. + * @param str String buffer to which the prefix(es) will be appended. + * @param len Size of "str" buffer. + * @return Pointer to "str". + */ +static char * +who_flags_qualifier(CLIENT *Client, const char *chan_user_modes, + char *str, size_t len) +{ + assert(Client != NULL); + + if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) { + if (strchr(chan_user_modes, 'q')) + strlcat(str, "~", len); + if (strchr(chan_user_modes, 'a')) + strlcat(str, "&", len); + if (strchr(chan_user_modes, 'o')) + strlcat(str, "@", len); + if (strchr(chan_user_modes, 'h')) + strlcat(str, "%", len); + if (strchr(chan_user_modes, 'v')) + strlcat(str, "+", len); + + return str; + } + + if (strchr(chan_user_modes, 'q')) + strlcat(str, "~", len); + else if (strchr(chan_user_modes, 'a')) + strlcat(str, "&", len); + else if (strchr(chan_user_modes, 'o')) + strlcat(str, "@", len); + else if (strchr(chan_user_modes, 'h')) + strlcat(str, "%", len); + else if (strchr(chan_user_modes, 'v')) + strlcat(str, "+", len); + + return str; +} + +/** + * 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_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) +{ + bool is_visible, is_member, is_ircop; + CL2CHAN *cl2chan; + const char *client_modes; + char flags[10]; + CLIENT *c; + int count = 0; + + assert( Client != NULL ); + assert( Chan != NULL ); + + IRC_SetPenalty(Client, 1); + + is_member = Channel_IsMemberOf(Chan, Client); + + /* Secret channel? */ + if (!is_member && strchr(Channel_Modes(Chan), 's')) + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, + Client_ID(Client), Channel_Name(Chan)); + + cl2chan = Channel_FirstMember(Chan); + for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) { + c = Channel_GetClient(cl2chan); + + client_modes = Client_Modes(c); + is_ircop = strchr(client_modes, 'o') != NULL; + if (OnlyOps && !is_ircop) + continue; + + is_visible = strchr(client_modes, 'i') == NULL; + if (is_member || is_visible) { + strlcpy(flags, who_flags_status(client_modes), + sizeof(flags)); + if (is_ircop) + strlcat(flags, "*", sizeof(flags)); + + who_flags_qualifier(Client, Channel_UserModes(Chan, c), + flags, sizeof(flags)); + + if (!write_whoreply(Client, c, Channel_Name(Chan), + flags)) + return DISCONNECTED; + count++; + } + } + + /* If there are a lot of clients, augment penalty a bit */ + if (count > MAX_RPL_WHO) + IRC_SetPenalty(Client, 1); + + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), + Channel_Name(Chan)); +} + +/** + * 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) +{ + CLIENT *c; + CL2CHAN *cl2chan; + CHANNEL *chan; + bool client_match, is_visible; + char flags[4]; + int count = 0; + + assert (Client != NULL); + + if (Mask) + ngt_LowerStr(Mask); + + IRC_SetPenalty(Client, 3); + for (c = Client_First(); c != NULL; c = Client_Next(c)) { + if (Client_Type(c) != CLIENT_USER) + continue; + + if (OnlyOps && !Client_HasMode(c, 'o')) + continue; + + if (Mask) { + /* Match pattern against user host/server/name/nick */ + client_match = MatchCaseInsensitive(Mask, + Client_Hostname(c)); + if (!client_match) + client_match = MatchCaseInsensitive(Mask, + Client_ID(Client_Introducer(c))); + if (!client_match) + client_match = MatchCaseInsensitive(Mask, + Client_Info(c)); + if (!client_match) + client_match = MatchCaseInsensitive(Mask, + Client_ID(c)); + if (!client_match) + continue; /* no match: skip this client */ + } + + is_visible = !Client_HasMode(c, 'i'); + + /* Target client is invisible, but mask matches exactly? */ + if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0) + is_visible = true; + + /* 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); + } + } + + if (!is_visible) /* target user is not visible */ + continue; + + if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO")) + break; + + strlcpy(flags, who_flags_status(Client_Modes(c)), sizeof(flags)); + if (strchr(Client_Modes(c), 'o')) + strlcat(flags, "*", sizeof(flags)); + + if (!write_whoreply(Client, c, "*", flags)) + return DISCONNECTED; + count++; + } + + return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), + Mask ? Mask : "*"); +} + +/** + * Generate WHOIS reply of one actual client. + * + * @param Client The client from which this command has been received. + * @param from The client requesting the information ("originator"). + * @param c The client of which information should be returned. + * @return CONNECTED or DISCONNECTED. + */ +static bool +IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c) +{ + char str[LINE_LEN + 1]; + CL2CHAN *cl2chan; + CHANNEL *chan; + + assert(Client != NULL); + assert(from != NULL); + assert(c != NULL); + + /* Nick, user, hostname and client info */ + if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from), + Client_ID(c), Client_User(c), + Client_HostnameDisplayed(c), Client_Info(c))) + return DISCONNECTED; + + /* Server */ + if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from), + Client_ID(c), Client_ID(Client_Introducer(c)), + Client_Info(Client_Introducer(c)))) + return DISCONNECTED; + + /* Channels */ + snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG, + Client_ID(from), Client_ID(c)); + cl2chan = Channel_FirstChannelOf(c); + while (cl2chan) { + chan = Channel_GetChannel(cl2chan); + assert(chan != NULL); + + /* next */ + cl2chan = Channel_NextChannelOf(c, cl2chan); + + /* Secret channel? */ + if (strchr(Channel_Modes(chan), 's') + && !Channel_IsMemberOf(chan, Client)) + continue; + + /* Local channel and request is not from a user? */ + if (Client_Type(Client) == CLIENT_SERVER + && Channel_IsLocal(chan)) + continue; + + /* Concatenate channel names */ + if (str[strlen(str) - 1] != ':') + strlcat(str, " ", sizeof(str)); + + who_flags_qualifier(Client, Channel_UserModes(chan, c), + str, sizeof(str)); + strlcat(str, Channel_Name(chan), sizeof(str)); + + if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) { + /* Line becomes too long: send it! */ + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG, + Client_ID(from), Client_ID(c)); + } + } + if(str[strlen(str) - 1] != ':') { + /* There is data left to send: */ + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + } + + /* IRC-Operator? */ + if (Client_HasMode(c, 'o') && + !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG, + Client_ID(from), Client_ID(c))) + return DISCONNECTED; + + /* IRC-Bot? */ + if (Client_HasMode(c, 'B') && + !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG, + Client_ID(from), Client_ID(c))) + return DISCONNECTED; + + /* Connected using SSL? */ + if (Conn_UsesSSL(Client_Conn(c)) && + !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from), + Client_ID(c))) + return DISCONNECTED; + + /* Registered nickname? */ + if (Client_HasMode(c, 'R') && + !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG, + Client_ID(from), Client_ID(c))) + return DISCONNECTED; + + /* Local client and requester is the user itself or an IRC Op? */ + if (Client_Conn(c) > NONE && + (from == c || (!Conf_MorePrivacy && Client_HasMode(from, 'o')))) { + /* Client hostname */ + if (!IRC_WriteStrClient(from, RPL_WHOISHOST_MSG, + Client_ID(from), Client_ID(c), Client_Hostname(c), + Conn_GetIPAInfo(Client_Conn(c)))) + return DISCONNECTED; + /* Client modes */ + if (!IRC_WriteStrClient(from, RPL_WHOISMODES_MSG, + Client_ID(from), Client_ID(c), Client_Modes(c))) + return DISCONNECTED; + } + + /* Idle and signon time (local clients only!) */ + if (!Conf_MorePrivacy && Client_Conn(c) > NONE && + !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG, + Client_ID(from), Client_ID(c), + (unsigned long)Conn_GetIdle(Client_Conn(c)), + (unsigned long)Conn_GetSignon(Client_Conn(c)))) + return DISCONNECTED; + + /* Away? */ + if (Client_HasMode(c, 'a') && + !IRC_WriteStrClient(from, RPL_AWAY_MSG, + Client_ID(from), Client_ID(c), Client_Away(c))) + return DISCONNECTED; + + return CONNECTED; +} + +static bool +WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry) +{ + char t_str[60]; + + (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y", + localtime(&entry->time)); + + if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix), + entry->id, entry->user, entry->host, entry->info)) + return DISCONNECTED; + + return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix), + entry->id, entry->server, t_str); +} + +static bool +Show_MOTD_Start(CLIENT *Client) +{ + return IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG, + Client_ID( Client ), Client_ID( Client_ThisServer( ))); +} + +static bool +Show_MOTD_Sendline(CLIENT *Client, const char *msg) +{ + return IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID( Client ), msg); +} + +static bool +Show_MOTD_End(CLIENT *Client) +{ + if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client))) + return DISCONNECTED; + + if (*Conf_CloakHost) + return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG, + Client_ID(Client), + Client_Hostname(Client)); + + return CONNECTED; +} + +#ifdef SSL_SUPPORT +static bool Show_MOTD_SSLInfo(CLIENT *Client) +{ + bool ret = true; + char buf[COMMAND_LEN] = "Connected using Cipher "; + + if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23)) + return true; + + if (!Show_MOTD_Sendline(Client, buf)) + ret = false; + + return ret; +} +#else +static inline bool +Show_MOTD_SSLInfo(UNUSED CLIENT *c) +{ return true; } +#endif + +/* Global functions */ + +/** + * Handler for the IRC command "ADMIN". + * + * @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_ADMIN(CLIENT *Client, REQUEST *Req ) { @@ -53,41 +496,41 @@ IRC_ADMIN(CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - /* find target ... */ - if( Req->argc == 1 ) target = Client_Search( Req->argv[0] ); - else target = Client_ThisServer( ); - - /* find Prefix */ - if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_Search( Req->prefix ); - else prefix = Client; - if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + IRC_SetPenalty(Client, 1); - /* forwad message to another server? */ - if( target != Client_ThisServer( )) - { - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, Client_ID( prefix ), Req->argv[0] ); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1) + _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix) - /* forward */ - IRC_WriteStrClientPrefix( target, prefix, "ADMIN %s", Req->argv[0] ); + /* Forward? */ + if(target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, prefix, + "ADMIN %s", Client_ID(target)); return CONNECTED; } - /* mit Versionsinfo antworten */ - if( ! IRC_WriteStrClient( Client, RPL_ADMINME_MSG, Client_ID( prefix ), Conf_ServerName )) return DISCONNECTED; - if( ! IRC_WriteStrClient( Client, RPL_ADMINLOC1_MSG, Client_ID( prefix ), Conf_ServerAdmin1 )) return DISCONNECTED; - if( ! IRC_WriteStrClient( Client, RPL_ADMINLOC2_MSG, Client_ID( prefix ), Conf_ServerAdmin2 )) return DISCONNECTED; - if( ! IRC_WriteStrClient( Client, RPL_ADMINEMAIL_MSG, Client_ID( prefix ), Conf_ServerAdminMail )) return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_ADMINME_MSG, Client_ID(prefix), + Conf_ServerName)) + return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_ADMINLOC1_MSG, Client_ID(prefix), + Conf_ServerAdmin1)) + return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_ADMINLOC2_MSG, Client_ID(prefix), + Conf_ServerAdmin2)) + return DISCONNECTED; + if (!IRC_WriteStrClient(Client, RPL_ADMINEMAIL_MSG, Client_ID(prefix), + Conf_ServerAdminMail)) + return DISCONNECTED; - IRC_SetPenalty( Client, 1 ); return CONNECTED; } /* IRC_ADMIN */ - /** * Handler for the IRC command "INFO". - * See RFC 2812 section 3.4.10. + * + * @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_INFO(CLIENT * Client, REQUEST * Req) @@ -98,38 +541,16 @@ IRC_INFO(CLIENT * Client, REQUEST * Req) assert(Client != NULL); assert(Req != NULL); - /* Wrong number of parameters? */ - if (Req->argc > 1) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); - - /* Determine prefix */ - if (Client_Type(Client) == CLIENT_SERVER) - prefix = Client_Search(Req->prefix); - else - prefix = Client; - if (!prefix) - return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, - Client_ID(Client), Req->prefix); - - /* Look for a target */ - if (Req->argc > 0) - target = Client_Search(Req->argv[0]); - else - target = Client_ThisServer(); - - /* Make sure that the target is a server */ - if (target && Client_Type(target) != CLIENT_SERVER) - target = Client_Introducer(target); + IRC_SetPenalty(Client, 2); - if (!target) - return IRC_WriteStrClient(prefix, ERR_NOSUCHSERVER_MSG, - Client_ID(prefix), Req->argv[0]); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1) + _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix) - /* Pass on to another server? */ + /* Forward? */ if (target != Client_ThisServer()) { IRC_WriteStrClientPrefix(target, prefix, "INFO %s", - Req->argv[0]); + Client_ID(target)); return CONNECTED; } @@ -151,16 +572,12 @@ IRC_INFO(CLIENT * Client, REQUEST * Req) if (!IRC_WriteStrClient(Client, RPL_ENDOFINFO_MSG, Client_ID(prefix))) return DISCONNECTED; - IRC_SetPenalty(Client, 2); return CONNECTED; } /* 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. @@ -176,10 +593,7 @@ IRC_ISON( CLIENT *Client, REQUEST *Req ) assert(Client != NULL); assert(Req != NULL); - /* Bad number of arguments? */ - if (Req->argc < 1) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); + _IRC_ARGC_GE_OR_RETURN_(Client, Req, 1) strlcpy(rpl, RPL_ISON_MSG, sizeof rpl); for (i = 0; i < Req->argc; i++) { @@ -201,12 +615,9 @@ IRC_ISON( CLIENT *Client, REQUEST *Req ) 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. @@ -222,9 +633,8 @@ IRC_LINKS(CLIENT *Client, REQUEST *Req) IRC_SetPenalty(Client, 1); - if (Req->argc > 2) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) /* Get pointer to server mask or "*", if none given */ if (Req->argc > 0) @@ -232,26 +642,15 @@ IRC_LINKS(CLIENT *Client, REQUEST *Req) else mask = "*"; - 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); - /* 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]); + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from) + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, + "LINKS %s %s", Client_ID(target), + Req->argv[1]); + return CONNECTED; + } } c = Client_First(); @@ -272,7 +671,13 @@ IRC_LINKS(CLIENT *Client, REQUEST *Req) Client_ID(from), mask); } /* IRC_LINKS */ - +/** + * Handler for the IRC "LUSERS" command. + * + * @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_LUSERS( CLIENT *Client, REQUEST *Req ) { @@ -281,49 +686,41 @@ IRC_LUSERS( CLIENT *Client, REQUEST *Req ) 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); - /* 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 ); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from) - /* An anderen Server forwarden? */ - if( Req->argc == 2 ) - { - target = Client_Search( Req->argv[1] ); - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[1] ); - else if( target != Client_ThisServer( )) return IRC_WriteStrClientPrefix( target, from, "LUSERS %s %s", Req->argv[0], Req->argv[1] ); + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, + "LUSERS %s %s", Req->argv[0], + Client_ID(target)); + return CONNECTED; } - /* 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 ); - - IRC_Send_LUSERS( target ); - - IRC_SetPenalty( target, 1 ); - return CONNECTED; + return IRC_Send_LUSERS(from); } /* IRC_LUSERS */ - /** * Handler for the IRC command "SERVLIST". - * List registered services, see RFC 2811, section 3.5.1: the syntax is - * "SERVLIST [ []]". + * + * @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_SERVLIST(CLIENT *Client, REQUEST *Req) { CLIENT *c; + IRC_SetPenalty(Client, 1); + assert(Client != NULL); assert(Req != NULL); - if (Req->argc > 2) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) if (Req->argc < 2 || strcmp(Req->argv[1], "0") == 0) { for (c = Client_First(); c!= NULL; c = Client_Next(c)) { @@ -345,7 +742,13 @@ IRC_SERVLIST(CLIENT *Client, REQUEST *Req) Req->argc > 1 ? Req->argv[1] : "0"); } /* IRC_SERVLIST */ - +/** + * Handler for the IRC command "MOTD". + * + * @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_MOTD( CLIENT *Client, REQUEST *Req ) { @@ -354,31 +757,29 @@ IRC_MOTD( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - /* From aus Prefix 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 ); + IRC_SetPenalty(Client, 3); - if( Req->argc == 1 ) - { - /* forward? */ - 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] ); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from) - if( target != Client_ThisServer( )) - { - /* Ok, anderer Server ist das Ziel: forwarden */ - return IRC_WriteStrClientPrefix( target, from, "MOTD %s", Req->argv[0] ); - } + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, "MOTD %s", + Client_ID(target)); + return CONNECTED; } - IRC_SetPenalty( from, 3 ); - return IRC_Show_MOTD( from ); + return IRC_Show_MOTD(from); } /* IRC_MOTD */ - +/** + * Handler for the IRC command "NAMES". + * + * @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_NAMES( CLIENT *Client, REQUEST *Req ) { @@ -389,121 +790,76 @@ IRC_NAMES( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - /* use prefix to determine "From" */ - 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 ); + IRC_SetPenalty(Client, 1); - if( Req->argc == 2 ) - { - /* forward to another server? */ - target = Client_Search( Req->argv[1] ); - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[1] ); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from) - if( target != Client_ThisServer( )) { - /* target is another server, forward */ - return IRC_WriteStrClientPrefix( target, from, "NAMES %s :%s", Req->argv[0], Req->argv[1] ); - } + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, "NAMES %s :%s", + Req->argv[0], Client_ID(target)); + return CONNECTED; } - if( Req->argc > 0 ) - { - /* bestimmte Channels durchgehen */ - ptr = strtok( Req->argv[0], "," ); - while( ptr ) - { - chan = Channel_Search( ptr ); - if( chan ) - { - /* print name */ - if( ! IRC_Send_NAMES( from, chan )) return DISCONNECTED; - } - if( ! IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), ptr )) return DISCONNECTED; - - /* get next channel name */ + if (Req->argc > 0) { + /* Return NAMES list for specific channels */ + ptr = strtok(Req->argv[0], ","); + while(ptr) { + chan = Channel_Search(ptr); + if (chan && !IRC_Send_NAMES(from, chan)) + return DISCONNECTED; + if (!IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG, + Client_ID(from), ptr)) + return DISCONNECTED; ptr = strtok( NULL, "," ); } return CONNECTED; } - chan = Channel_First( ); - while( chan ) - { - if( ! IRC_Send_NAMES( from, chan )) return DISCONNECTED; - - chan = Channel_Next( chan ); + chan = Channel_First(); + while (chan) { + if (!IRC_Send_NAMES(from, chan)) + return DISCONNECTED; + chan = Channel_Next(chan); } /* Now print all clients which are not in any channel */ - c = Client_First( ); - snprintf( rpl, sizeof( rpl ), RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" ); - while( c ) - { - if(( Client_Type( c ) == CLIENT_USER ) && ( Channel_FirstChannelOf( c ) == NULL ) && ( ! strchr( Client_Modes( c ), 'i' ))) + c = Client_First(); + snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG, Client_ID(from), "*", "*"); + while (c) { + if (Client_Type(c) == CLIENT_USER + && Channel_FirstChannelOf(c) == NULL + && !strchr(Client_Modes(c), 'i')) { /* its a user, concatenate ... */ - if( rpl[strlen( rpl ) - 1] != ':' ) strlcat( rpl, " ", sizeof( rpl )); - strlcat( rpl, Client_ID( c ), sizeof( rpl )); + if (rpl[strlen(rpl) - 1] != ':') + strlcat(rpl, " ", sizeof(rpl)); + strlcat(rpl, Client_ID(c), sizeof(rpl)); - if( strlen( rpl ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 )) - { + if (strlen(rpl) > LINE_LEN - CLIENT_NICK_LEN - 4) { /* Line is gwoing too long, send now */ - if( ! IRC_WriteStrClient( from, "%s", rpl )) return DISCONNECTED; - snprintf( rpl, sizeof( rpl ), RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" ); + if (!IRC_WriteStrClient(from, "%s", rpl)) + return DISCONNECTED; + snprintf(rpl, sizeof(rpl), RPL_NAMREPLY_MSG, + Client_ID(from), "*", "*"); } } - - c = Client_Next( c ); - } - if( rpl[strlen( rpl ) - 1] != ':') - { - if( ! IRC_WriteStrClient( from, "%s", rpl )) return DISCONNECTED; + c = Client_Next(c); } + if (rpl[strlen(rpl) - 1] != ':' && !IRC_WriteStrClient(from, "%s", rpl)) + return DISCONNECTED; - IRC_SetPenalty( from, 1 ); - return IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), "*" ); + return IRC_WriteStrClient(from, RPL_ENDOFNAMES_MSG, Client_ID(from), "*"); } /* IRC_NAMES */ - -static unsigned int -t_diff(time_t *t, const time_t d) -{ - time_t diff, remain; - - diff = *t / d; - remain = diff * d; - *t -= remain; - - return (unsigned int)diff; -} - - -static unsigned int -uptime_days(time_t *now) -{ - return t_diff(now, 60 * 60 * 24); -} - - -static unsigned int -uptime_hrs(time_t *now) -{ - return t_diff(now, 60 * 60); -} - - -static unsigned int -uptime_mins(time_t *now) -{ - return t_diff(now, 60); -} - - /** * Handler for the IRC command "STATS". - * See RFC 2812 section 3.4.4. + * + * @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_STATS( CLIENT *Client, REQUEST *Req ) @@ -520,32 +876,17 @@ IRC_STATS( CLIENT *Client, REQUEST *Req ) assert(Client != NULL); assert(Req != NULL); - if (Req->argc > 2) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); - - /* use prefix to determine "From" */ - 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); + IRC_SetPenalty(Client, 2); - if (Req->argc == 2) { - /* forward to another server? */ - target = Client_Search(Req->argv[1]); - if ((!target) || (Client_Type(target) != CLIENT_SERVER)) - return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG, - Client_ID(from), Req->argv[1]); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 1, from) - if (target != Client_ThisServer()) { - /* forward to another server */ - return IRC_WriteStrClientPrefix(target, from, - "STATS %s %s", Req->argv[0], Req->argv[1]); - } + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, "STATS %s %s", + Req->argv[0], Client_ID(target)); + return CONNECTED; } if (Req->argc > 0) @@ -637,25 +978,35 @@ IRC_STATS( CLIENT *Client, REQUEST *Req ) break; } - IRC_SetPenalty(from, 2); return IRC_WriteStrClient(from, RPL_ENDOFSTATS_MSG, Client_ID(from), query); } /* IRC_STATS */ - /** * Handler for the IRC command "SUMMON". - * See RFC 2812 section 4.5. ngIRCd doesn't implement this functionality and - * therefore answers with ERR_SUMMONDISABLED. + * + * @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_SUMMON(CLIENT * Client, UNUSED REQUEST * Req) { + assert(Client != NULL); + + IRC_SetPenalty(Client, 1); + return IRC_WriteStrClient(Client, ERR_SUMMONDISABLED_MSG, Client_ID(Client)); } /* IRC_SUMMON */ - +/** + * Handler for the IRC command "TIME". + * + * @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_TIME( CLIENT *Client, REQUEST *Req ) { @@ -663,35 +1014,34 @@ IRC_TIME( CLIENT *Client, REQUEST *Req ) char t_str[64]; time_t t; - assert( Client != NULL ); - assert( Req != NULL ); - - if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + assert(Client != NULL); + assert(Req != NULL); - 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 ); + IRC_SetPenalty(Client, 1); - if( Req->argc == 1 ) - { - target = Client_Search( Req->argv[0] ); - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[0] ); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1) + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from) - if( target != Client_ThisServer( )) - { - return IRC_WriteStrClientPrefix( target, from, "TIME %s", Req->argv[0] ); - } + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, "TIME %s", + Client_ID(target)); + return CONNECTED; } t = time( NULL ); - (void)strftime( t_str, 60, "%A %B %d %Y -- %H:%M %Z", localtime( &t )); - return IRC_WriteStrClient( from, RPL_TIME_MSG, Client_ID( from ), Client_ID( Client_ThisServer( )), t_str ); + (void)strftime(t_str, 60, "%A %B %d %Y -- %H:%M %Z", localtime(&t)); + return IRC_WriteStrClient(from, RPL_TIME_MSG, Client_ID(from), + Client_ID(Client_ThisServer()), t_str); } /* IRC_TIME */ - /** * Handler for the IRC command "USERHOST". - * See RFC 2812 section 4.8. + * + * @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_USERHOST(CLIENT *Client, REQUEST *Req) @@ -703,9 +1053,9 @@ IRC_USERHOST(CLIENT *Client, REQUEST *Req) assert(Client != NULL); assert(Req != NULL); - if ((Req->argc < 1)) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); + IRC_SetPenalty(Client, 1); + + _IRC_ARGC_GE_OR_RETURN_(Client, Req, 1) if (Req->argc > 5) max = 5; @@ -736,277 +1086,62 @@ IRC_USERHOST(CLIENT *Client, REQUEST *Req) return IRC_WriteStrClient(Client, rpl, Client_ID(Client)); } /* IRC_USERHOST */ - /** * Handler for the IRC command "USERS". - * See RFC 2812 section 4.6. As suggested there the command is disabled. + * + * @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_USERS(CLIENT * Client, UNUSED REQUEST * Req) -{ - return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG, - Client_ID(Client)); -} /* IRC_USERS */ - - -GLOBAL bool -IRC_VERSION( CLIENT *Client, REQUEST *Req ) -{ - CLIENT *target, *prefix; - - assert( Client != NULL ); - assert( Req != NULL ); - - if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - /* Ziel suchen */ - if( Req->argc == 1 ) target = Client_Search( Req->argv[0] ); - else target = Client_ThisServer( ); - - /* Prefix ermitteln */ - if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_Search( Req->prefix ); - else prefix = Client; - if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - - /* An anderen Server weiterleiten? */ - if( target != Client_ThisServer( )) - { - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, Client_ID( prefix ), Req->argv[0] ); - - /* forwarden */ - IRC_WriteStrClientPrefix( target, prefix, "VERSION %s", Req->argv[0] ); - return CONNECTED; - } - - /* send version information */ - IRC_SetPenalty(Client, 1); - return IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix), - PACKAGE_NAME, PACKAGE_VERSION, - NGIRCd_DebugLevel, Conf_ServerName, - NGIRCd_VersionAddition); -} /* IRC_VERSION */ - - -static bool -write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags) -{ - return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client), - channelname, Client_User(c), - Client_HostnameDisplayed(c), - Client_ID(Client_Introducer(c)), Client_ID(c), - flags, Client_Hops(c), Client_Info(c)); -} - - -static const char * -who_flags_status(const char *client_modes) -{ - if (strchr(client_modes, 'a')) - return "G"; /* away */ - return "H"; -} - - -/** - * Return channel user mode prefix(es). - * - * @param Client The client requesting the mode prefixes. - * @param chan_user_modes String with channel user modes. - * @param str String buffer to which the prefix(es) will be appended. - * @param len Size of "str" buffer. - * @return Pointer to "str". - */ -static char * -who_flags_qualifier(CLIENT *Client, const char *chan_user_modes, - char *str, size_t len) { assert(Client != NULL); - if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) { - if (strchr(chan_user_modes, 'q')) - strlcat(str, "~", len); - if (strchr(chan_user_modes, 'a')) - strlcat(str, "&", len); - if (strchr(chan_user_modes, 'o')) - strlcat(str, "@", len); - if (strchr(chan_user_modes, 'h')) - strlcat(str, "%", len); - if (strchr(chan_user_modes, 'v')) - strlcat(str, "+", len); - - return str; - } - - if (strchr(chan_user_modes, 'q')) - strlcat(str, "~", len); - else if (strchr(chan_user_modes, 'a')) - strlcat(str, "&", len); - else if (strchr(chan_user_modes, 'o')) - strlcat(str, "@", len); - else if (strchr(chan_user_modes, 'h')) - strlcat(str, "%", len); - else if (strchr(chan_user_modes, 'v')) - strlcat(str, "+", len); - - return str; -} - - -/** - * 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_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) -{ - bool is_visible, is_member, is_ircop; - CL2CHAN *cl2chan; - const char *client_modes; - char flags[10]; - CLIENT *c; - int count = 0; - - assert( Client != NULL ); - assert( Chan != NULL ); - IRC_SetPenalty(Client, 1); - is_member = Channel_IsMemberOf(Chan, Client); - - /* Secret channel? */ - if (!is_member && strchr(Channel_Modes(Chan), 's')) - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, - Client_ID(Client), Channel_Name(Chan)); - - cl2chan = Channel_FirstMember(Chan); - for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) { - c = Channel_GetClient(cl2chan); - - client_modes = Client_Modes(c); - is_ircop = strchr(client_modes, 'o') != NULL; - if (OnlyOps && !is_ircop) - continue; - - is_visible = strchr(client_modes, 'i') == NULL; - if (is_member || is_visible) { - strlcpy(flags, who_flags_status(client_modes), - sizeof(flags)); - if (is_ircop) - strlcat(flags, "*", sizeof(flags)); - - who_flags_qualifier(Client, Channel_UserModes(Chan, c), - flags, sizeof(flags)); - - if (!write_whoreply(Client, c, Channel_Name(Chan), - flags)) - return DISCONNECTED; - count++; - } - } - - /* If there are a lot of clients, augment penalty a bit */ - if (count > MAX_RPL_WHO) - IRC_SetPenalty(Client, 1); - - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), - Channel_Name(Chan)); -} - + return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG, + Client_ID(Client)); +} /* IRC_USERS */ /** - * Send WHO reply for a "mask target" ("WHO m*sk"). + * Handler for the IRC command "VERSION". * - * @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) -{ - CLIENT *c; - CL2CHAN *cl2chan; - CHANNEL *chan; - bool client_match, is_visible; - char flags[4]; - int count = 0; - - assert (Client != NULL); - - if (Mask) - ngt_LowerStr(Mask); - - IRC_SetPenalty(Client, 3); - for (c = Client_First(); c != NULL; c = Client_Next(c)) { - if (Client_Type(c) != CLIENT_USER) - continue; - - if (OnlyOps && !Client_HasMode(c, 'o')) - continue; - - if (Mask) { - /* Match pattern against user host/server/name/nick */ - client_match = MatchCaseInsensitive(Mask, - Client_HostnameDisplayed(c)); - if (!client_match) - client_match = MatchCaseInsensitive(Mask, - Client_ID(Client_Introducer(c))); - if (!client_match) - client_match = MatchCaseInsensitive(Mask, - Client_Info(c)); - if (!client_match) - client_match = MatchCaseInsensitive(Mask, - Client_ID(c)); - if (!client_match) - continue; /* no match: skip this client */ - } - - is_visible = !Client_HasMode(c, 'i'); - - /* Target client is invisible, but mask matches exactly? */ - if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0) - is_visible = true; - - /* 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); - } - } + * @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_VERSION( CLIENT *Client, REQUEST *Req ) +{ + CLIENT *target, *prefix; - if (!is_visible) /* target user is not visible */ - continue; + assert( Client != NULL ); + assert( Req != NULL ); - if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO")) - break; + IRC_SetPenalty(Client, 1); - strlcpy(flags, who_flags_status(Client_Modes(c)), sizeof(flags)); - if (strchr(Client_Modes(c), 'o')) - strlcat(flags, "*", sizeof(flags)); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 1) + _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, prefix) - if (!write_whoreply(Client, c, "*", flags)) - return DISCONNECTED; - count++; + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, prefix, "VERSION %s", + Client_ID(target)); + return CONNECTED; } - return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), - Mask ? Mask : "*"); -} - + /* send version information */ + return IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix), + PACKAGE_NAME, PACKAGE_VERSION, + NGIRCd_DebugLevel, Conf_ServerName, + NGIRCd_VersionAddition); +} /* IRC_VERSION */ /** * 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. @@ -1020,12 +1155,11 @@ IRC_WHO(CLIENT *Client, REQUEST *Req) 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); - only_ops = false; + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2) + only_ops = false; if (Req->argc == 2) { if (strcmp(Req->argv[1], "o") == 0) only_ops = true; @@ -1038,7 +1172,6 @@ IRC_WHO(CLIENT *Client, REQUEST *Req) #endif } - IRC_SetPenalty(Client, 1); if (Req->argc >= 1) { /* Channel or mask given */ chan = Channel_Search(Req->argv[0]); @@ -1058,152 +1191,13 @@ IRC_WHO(CLIENT *Client, REQUEST *Req) return IRC_WHO_Mask(Client, NULL, only_ops); } /* IRC_WHO */ - /** - * Generate WHOIS reply of one actual client. + * Handler for the IRC "WHOIS" command. * * @param Client The client from which this command has been received. - * @param from The client requesting the information ("originator"). - * @param c The client of which information should be returned. + * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ -static bool -IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c) -{ - char str[LINE_LEN + 1]; - CL2CHAN *cl2chan; - CHANNEL *chan; - - assert(Client != NULL); - assert(from != NULL); - assert(c != NULL); - - /* Nick, user, hostname and client info */ - if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from), - Client_ID(c), Client_User(c), - Client_HostnameDisplayed(c), Client_Info(c))) - return DISCONNECTED; - - /* Server */ - if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from), - Client_ID(c), Client_ID(Client_Introducer(c)), - Client_Info(Client_Introducer(c)))) - return DISCONNECTED; - - /* Channels */ - snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG, - Client_ID(from), Client_ID(c)); - cl2chan = Channel_FirstChannelOf(c); - while (cl2chan) { - chan = Channel_GetChannel(cl2chan); - assert(chan != NULL); - - /* next */ - cl2chan = Channel_NextChannelOf(c, cl2chan); - - /* Secret channel? */ - if (strchr(Channel_Modes(chan), 's') - && !Channel_IsMemberOf(chan, Client)) - continue; - - /* Local channel and request is not from a user? */ - if (Client_Type(Client) == CLIENT_SERVER - && Channel_IsLocal(chan)) - continue; - - /* Concatenate channel names */ - if (str[strlen(str) - 1] != ':') - strlcat(str, " ", sizeof(str)); - - who_flags_qualifier(Client, Channel_UserModes(chan, c), - str, sizeof(str)); - strlcat(str, Channel_Name(chan), sizeof(str)); - - if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) { - /* Line becomes too long: send it! */ - if (!IRC_WriteStrClient(Client, "%s", str)) - return DISCONNECTED; - snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG, - Client_ID(from), Client_ID(c)); - } - } - if(str[strlen(str) - 1] != ':') { - /* There is data left to send: */ - if (!IRC_WriteStrClient(Client, "%s", str)) - return DISCONNECTED; - } - - /* Service? */ - if (Client_Type(c) == CLIENT_SERVICE && - !IRC_WriteStrClient(from, RPL_WHOISSERVICE_MSG, - Client_ID(from), Client_ID(c))) - return DISCONNECTED; - - /* IRC-Operator? */ - if (Client_HasMode(c, 'o') && Client_Type(c) != CLIENT_SERVICE && - !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG, - Client_ID(from), Client_ID(c))) - return DISCONNECTED; - - /* IRC-Bot? */ - if (Client_HasMode(c, 'B') && Client_Type(c) != CLIENT_SERVICE && - !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG, - Client_ID(from), Client_ID(c))) - return DISCONNECTED; - - /* Connected using SSL? */ - if (Conn_UsesSSL(Client_Conn(c)) && - !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from), - Client_ID(c))) - return DISCONNECTED; - - /* Registered nickname? */ - if (Client_HasMode(c, 'R') && - !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG, - Client_ID(from), Client_ID(c))) - return DISCONNECTED; - - /* Local client and requester is the user itself or an IRC Op? */ - if (Client_Conn(c) > NONE && - (from == c || (!Conf_MorePrivacy && Client_HasMode(from, 'o')))) { - /* Client hostname */ - if (!IRC_WriteStrClient(from, RPL_WHOISHOST_MSG, - Client_ID(from), Client_ID(c), Client_Hostname(c), - Conn_GetIPAInfo(Client_Conn(c)))) - return DISCONNECTED; - /* Client modes */ - if (!IRC_WriteStrClient(from, RPL_WHOISMODES_MSG, - Client_ID(from), Client_ID(c), Client_Modes(c))) - return DISCONNECTED; - } - - /* Idle and signon time (local clients only!) */ - if (!Conf_MorePrivacy && Client_Conn(c) > NONE && - !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG, - Client_ID(from), Client_ID(c), - (unsigned long)Conn_GetIdle(Client_Conn(c)), - (unsigned long)Conn_GetSignon(Client_Conn(c)))) - return DISCONNECTED; - - /* Away? */ - if (Client_HasMode(c, 'a') && - !IRC_WriteStrClient(from, RPL_AWAY_MSG, - Client_ID(from), Client_ID(c), Client_Away(c))) - return DISCONNECTED; - - return CONNECTED; -} /* IRC_WHOIS_SendReply */ - - -/** - * Handler for the IRC "WHOIS" command. - * - * See RFC 2812, 3.6.2 "Whois 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_WHOIS( CLIENT *Client, REQUEST *Req ) { @@ -1216,40 +1210,29 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); + IRC_SetPenalty(Client, 1); + /* Bad number of parameters? */ if (Req->argc < 1 || Req->argc > 2) return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); - /* Search sender of the WHOIS */ - if (Client_Type(Client) == CLIENT_SERVER) { - from = Client_Search(Req->prefix); - } else { - IRC_SetPenalty(Client, 1); - from = Client; - } - if (!from) - return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, - Client_ID(Client), Req->prefix); + _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) /* Get target server for this command */ if (Req->argc > 1) { - /* Search the target server, which can be specified as a - * nickname on that server as well: */ - target = Client_Search(Req->argv[0]); - if (!target) - return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG, - Client_ID(from), Req->argv[0]); + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, Client) } else target = Client_ThisServer(); + assert(target != NULL); - /* Forward to other server? */ - if (Client_NextHop(target) != Client_ThisServer() && - Client_Type(Client_NextHop(target)) == CLIENT_SERVER) - return IRC_WriteStrClientPrefix(target, from, - "WHOIS %s :%s", - Req->argv[0], Req->argv[1]); + /* Forward? */ + if (target != Client_ThisServer()) { + IRC_WriteStrClientPrefix(target, from, "WHOIS %s :%s", + Req->argv[0], Req->argv[1]); + return CONNECTED; + } is_remote = Client_Conn(from) < 0; strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask)); @@ -1315,27 +1298,12 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) Client_ID(from), Req->argv[Req->argc - 1]); } /* IRC_WHOIS */ - -static bool -WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry) -{ - char t_str[60]; - - (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y", - localtime(&entry->time)); - - if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix), - entry->id, entry->user, entry->host, entry->info)) - return DISCONNECTED; - - return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix), - entry->id, entry->server, t_str); -} - /** - * IRC "WHOWAS" function. - * This function implements the IRC command "WHOWHAS". It handles local - * requests and request that should be forwarded to other servers. + * Handler for the IRC "WHOWAS" command. + * + * @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_WHOWAS( CLIENT *Client, REQUEST *Req ) @@ -1354,38 +1322,19 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req ) return CONNECTED; /* Wrong number of parameters? */ - if (Req->argc > 3) - return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID(Client), Req->command); if (Req->argc < 1) - return IRC_WriteStrClient(Client, ERR_NONICKNAMEGIVEN_MSG, Client_ID(Client)); - - /* Search target */ - if (Req->argc == 3) - target = Client_Search(Req->argv[2]); - else - target = Client_ThisServer(); - - /* Get prefix */ - if (Client_Type(Client) == CLIENT_SERVER) - prefix = Client_Search(Req->prefix); - else - prefix = Client; + return IRC_WriteStrClient(Client, ERR_NONICKNAMEGIVEN_MSG, + Client_ID(Client)); - if (!prefix) - return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, - Client_ID(Client), Req->prefix); + _IRC_ARGC_LE_OR_RETURN_(Client, Req, 3) + _IRC_GET_SENDER_OR_RETURN_(prefix, Req, Client) + _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 2, prefix) - /* Forward to other server? */ + /* Forward? */ if (target != Client_ThisServer()) { - if (!target || (Client_Type(target) != CLIENT_SERVER)) - return IRC_WriteStrClient(prefix, ERR_NOSUCHSERVER_MSG, - Client_ID(prefix), Req->argv[2]); - - /* Forward */ - IRC_WriteStrClientPrefix( target, prefix, "WHOWAS %s %s %s", - Req->argv[0], Req->argv[1], - Req->argv[2] ); + IRC_WriteStrClientPrefix(target, prefix, "WHOWAS %s %s %s", + Req->argv[0], Req->argv[1], + Client_ID(target)); return CONNECTED; } @@ -1433,10 +1382,10 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req ) Client_ID(prefix), nick)) return DISCONNECTED; } - return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG, Client_ID(prefix), Req->argv[0]); + return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG, + Client_ID(prefix), Req->argv[0]); } /* IRC_WHOWAS */ - /** * Send LUSERS reply to a client. * @@ -1509,54 +1458,6 @@ IRC_Send_LUSERS(CLIENT *Client) return CONNECTED; } /* IRC_Send_LUSERS */ - -static bool -Show_MOTD_Start(CLIENT *Client) -{ - return IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG, - Client_ID( Client ), Client_ID( Client_ThisServer( ))); -} - -static bool -Show_MOTD_Sendline(CLIENT *Client, const char *msg) -{ - return IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID( Client ), msg); -} - -static bool -Show_MOTD_End(CLIENT *Client) -{ - if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client))) - return DISCONNECTED; - - if (*Conf_CloakHost) - return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG, - Client_ID(Client), - Client_Hostname(Client)); - - return CONNECTED; -} - -#ifdef SSL_SUPPORT -static bool Show_MOTD_SSLInfo(CLIENT *Client) -{ - bool ret = true; - char buf[COMMAND_LEN] = "Connected using Cipher "; - - if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23)) - return true; - - if (!Show_MOTD_Sendline(Client, buf)) - ret = false; - - return ret; -} -#else -static inline bool -Show_MOTD_SSLInfo(UNUSED CLIENT *c) -{ return true; } -#endif - GLOBAL bool IRC_Show_MOTD( CLIENT *Client ) { @@ -1589,7 +1490,6 @@ IRC_Show_MOTD( CLIENT *Client ) return Show_MOTD_End(Client); } /* IRC_Show_MOTD */ - /** * Send NAMES reply for a specific client and channel. * @@ -1659,7 +1559,6 @@ IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan) return CONNECTED; } /* IRC_Send_NAMES */ - /** * Send the ISUPPORT numeric (005). * This numeric indicates the features that are supported by this server. @@ -1678,5 +1577,4 @@ IRC_Send_ISUPPORT(CLIENT * Client) MAX_HNDL_CHANNEL_LISTS); } /* IRC_Send_ISUPPORT */ - /* -eof- */ diff --git a/src/ngircd/irc-macros.h b/src/ngircd/irc-macros.h new file mode 100644 index 00000000..bd63ec49 --- /dev/null +++ b/src/ngircd/irc-macros.h @@ -0,0 +1,83 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2013 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. + */ + +#ifndef __irc_macros_h__ +#define __irc_macros_h__ + +/** + * @file + * Macros for functions that handle IRC commands. + */ + +/** + * Make sure that number of passed parameters is less or equal than Max. + * + * If there are more than Max parameters, send an error to the client and + * return from the function. + */ +#define _IRC_ARGC_LE_OR_RETURN_(Client, Req, Max) \ +if (Req->argc > Max) \ + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \ + Client_ID(Client), Req->command); + +/** + * Make sure that number of passed parameters is greater or equal than Min. + * + * If there aren't at least Min parameters, send an error to the client and + * return from the function. + */ +#define _IRC_ARGC_GE_OR_RETURN_(Client, Req, Min) \ +if (Req->argc < Min) \ + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \ + Client_ID(Client), Req->command); + +/** + * Get sender of an IRC command. + * + * The sender is either stored in the prefix if the command has been + * received from a server or set to the client. If the sender is invalid, + * send an error to the client and return from the function. + */ +#define _IRC_GET_SENDER_OR_RETURN_(Sender, Req, Client) \ + if (Client_Type(Client) == CLIENT_SERVER) \ + Sender = Client_Search(Req->prefix); \ + else \ + Sender = Client; \ + if (!Sender) \ + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, \ + Client_ID(Client), Req->prefix); + +/** + * Get target of an IRC command and make sure that it is a server. + * + * Set the target to the local server if no target parameter is given in the + * received command, and send an error to the client and return from the + * function if the given target isn't resolvable to a server: the target + * parameter can be a server name, a nick name (then the target is set to + * the server to which this nick is connected), or a mask matching at least + * one server name in the network. + */ +#define _IRC_GET_TARGET_SERVER_OR_RETURN_(Target, Req, Argc, From) \ + if (Req->argc > Argc) { \ + Target = Client_Search(Req->argv[Argc]); \ + if (!Target) \ + Target = Client_SearchServer(Req->argv[Argc]); \ + if (!Target) \ + return IRC_WriteStrClient(From, ERR_NOSUCHSERVER_MSG, \ + Client_ID(From), Req->argv[Argc]); \ + if (Client_Type(Target) != CLIENT_SERVER) \ + Target = Client_Introducer(Target); \ + } else \ + Target = Client_ThisServer(); + +#endif /* __irc_macros_h__ */ + +/* -eof- */