X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc.c;h=5288a8ce7f9b81455542cff80da962b276a06bbb;hp=5bfee00f2f95f33dc54ea45b55a881f163357f31;hb=cd6e40493c2a156761787ae31de47602f43f83a4;hpb=8465653c6efa9ce0a976f0a6c8fd63a3ab3b2bd1 diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index 5bfee00f..5288a8ce 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -9,11 +9,50 @@ * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS. * - * $Id: irc.c,v 1.65 2002/02/23 00:03:54 alex Exp $ + * $Id: irc.c,v 1.77 2002/02/27 17:05:41 alex Exp $ * * irc.c: IRC-Befehle * * $Log: irc.c,v $ + * Revision 1.77 2002/02/27 17:05:41 alex + * - PRIVMSG beachtet nun die Channel-Modes "n" und "m". + * + * Revision 1.76 2002/02/27 16:04:14 alex + * - Bug bei belegtem Nickname bei User-Registrierung (NICK-Befehl) behoben. + * + * Revision 1.75 2002/02/27 15:23:27 alex + * - NAMES beachtet nun das "invisible" Flag ("i") von Usern. + * + * Revision 1.74 2002/02/27 03:44:53 alex + * - gerade eben in SQUIT eingefuehrten Bug behoben: entfernte Server werden nun + * nur noch geloescht, die Verbindung, von der SQUIT kam, bleibt wieder offen. + * + * Revision 1.73 2002/02/27 03:08:05 alex + * - Log-Meldungen bei SQUIT erneut ueberarbeitet ... + * + * Revision 1.72 2002/02/27 02:26:58 alex + * - SQUIT wird auf jeden Fall geforwarded, zudem besseres Logging. + * + * Revision 1.71 2002/02/27 00:50:05 alex + * - einige unnoetige Client_NextHop()-Aufrufe entfernt. + * - NAMES korrigiert und komplett implementiert. + * + * Revision 1.70 2002/02/26 22:06:40 alex + * - Nick-Aenderungen werden nun wieder korrekt ins Logfile geschrieben. + * + * Revision 1.69 2002/02/26 20:52:40 alex + * - VERSION wurde falsch weitergeleitet und beantwortet (Prefix nicht beachtet) + * + * Revision 1.68 2002/02/25 17:46:27 alex + * - an User wird nun immer ein "komplettes" Prefix verschickt. + * + * Revision 1.67 2002/02/25 13:21:25 alex + * - WHOIS wird nicht mehr automatisch an den "Original-Server" weiterge- + * leitet: war eh nicht RFC-konform und machte Probleme mit Clients. + * + * Revision 1.66 2002/02/23 21:39:48 alex + * - IRC-Befehl KILL sowie Kills bei Nick Collsisions implementiert. + * * Revision 1.65 2002/02/23 00:03:54 alex * - Ergebnistyp von Conn_GetIdle() und Conn_LastPing() auf "time_t" geaendert. * @@ -279,11 +318,13 @@ LOCAL BOOLEAN Hello_User( CLIENT *Client ); LOCAL BOOLEAN Show_MOTD( CLIENT *Client ); -LOCAL VOID Kill_Nick( CHAR *Nick ); +LOCAL VOID Kill_Nick( CHAR *Nick, CHAR *Reason ); LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ); LOCAL BOOLEAN Send_LUSERS( CLIENT *Client ); +CHAR *Get_Prefix( CLIENT *Target, CLIENT *Client ); + GLOBAL VOID IRC_Init( VOID ) { @@ -330,7 +371,7 @@ GLOBAL BOOLEAN IRC_WriteStrClientPrefix( CLIENT *Client, CLIENT *Prefix, CHAR *F vsnprintf( buffer, 1000, Format, ap ); va_end( ap ); - return Conn_WriteStr( Client_Conn( Client_NextHop( Client )), ":%s %s", Client_ID( Prefix ), buffer ); + return Conn_WriteStr( Client_Conn( Client_NextHop( Client )), ":%s %s", Get_Prefix( Client_NextHop( Client ), Prefix ), buffer ); } /* IRC_WriteStrClientPrefix */ @@ -352,8 +393,8 @@ GLOBAL BOOLEAN IRC_WriteStrChannel( CLIENT *Client, CHANNEL *Chan, BOOLEAN Remot GLOBAL BOOLEAN IRC_WriteStrChannelPrefix( CLIENT *Client, CHANNEL *Chan, CLIENT *Prefix, BOOLEAN Remote, CHAR *Format, ... ) { + BOOLEAN sock[MAX_CONNECTIONS], is_server[MAX_CONNECTIONS], ok = CONNECTED; CHAR buffer[1000]; - BOOLEAN sock[MAX_CONNECTIONS], ok = CONNECTED; CL2CHAN *cl2chan; CLIENT *c; INT s, i; @@ -390,6 +431,8 @@ GLOBAL BOOLEAN IRC_WriteStrChannelPrefix( CLIENT *Client, CHANNEL *Chan, CLIENT assert( s >= 0 ); assert( s < MAX_CONNECTIONS ); sock[s] = TRUE; + if( Client_Type( c ) == CLIENT_SERVER ) is_server[s] = TRUE; + else is_server[s] = FALSE; } cl2chan = Channel_NextMember( Chan, cl2chan ); } @@ -399,7 +442,8 @@ GLOBAL BOOLEAN IRC_WriteStrChannelPrefix( CLIENT *Client, CHANNEL *Chan, CLIENT { if( sock[i] ) { - ok = Conn_WriteStr( i, ":%s %s", Client_ID( Prefix ), buffer ); + if( is_server[i] ) ok = Conn_WriteStr( i, ":%s %s", Client_ID( Prefix ), buffer ); + else ok = Conn_WriteStr( i, ":%s %s", Client_Mask( Prefix ), buffer ); if( ! ok ) break; } } @@ -451,7 +495,7 @@ GLOBAL VOID IRC_WriteStrServersPrefix( CLIENT *ExceptOf, CLIENT *Prefix, CHAR *F GLOBAL BOOLEAN IRC_WriteStrRelatedPrefix( CLIENT *Client, CLIENT *Prefix, BOOLEAN Remote, CHAR *Format, ... ) { - BOOLEAN sock[MAX_CONNECTIONS], ok = CONNECTED; + BOOLEAN sock[MAX_CONNECTIONS], is_server[MAX_CONNECTIONS], ok = CONNECTED; CL2CHAN *chan_cl2chan, *cl2chan; CHAR buffer[1000]; CHANNEL *chan; @@ -495,6 +539,8 @@ GLOBAL BOOLEAN IRC_WriteStrRelatedPrefix( CLIENT *Client, CLIENT *Prefix, BOOLEA assert( s >= 0 ); assert( s < MAX_CONNECTIONS ); sock[s] = TRUE; + if( Client_Type( c ) == CLIENT_SERVER ) is_server[s] = TRUE; + else is_server[s] = FALSE; } cl2chan = Channel_NextMember( chan, cl2chan ); } @@ -508,7 +554,8 @@ GLOBAL BOOLEAN IRC_WriteStrRelatedPrefix( CLIENT *Client, CLIENT *Prefix, BOOLEA { if( sock[i] ) { - ok = Conn_WriteStr( i, ":%s %s", Client_ID( Prefix ), buffer ); + if( is_server[i] ) ok = Conn_WriteStr( i, ":%s %s", Client_ID( Prefix ), buffer ); + else ok = Conn_WriteStr( i, ":%s %s", Client_Mask( Prefix ), buffer ); if( ! ok ) break; } } @@ -874,18 +921,27 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) if( Client_Type( Client ) == CLIENT_USER ) IRC_WriteStrClientPrefix( Client, Client, "NICK :%s", Req->argv[0] ); IRC_WriteStrServersPrefix( Client, target, "NICK :%s", Req->argv[0] ); IRC_WriteStrRelatedPrefix( target, target, FALSE, "NICK :%s", Req->argv[0] ); - - /* Client-Nick registrieren */ - Client_SetID( target, Req->argv[0] ); if(( Client_Type( target ) != CLIENT_USER ) && ( Client_Type( target ) != CLIENT_SERVER )) { /* Neuer Client */ - Log( LOG_DEBUG, "Connection %d: got NICK command ...", Client_Conn( Client )); + Log( LOG_DEBUG, "Connection %d: got valid NICK command ...", Client_Conn( Client )); + + /* Client-Nick registrieren */ + Client_SetID( target, Req->argv[0] ); + + /* schon ein USER da? Dann registrieren! */ if( Client_Type( Client ) == CLIENT_GOTUSER ) return Hello_User( Client ); else Client_SetType( Client, CLIENT_GOTNICK ); } - else Log( LOG_INFO, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] ); + else + { + /* Nick-Aenderung */ + Log( LOG_INFO, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] ); + + /* neuen Client-Nick speichern */ + Client_SetID( target, Req->argv[0] ); + } return CONNECTED; } @@ -904,7 +960,7 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) * sowohl der neue, als auch der alte Client muessen nun * disconnectiert werden. */ Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] ); - Kill_Nick( Req->argv[0] ); + Kill_Nick( Req->argv[0], "Nick collision" ); return CONNECTED; } @@ -913,7 +969,7 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) if( ! intr_c ) { Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] ); - Kill_Nick( Req->argv[0] ); + Kill_Nick( Req->argv[0], "Unknown server" ); return CONNECTED; } @@ -925,7 +981,7 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) * Der Client muss disconnectiert werden, damit der Netz- * status konsistent bleibt. */ Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client )); - Kill_Nick( Req->argv[0] ); + Kill_Nick( Req->argv[0], "Server error" ); return CONNECTED; } @@ -959,7 +1015,7 @@ GLOBAL BOOLEAN IRC_USER( CLIENT *Client, REQUEST *Req ) Client_SetUser( Client, Req->argv[0], FALSE ); Client_SetInfo( Client, Req->argv[3] ); - Log( LOG_DEBUG, "Connection %d: got USER command ...", Client_Conn( Client )); + Log( LOG_DEBUG, "Connection %d: got valid USER command ...", Client_Conn( Client )); if( Client_Type( Client ) == CLIENT_GOTNICK ) return Hello_User( Client ); else Client_SetType( Client, CLIENT_GOTUSER ); return CONNECTED; @@ -1017,6 +1073,7 @@ GLOBAL BOOLEAN IRC_QUIT( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_SQUIT( CLIENT *Client, REQUEST *Req ) { CLIENT *target; + CHAR msg[LINE_LEN + 64]; assert( Client != NULL ); assert( Req != NULL ); @@ -1027,28 +1084,36 @@ GLOBAL BOOLEAN IRC_SQUIT( CLIENT *Client, REQUEST *Req ) /* Falsche Anzahl Parameter? */ if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + Log( LOG_DEBUG, "Got SQUIT from %s for \"%s\": \"%s\" ...", Client_ID( Client ), Req->argv[0], Req->argv[1] ); + + /* SQUIT an alle Server weiterleiten */ + IRC_WriteStrServers( Client, "SQUIT %s :%s", Req->argv[0], Req->argv[1] ); + target = Client_GetFromID( Req->argv[0] ); if( ! target ) { - Log( LOG_ERR, "Got SQUIT from %s for unknown server \%s\"!?", Client_ID( Client ), Req->argv[0] ); + Log( LOG_ERR, "Got SQUIT from %s for unknown server \"%s\"!?", Client_ID( Client ), Req->argv[0] ); return CONNECTED; } - if( target == Client ) Log( LOG_DEBUG, "Got SQUIT from %s: %s", Client_ID( Client ), Req->argv[1] ); - else Log( LOG_DEBUG, "Got SQUIT from %s for %s: %s", Client_ID( Client ), Client_ID( target ), Req->argv[1] ); - - /* SQUIT an alle Server weiterleiten */ - IRC_WriteStrServers( Client, "SQUIT %s :%s", Req->argv[0], Req->argv[1] ); + if( Req->argv[1][0] ) + { + if( strlen( Req->argv[1] ) > LINE_LEN ) Req->argv[1][LINE_LEN] = '\0'; + sprintf( msg, "%s (SQUIT from %s).", Req->argv[1], Client_ID( Client )); + } + else sprintf( msg, "Got SQUIT from %s.", Client_ID( Client )); if( Client_Conn( target ) > NONE ) { - if( Req->argv[1][0] ) Conn_Close( Client_Conn( target ), "Got SQUIT command.", Req->argv[1], TRUE ); - else Conn_Close( Client_Conn( target ), "Got SQUIT command.", NULL, TRUE ); + /* dieser Server hat die Connection */ + if( Req->argv[1][0] ) Conn_Close( Client_Conn( target ), msg, Req->argv[1], TRUE ); + else Conn_Close( Client_Conn( target ), msg, NULL, TRUE ); return DISCONNECTED; } else { - Client_Destroy( target, "Got SQUIT command.", Req->argv[1] ); + /* Verbindung hielt anderer Server */ + Client_Destroy( target, msg, Req->argv[1] ); return CONNECTED; } } /* IRC_SQUIT */ @@ -1078,7 +1143,7 @@ GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req ) if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_GetFromID( Req->prefix ); else from = Client; if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix ); - return IRC_WriteStrClientPrefix( Client_NextHop( target ), from, "PING %s :%s", Client_ID( from ), Req->argv[1] ); + return IRC_WriteStrClientPrefix( target, from, "PING %s :%s", Client_ID( from ), Req->argv[1] ); } } @@ -1111,7 +1176,7 @@ GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req ) if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_GetFromID( Req->prefix ); else from = Client; if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix ); - return IRC_WriteStrClientPrefix( Client_NextHop( target ), from, "PONG %s :%s", Client_ID( from ), Req->argv[1] ); + return IRC_WriteStrClientPrefix( target, from, "PONG %s :%s", Client_ID( from ), Req->argv[1] ); } } @@ -1141,7 +1206,8 @@ GLOBAL BOOLEAN IRC_MOTD( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req ) { - CLIENT *to, *from; + BOOLEAN is_member, has_voice, is_op, ok; + CLIENT *cl, *from; CHANNEL *chan; assert( Client != NULL ); @@ -1158,18 +1224,34 @@ GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req ) else from = Client; if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - to = Client_Search( Req->argv[0] ); - if( to ) + cl = Client_Search( Req->argv[0] ); + if( cl ) { /* Okay, Ziel ist ein User */ if( Client_Conn( from ) > NONE ) Conn_UpdateIdle( Client_Conn( from )); - return IRC_WriteStrClientPrefix( to, from, "PRIVMSG %s :%s", Client_ID( to ), Req->argv[1] ); + return IRC_WriteStrClientPrefix( cl, from, "PRIVMSG %s :%s", Client_ID( cl ), Req->argv[1] ); } chan = Channel_Search( Req->argv[0] ); if( chan ) { /* Okay, Ziel ist ein Channel */ + is_member = has_voice = is_op = FALSE; + if( Channel_IsMemberOf( chan, from )) + { + is_member = TRUE; + if( strchr( Channel_UserModes( chan, from ), 'v' )) has_voice = TRUE; + if( strchr( Channel_UserModes( chan, from ), 'o' )) is_op = TRUE; + } + + /* pruefen, ob Client in Channel schreiben darf */ + ok = TRUE; + if( strchr( Channel_Modes( chan ), 'n' ) && ( ! is_member )) ok = FALSE; + if( strchr( Channel_Modes( chan ), 'm' ) && ( ! is_op ) && ( ! has_voice )) ok = FALSE; + + if( ! ok ) return IRC_WriteStrClient( from, ERR_CANNOTSENDTOCHAN_MSG, Client_ID( from ), Req->argv[0] ); + + /* Text senden */ if( Client_Conn( from ) > NONE ) Conn_UpdateIdle( Client_Conn( from )); return IRC_WriteStrChannelPrefix( Client, chan, from, TRUE, "PRIVMSG %s :%s", Req->argv[0], Req->argv[1] ); } @@ -1606,46 +1688,96 @@ GLOBAL BOOLEAN IRC_RESTART( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_NAMES( CLIENT *Client, REQUEST *Req ) { - CHAR rpl[COMMAND_LEN]; - CLIENT *c; + CHAR rpl[COMMAND_LEN], *ptr; + CLIENT *target, *from, *c; + CHANNEL *chan; assert( Client != NULL ); assert( Req != NULL ); - if( Client_Type( Client ) != CLIENT_USER ) return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); + if(( Client_Type( Client ) != CLIENT_USER ) && ( Client_Type( Client ) != CLIENT_SERVER )) return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); /* Falsche Anzahl Parameter? */ - if( Req->argc != 0 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + /* From aus Prefix ermitteln */ + if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_GetFromID( Req->prefix ); + else from = Client; + if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix ); + + if( Req->argc == 2 ) + { + /* an anderen Server forwarden */ + target = Client_GetFromID( Req->argv[1] ); + if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[1] ); + + if( target != Client_ThisServer( )) + { + /* Ok, anderer Server ist das Ziel: forwarden */ + return IRC_WriteStrClientPrefix( target, from, "NAMES %s :%s", Req->argv[0], Req->argv[1] ); + } + } + + if( Req->argc > 0 ) + { + /* bestimmte Channels durchgehen */ + ptr = strtok( Req->argv[0], "," ); + while( ptr ) + { + chan = Channel_Search( ptr ); + if( chan ) + { + /* Namen ausgeben */ + if( ! Send_NAMES( from, chan )) return DISCONNECTED; + } + if( ! IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), ptr )) return DISCONNECTED; + + /* naechsten Namen ermitteln */ + ptr = strtok( NULL, "," ); + } + return CONNECTED; + } + + /* alle Channels durchgehen */ + chan = Channel_First( ); + while( chan ) + { + /* Namen ausgeben */ + if( ! Send_NAMES( from, chan )) return DISCONNECTED; - /* Noch alle User ausgeben, die in keinem Channel sind */ - rpl[0] = '\0'; + /* naechster Channel */ + chan = Channel_Next( chan ); + } + + /* Nun noch alle Clients ausgeben, die in keinem Channel sind */ c = Client_First( ); + sprintf( rpl, RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" ); while( c ) { - if( Client_Type( c ) == CLIENT_USER ) + if(( Client_Type( c ) == CLIENT_USER ) && ( Channel_FirstChannelOf( c ) == NULL ) && ( ! strchr( Client_Modes( c ), 'i' ))) { - /* Okay, das ist ein User */ + /* Okay, das ist ein User: anhaengen */ + if( rpl[strlen( rpl ) - 1] != ':' ) strcat( rpl, " " ); strcat( rpl, Client_ID( c )); - strcat( rpl, " " ); - } - /* Antwort zu lang? Splitten. */ - if( strlen( rpl ) > 480 ) - { - if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0'; - if( ! IRC_WriteStrClient( Client, RPL_NAMREPLY_MSG, Client_ID( Client ), "*", "*", rpl )) return DISCONNECTED; - rpl[0] = '\0'; + if( strlen( rpl ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 )) + { + /* Zeile wird zu lang: senden! */ + if( ! IRC_WriteStrClient( from, rpl )) return DISCONNECTED; + sprintf( rpl, RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" ); + } } - + + /* naechster Client */ c = Client_Next( c ); } - if( rpl[0] ) + if( rpl[strlen( rpl ) - 1] != ':') { /* es wurden User gefunden */ - if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0'; - if( ! IRC_WriteStrClient( Client, RPL_NAMREPLY_MSG, Client_ID( Client ), "*", "*", rpl )) return DISCONNECTED; + if( ! IRC_WriteStrClient( from, rpl )) return DISCONNECTED; } - return IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), "*" ); + + return IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), "*" ); } /* IRC_NAMES */ @@ -1719,17 +1851,11 @@ GLOBAL BOOLEAN IRC_WHOIS( CLIENT *Client, REQUEST *Req ) if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[1] ); ptr = Req->argv[1]; } -#ifndef STRICT_RFC - else if( Client_Conn( c ) == NONE ) - { - /* Client ist nicht von uns. Ziel-Server suchen */ - target = c; - ptr = Req->argv[0]; - } -#endif - else target = NULL; + else target = Client_ThisServer( ); + + assert( target != NULL ); - if( target && ( Client_NextHop( target ) != Client_ThisServer( )) && ( Client_Type( Client_NextHop( target )) == CLIENT_SERVER )) return IRC_WriteStrClientPrefix( target, from, "WHOIS %s :%s", Req->argv[0], ptr ); + if(( Client_NextHop( target ) != Client_ThisServer( )) && ( Client_Type( Client_NextHop( target )) == CLIENT_SERVER )) return IRC_WriteStrClientPrefix( target, from, "WHOIS %s :%s", Req->argv[0], ptr ); /* Nick, User und Name */ if( ! IRC_WriteStrClient( from, RPL_WHOISUSER_MSG, Client_ID( from ), Client_ID( c ), Client_User( c ), Client_Hostname( c ), Client_Info( c ))) return DISCONNECTED; @@ -2004,10 +2130,13 @@ GLOBAL BOOLEAN IRC_JOIN( CLIENT *Client, REQUEST *Req ) { /* an Client bestaetigen */ IRC_WriteStrClientPrefix( Client, target, "JOIN :%s", channame ); + /* Topic an Client schicken */ IRC_WriteStrClient( Client, RPL_TOPIC_MSG, Client_ID( Client ), channame, "What a wonderful channel!" ); + /* Mitglieder an Client Melden */ Send_NAMES( Client, chan ); + IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( chan )); } /* naechsten Namen ermitteln */ @@ -2055,7 +2184,7 @@ GLOBAL BOOLEAN IRC_PART( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_VERSION( CLIENT *Client, REQUEST *Req ) { - CLIENT *target; + CLIENT *target, *prefix; assert( Client != NULL ); assert( Req != NULL ); @@ -2067,18 +2196,58 @@ GLOBAL BOOLEAN IRC_VERSION( CLIENT *Client, REQUEST *Req ) if( Req->argc == 1 ) target = Client_GetFromID( Req->argv[0] ); else target = Client_ThisServer( ); + /* Prefix ermitteln */ + if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_GetFromID( Req->prefix ); + else prefix = Client; + if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix ); + /* An anderen Server weiterleiten? */ if( target != Client_ThisServer( )) { if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[0] ); - IRC_WriteStrClientPrefix( Client_NextHop( target ), Client, "VERSION %s", Req->argv[0] ); + + /* forwarden */ + IRC_WriteStrClientPrefix( target, prefix, "VERSION %s", Req->argv[0] ); return CONNECTED; } - return IRC_WriteStrClient( Client, RPL_VERSION_MSG, Client_ID( Client ), NGIRCd_DebugLevel, Conf_ServerName, NGIRCd_VersionAddition( )); + /* mit Versionsinfo antworten */ + return IRC_WriteStrClient( Client, RPL_VERSION_MSG, Client_ID( prefix ), NGIRCd_DebugLevel, Conf_ServerName, NGIRCd_VersionAddition( )); } /* IRC_VERSION */ +GLOBAL BOOLEAN IRC_KILL( CLIENT *Client, REQUEST *Req ) +{ + CLIENT *prefix, *c; + + assert( Client != NULL ); + assert( Req != NULL ); + + if( Client_Type( Client ) != CLIENT_SERVER ) return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); + + /* Falsche Anzahl Parameter? */ + if(( Req->argc != 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + prefix = Client_GetFromID( Req->prefix ); + if( ! prefix ) + { + Log( LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!", Req->prefix ); + prefix = Client_ThisServer( ); + } + + Log( LOG_NOTICE, "Got KILL command from \"%s\" for \"%s\": %s", Client_Mask( prefix ), Req->argv[0], Req->argv[1] ); + + /* andere Server benachrichtigen */ + IRC_WriteStrServersPrefix( Client, prefix, "KILL %s :%s", Req->argv[0], Req->argv[1] ); + + /* haben wir selber einen solchen Client? */ + c = Client_GetFromID( Req->argv[0] ); + if( c && ( Client_Conn( c ) != NONE )) Conn_Close( Client_Conn( c ), NULL, Req->argv[1], TRUE ); + + return CONNECTED; +} /* IRC_KILL */ + + LOCAL BOOLEAN Hello_User( CLIENT *Client ) { assert( Client != NULL ); @@ -2145,16 +2314,27 @@ LOCAL BOOLEAN Show_MOTD( CLIENT *Client ) } /* Show_MOTD */ -LOCAL VOID Kill_Nick( CHAR *Nick ) +LOCAL VOID Kill_Nick( CHAR *Nick, CHAR *Reason ) { - Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected!", Nick ); - /* FIXME */ - Log( LOG_ALERT, "[Kill_Nick() not implemented - OOOPS!]" ); + CLIENT *c; + + assert( Nick != NULL ); + assert( Reason != NULL ); + + Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason ); + + /* andere Server benachrichtigen */ + IRC_WriteStrServers( NULL, "KILL %s :%s", Nick, Reason ); + + /* Ggf. einen eigenen Client toeten */ + c = Client_GetFromID( Nick ); + if( c && ( Client_Conn( c ) != NONE )) Conn_Close( Client_Conn( c ), NULL, Reason, TRUE ); } /* Kill_Nick */ LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ) { + BOOLEAN is_visible, is_member; CHAR str[LINE_LEN + 1]; CL2CHAN *cl2chan; CLIENT *cl; @@ -2162,6 +2342,9 @@ LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ) assert( Client != NULL ); assert( Chan != NULL ); + if( Channel_IsMemberOf( Chan, Client )) is_member = TRUE; + else is_member = FALSE; + /* Alle Mitglieder suchen */ sprintf( str, RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan )); cl2chan = Channel_FirstMember( Chan ); @@ -2169,17 +2352,23 @@ LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ) { cl = Channel_GetClient( cl2chan ); - /* Nick anhaengen */ - if( str[strlen( str ) - 1] != ':' ) strcat( str, " " ); - if( strchr( Channel_UserModes( Chan, cl ), 'v' )) strcat( str, "+" ); - if( strchr( Channel_UserModes( Chan, cl ), 'o' )) strcat( str, "@" ); - strcat( str, Client_ID( cl )); + if( strchr( Client_Modes( cl ), 'i' )) is_visible = FALSE; + else is_visible = TRUE; - if( strlen( str ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 )) + if( is_member || is_visible ) { - /* Zeile wird zu lang: senden! */ - if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; - sprintf( str, RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan )); + /* Nick anhaengen */ + if( str[strlen( str ) - 1] != ':' ) strcat( str, " " ); + if( strchr( Channel_UserModes( Chan, cl ), 'v' )) strcat( str, "+" ); + if( strchr( Channel_UserModes( Chan, cl ), 'o' )) strcat( str, "@" ); + strcat( str, Client_ID( cl )); + + if( strlen( str ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 )) + { + /* Zeile wird zu lang: senden! */ + if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; + sprintf( str, RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan )); + } } /* naechstes Mitglied suchen */ @@ -2191,9 +2380,6 @@ LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ) if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; } - /* Ende anzeigen */ - IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( Chan )); - return CONNECTED; } /* Send_NAMES */ @@ -2231,4 +2417,14 @@ LOCAL BOOLEAN Send_LUSERS( CLIENT *Client ) } /* Send_LUSERS */ +CHAR *Get_Prefix( CLIENT *Target, CLIENT *Client ) +{ + assert( Target != NULL ); + assert( Client != NULL ); + + if( Client_Type( Target ) == CLIENT_SERVER ) return Client_ID( Client ); + else return Client_Mask( Client ); +} /* Get_Prefix */ + + /* -eof- */