X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc.c;h=93d284c3ac547906b0627cfbb8d1b29a323ee8fd;hp=eb1dd807b871c73ad9e6ce1403afb46c4ddca338;hb=d59f02904374ca5f0ac16bf493448eb516dd2df8;hpb=0595f42fbe3c666fb012433e5fa06074abfc13b9 diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index eb1dd807..93d284c3 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -9,11 +9,109 @@ * 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.50 2002/02/11 01:03:20 alex Exp $ + * $Id: irc.c,v 1.80 2002/02/27 20:33:13 alex Exp $ * * irc.c: IRC-Befehle * * $Log: irc.c,v $ + * Revision 1.80 2002/02/27 20:33:13 alex + * - Channel-Topics implementiert. + * + * Revision 1.79 2002/02/27 18:57:21 alex + * - PRIVMSG zeugt nun bei Texten an User an, wenn diese "away" sind. + * + * Revision 1.78 2002/02/27 18:23:45 alex + * - IRC-Befehl "AWAY" implementert. + * + * 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. + * + * Revision 1.64 2002/02/19 20:06:45 alex + * - User-Registrierung wird nicht mehr als Nick-Aenderung protokolliert, + * - VERSION liefert nun doch wieder den Debug-Status im Reply. + * + * Revision 1.63 2002/02/19 02:21:17 alex + * - der Debug-Level wird bei VERSION nicht mehr geliefert. Grund: a) absolut + * unnoetig und b) Compiler-Fehler, wenn ohne Debug-Code configure'd ;-)) + * + * Revision 1.62 2002/02/17 23:38:58 alex + * - neuer IRC-Befehl VERSION implementiert: IRC_VERSION(). + * + * Revision 1.61 2002/02/17 19:03:12 alex + * - NICK-Aenderungen wurden dem User selber mit dem falschen Prefix geliefert. + * + * Revision 1.60 2002/02/17 17:43:14 alex + * - Fehlerhafte Modes werden nun ausfuehrlicher an den Client gemeldet. + * + * Revision 1.59 2002/02/17 17:30:21 alex + * - NICK-Aenderungen werden an alle Server und betroffene Clients gemeldet. + * - Neue Funktion IRC_WriteStrRelatedPrefix(). + * + * Revision 1.58 2002/02/17 17:18:59 alex + * - NICK korrigiert. + * + * Revision 1.57 2002/02/13 23:05:29 alex + * - Nach Connect eines Users werden LUSERS-Informationen angezeigt. + * + * Revision 1.56 2002/02/13 17:52:27 alex + * - es werden nun Channel- und User-Modes von Usern angenommen. + * + * Revision 1.55 2002/02/12 14:40:37 alex + * - via NJOIN gemeldete Benutzer wurden nicht in Channels bekannt gemacht. + * + * Revision 1.54 2002/02/11 23:33:35 alex + * - weitere Anpassungen an Channel-Modes und Channel-User-Modes. + * + * Revision 1.53 2002/02/11 16:06:21 alex + * - Die Quelle von MODE-Aenderungen wird nun korrekt weitergegeben. + * + * Revision 1.52 2002/02/11 15:52:21 alex + * - PONG an den Server selber wurde faelschlicherweise versucht zu forwarden. + * - Channel-Modes wurden falsch geliefert (als User-Modes). + * + * Revision 1.51 2002/02/11 15:15:53 alex + * - PING und PONG werden nun auch korrekt an andere Server geforwarded. + * - bei MODE-Meldungen wird der letzte Parameter nicht mehr mit ":" getrennt. + * * Revision 1.50 2002/02/11 01:03:20 alex * - Aenderungen und Anpassungen an Channel-Modes und Channel-User-Modes: * Modes werden besser geforwarded, lokale User, fuer die ein Channel @@ -229,9 +327,12 @@ 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 ) @@ -279,7 +380,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 */ @@ -301,11 +402,11 @@ 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, i; CL2CHAN *cl2chan; CLIENT *c; - INT s; + INT s, i; va_list ap; assert( Client != NULL ); @@ -339,6 +440,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 ); } @@ -348,7 +451,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; } } @@ -398,6 +502,77 @@ GLOBAL VOID IRC_WriteStrServersPrefix( CLIENT *ExceptOf, CLIENT *Prefix, CHAR *F } /* IRC_WriteStrServersPrefix */ +GLOBAL BOOLEAN IRC_WriteStrRelatedPrefix( CLIENT *Client, CLIENT *Prefix, BOOLEAN Remote, CHAR *Format, ... ) +{ + BOOLEAN sock[MAX_CONNECTIONS], is_server[MAX_CONNECTIONS], ok = CONNECTED; + CL2CHAN *chan_cl2chan, *cl2chan; + CHAR buffer[1000]; + CHANNEL *chan; + va_list ap; + CLIENT *c; + INT i, s; + + assert( Client != NULL ); + assert( Prefix != NULL ); + assert( Format != NULL ); + + va_start( ap, Format ); + vsnprintf( buffer, 1000, Format, ap ); + va_end( ap ); + + /* initialisieren */ + for( i = 0; i < MAX_CONNECTIONS; i++ ) sock[i] = FALSE; + + /* An alle Clients, die in einem Channel mit dem "Ausloeser" sind, + * den Text schicken. An Remote-Server aber jeweils nur einmal. */ + chan_cl2chan = Channel_FirstChannelOf( Client ); + while( chan_cl2chan ) + { + /* Channel des Users durchsuchen */ + chan = Channel_GetChannel( chan_cl2chan ); + cl2chan = Channel_FirstMember( chan ); + while( cl2chan ) + { + c = Channel_GetClient( cl2chan ); + if( ! Remote ) + { + if( Client_Conn( c ) <= NONE ) c = NULL; + else if( Client_Type( c ) == CLIENT_SERVER ) c = NULL; + } + if( c ) c = Client_NextHop( c ); + + if( c && ( c != Client )) + { + /* Ok, anderer Client */ + s = Client_Conn( c ); + 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 ); + } + + /* naechsten Channel */ + chan_cl2chan = Channel_NextChannelOf( Client, chan_cl2chan ); + } + + /* Senden ... */ + for( i = 0; i < MAX_CONNECTIONS; i++ ) + { + if( sock[i] ) + { + 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; + } + } + return ok; +} /* IRC_WriteStrRelatedPrefix */ + + + GLOBAL BOOLEAN IRC_PASS( CLIENT *Client, REQUEST *Req ) { assert( Client != NULL ); @@ -527,7 +702,7 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) if( Client_Conn( c ) > NONE ) { /* Dem gefundenen Server gleich den neuen - * Server bekannt machen */ + * Server bekannt machen */ if( ! IRC_WriteStrClient( c, "SERVER %s %d %d :%s", Client_ID( Client ), Client_Hops( Client ) + 1, Client_MyToken( Client ), Client_Info( Client ))) return DISCONNECTED; } @@ -641,8 +816,8 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_NJOIN( CLIENT *Client, REQUEST *Req ) { + CHAR *channame, *ptr, modes[8]; BOOLEAN is_op, is_voiced; - CHAR *channame, *ptr; CHANNEL *chan; CLIENT *c; @@ -677,6 +852,17 @@ GLOBAL BOOLEAN IRC_NJOIN( CLIENT *Client, REQUEST *Req ) if( is_op ) Channel_UserModeAdd( chan, c, 'o' ); if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' ); + + /* im Channel bekannt machen */ + IRC_WriteStrChannelPrefix( Client, chan, c, FALSE, "JOIN :%s", channame ); + + /* Channel-User-Modes setzen */ + strcpy( modes, Channel_UserModes( chan, c )); + if( modes[0] ) + { + /* Modes im Channel bekannt machen */ + IRC_WriteStrChannelPrefix( Client, chan, Client, FALSE, "MODE %s +%s %s", channame, modes, Client_ID( c )); + } } else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame ); @@ -724,10 +910,10 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) target = Client; } +#ifndef STRICT_RFC /* Wenn der Client zu seinem eigenen Nick wechseln will, so machen * wir nichts. So macht es das Original und mind. Snak hat probleme, * wenn wir es nicht so machen. Ob es so okay ist? Hm ... */ -#ifndef STRICT_RFC if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 ) return CONNECTED; #endif @@ -739,30 +925,33 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) if( ! Client_CheckNick( target, Req->argv[0] )) return CONNECTED; } - if( Client_Type( Client ) == CLIENT_USER ) - { - /* Nick-Aenderung: allen mitteilen! */ - Log( LOG_INFO, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] ); - IRC_WriteStrClient( Client, "NICK :%s", Req->argv[0] ); - IRC_WriteStrServersPrefix( NULL, Client, "NICK :%s", Req->argv[0] ); - } - else if( Client_Type( Client ) == CLIENT_SERVER ) - { - /* Nick-Aenderung: allen mitteilen! */ - Log( LOG_DEBUG, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] ); - IRC_WriteStrServersPrefix( Client, target, "NICK :%s", Req->argv[0] ); - } - - /* Client-Nick registrieren */ - Client_SetID( target, Req->argv[0] ); + /* Nick-Aenderung: allen mitteilen! */ + + 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] ); 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 + { + /* 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; } else if( Client_Type( Client ) == CLIENT_SERVER ) @@ -780,7 +969,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; } @@ -789,7 +978,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; } @@ -801,7 +990,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; } @@ -835,7 +1024,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; @@ -893,6 +1082,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 ); @@ -903,28 +1093,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 */ @@ -932,6 +1130,8 @@ GLOBAL BOOLEAN IRC_SQUIT( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req ) { + CLIENT *target, *from; + assert( Client != NULL ); assert( Req != NULL ); @@ -939,7 +1139,22 @@ GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req ) /* Falsche Anzahl Parameter? */ if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NOORIGIN_MSG, Client_ID( Client )); - if( Req->argc > 1 ) 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 ); + + if( Req->argc == 2 ) + { + /* es wurde ein Ziel-Client angegeben */ + 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, forwarden */ + 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( target, from, "PING %s :%s", Client_ID( from ), Req->argv[1] ); + } + } Log( LOG_DEBUG, "Connection %d: got PING, sending PONG ...", Client_Conn( Client )); return IRC_WriteStrClient( Client, "PONG %s :%s", Client_ID( Client_ThisServer( )), Client_ID( Client )); @@ -948,6 +1163,8 @@ GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req ) { + CLIENT *target, *from; + assert( Client != NULL ); assert( Req != NULL ); @@ -957,10 +1174,25 @@ GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req ) if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NOORIGIN_MSG, Client_ID( Client )); if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + /* forwarden? */ + if( Req->argc == 2 ) + { + 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, forwarden */ + 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( target, from, "PONG %s :%s", Client_ID( from ), Req->argv[1] ); + } + } + /* Der Connection-Timestamp wurde schon beim Lesen aus dem Socket * aktualisiert, daher muss das hier nicht mehr gemacht werden. */ - if( Client_Conn( Client ) > NONE ) Log( LOG_DEBUG, "Connection %d: received PONG. Lag: %d seconds.", Client_Conn( Client ), time( NULL ) - Conn_LastPing( Client_Conn( Client ))); + if( Client_Conn( Client ) > NONE ) Log( LOG_DEBUG, "Connection %d: received PONG. Lag: %ld seconds.", Client_Conn( Client ), time( NULL ) - Conn_LastPing( Client_Conn( Client ))); else Log( LOG_DEBUG, "Connection %d: received PONG.", Client_Conn( Client )); return CONNECTED; @@ -983,7 +1215,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 ); @@ -1000,18 +1233,41 @@ 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_Type( Client ) != CLIENT_SERVER ) && ( strchr( Client_Modes( cl ), 'a' ))) + { + /* Ziel-User ist AWAY: Meldung verschicken */ + if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( cl ), Client_Away( cl ))) return DISCONNECTED; + } + + /* Text senden */ 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] ); } @@ -1049,14 +1305,14 @@ GLOBAL BOOLEAN IRC_NOTICE( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) { CHAR *mode_ptr, the_modes[CLIENT_MODE_LEN], x[2]; + CLIENT *cl, *chan_cl, *prefix; BOOLEAN set, ok; CHANNEL *chan; - CLIENT *cl, *chan_cl; assert( Client != NULL ); assert( Req != NULL ); - cl = chan_cl = NULL; + cl = chan_cl = prefix = NULL; chan = NULL; /* Valider Client? */ @@ -1101,7 +1357,7 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) /* Werden die Modes "nur" erfragt? */ if(( cl ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_UMODEIS_MSG, Client_ID( Client ), Client_Modes( cl )); - if(( chan ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_UMODEISCHAN_MSG, Client_ID( Client ), Channel_Name( chan ), Channel_Modes( chan )); + if(( chan ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_CHANNELMODEIS_MSG, Client_ID( Client ), Channel_Name( chan ), Channel_Modes( chan )); mode_ptr = Req->argv[1]; @@ -1119,6 +1375,14 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) else set = TRUE; if(( *mode_ptr == '-' ) || ( *mode_ptr == '+' )) mode_ptr++; } + + /* Prefix fuer Antworten etc. ermitteln */ + if( Client_Type( Client ) == CLIENT_SERVER ) + { + prefix = Client_GetFromID( Req->prefix ); + if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + } + else prefix = Client; /* Reply-String mit Aenderungen vorbereiten */ if( set ) strcpy( the_modes, "+" ); @@ -1134,6 +1398,14 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) /* Befehl kommt von einem Server, daher * trauen wir ihm "unbesehen" ... */ x[0] = *mode_ptr; + + if(( cl ) && ( x[0] == 'a' )) + { + /* away */ + if( set ) Client_SetAway( cl, "Away" ); + else Client_SetAway( cl, NULL ); + } + } else { @@ -1162,22 +1434,79 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) else ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); break; default: - ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Client )); + ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr ); x[0] = '\0'; } } if( chan ) { + /* Ist der User ein Channel Operator? */ + if( ! strchr( Channel_UserModes( chan, Client ), 'o' )) + { + Log( LOG_DEBUG, "Can't change modes: \"%s\" is not operator on %s!", Client_ID( Client ), Channel_Name( chan )); + ok = IRC_WriteStrClient( Client, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Client ), Channel_Name( chan )); + break; + } + /* Channel-Modes oder Channel-User-Modes */ if( chan_cl ) { /* Channel-User-Modes */ - Log( LOG_DEBUG, "Channel-User-Mode '%c' not supported ...", *mode_ptr ); + switch( *mode_ptr ) + { + case 'o': + /* Channel Operator */ + x[0] = 'o'; + break; + case 'v': + /* Voice */ + x[0] = 'v'; + break; + default: + Log( LOG_DEBUG, "Unknown channel-user-mode \"%c%c\" from \"%s\" on \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Client_ID( chan_cl ), Channel_Name( chan )); + ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr ); + x[0] = '\0'; + } } else { /* Channel-Modes */ - Log( LOG_DEBUG, "Channel-Mode '%c' not supported ...", *mode_ptr ); + switch( *mode_ptr ) + { + case 'a': + /* Anonymous */ + x[0] = 'a'; + break; + case 'm': + /* Moderated */ + x[0] = 'm'; + break; + case 'n': + /* kein Schreiben in den Channel von aussen */ + x[0] = 'n'; + break; + case 'p': + /* Private */ + x[0] = 'p'; + break; + case 'q': + /* Quiet */ + x[0] = 'q'; + break; + case 's': + /* Secret */ + x[0] = 's'; + break; + case 't': + /* Topic Lock */ + x[0] = 't'; + break; + default: + Log( LOG_DEBUG, "Unknown channel-mode \"%c%c\" from \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Channel_Name( chan )); + ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr ); + x[0] = '\0'; + } } } } @@ -1244,13 +1573,13 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) if( Client_Type( Client ) == CLIENT_SERVER ) { /* Modes an andere Server forwarden */ - IRC_WriteStrServersPrefix( Client, Client, "MODE %s :%s", Client_ID( cl ), the_modes ); + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes ); } else { /* Bestaetigung an Client schicken & andere Server informieren */ - ok = IRC_WriteStrClient( Client, "MODE %s :%s", Client_ID( cl ), the_modes ); - IRC_WriteStrServers( Client, "MODE %s :%s", Client_ID( cl ), the_modes ); + ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Client_ID( cl ), the_modes ); + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes ); } Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( cl ), Client_Modes( cl )); } @@ -1263,15 +1592,15 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) if( Client_Type( Client ) == CLIENT_SERVER ) { /* Modes an andere Server und Channel-User forwarden */ - IRC_WriteStrServersPrefix( Client, Client, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); - IRC_WriteStrChannelPrefix( Client, chan, Client, FALSE, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); + IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); } else { - /* Bestaetigung an Client schicken & andere Server informieren */ - ok = IRC_WriteStrClient( Client, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); - IRC_WriteStrServersPrefix( Client, Client, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); - IRC_WriteStrChannelPrefix( Client, chan, Client, FALSE, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); + /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */ + ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); + IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl)); } Log( LOG_DEBUG, "User \"%s\" on %s: Mode change, now \"%s\".", Client_Mask( chan_cl), Channel_Name( chan ), Channel_UserModes( chan, chan_cl )); } @@ -1280,14 +1609,16 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) /* Channel-Mode */ if( Client_Type( Client ) == CLIENT_SERVER ) { - /* Modes an andere Server forwarden */ - IRC_WriteStrServersPrefix( Client, Client, "MODE %s :%s", Channel_Name( chan ), the_modes ); + /* Modes an andere Server und Channel-User forwarden */ + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes ); + IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes ); } else { - /* Bestaetigung an Client schicken & andere Server informieren */ - ok = IRC_WriteStrClient( Client, "MODE %s :%s", Channel_Name( chan ), the_modes ); - IRC_WriteStrServers( Client, "MODE %s :%s", Channel_Name( chan ), the_modes ); + /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */ + ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Channel_Name( chan ), the_modes ); + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes ); + IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes ); } Log( LOG_DEBUG, "Channel \"%s\": Mode change, now \"%s\".", Channel_Name( chan ), Channel_Modes( chan )); } @@ -1298,6 +1629,33 @@ GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) } /* IRC_MODE */ +GLOBAL BOOLEAN IRC_AWAY( CLIENT *Client, REQUEST *Req ) +{ + assert( Client != NULL ); + assert( Req != NULL ); + + if( Client_Type( Client ) != CLIENT_USER ) return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); + + /* Falsche Anzahl Parameter? */ + if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + if(( Req->argc == 1 ) && (Req->argv[0][0] )) + { + /* AWAY setzen */ + Client_SetAway( Client, Req->argv[0] ); + IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client )); + return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client )); + } + else + { + /* AWAY loeschen */ + Client_SetAway( Client, NULL ); + IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client )); + return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client )); + } +} /* IRC_AWAY */ + + GLOBAL BOOLEAN IRC_OPER( CLIENT *Client, REQUEST *Req ) { INT i; @@ -1381,46 +1739,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 ); - /* Noch alle User ausgeben, die in keinem Channel sind */ - rpl[0] = '\0'; + /* 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; + + /* 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 */ @@ -1494,17 +1902,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; @@ -1554,6 +1956,12 @@ GLOBAL BOOLEAN IRC_WHOIS( CLIENT *Client, REQUEST *Req ) if( ! IRC_WriteStrClient( from, RPL_WHOISIDLE_MSG, Client_ID( from ), Client_ID( c ), Conn_GetIdle( Client_Conn ( c )))) return DISCONNECTED; } + /* Away? */ + if( Client_HasMode( c, 'a' )) + { + if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( c ), Client_Away( c ))) return DISCONNECTED; + } + /* End of Whois */ return IRC_WriteStrClient( from, RPL_ENDOFWHOIS_MSG, Client_ID( from ), Client_ID( c )); } /* IRC_WHOIS */ @@ -1615,7 +2023,6 @@ GLOBAL BOOLEAN IRC_ERROR( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_LUSERS( CLIENT *Client, REQUEST *Req ) { CLIENT *target, *from; - INT cnt; assert( Client != NULL ); assert( Req != NULL ); @@ -1643,28 +2050,7 @@ GLOBAL BOOLEAN IRC_LUSERS( CLIENT *Client, REQUEST *Req ) else target = Client; if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - /* Users, Services und Serevr im Netz */ - if( ! IRC_WriteStrClient( target, RPL_LUSERCLIENT_MSG, Client_ID( target ), Client_UserCount( ), Client_ServiceCount( ), Client_ServerCount( ))) return DISCONNECTED; - - /* IRC-Operatoren im Netz */ - cnt = Client_OperCount( ); - if( cnt > 0 ) - { - if( ! IRC_WriteStrClient( target, RPL_LUSEROP_MSG, Client_ID( target ), cnt )) return DISCONNECTED; - } - - /* Unbekannt Verbindungen */ - cnt = Client_UnknownCount( ); - if( cnt > 0 ) - { - if( ! IRC_WriteStrClient( target, RPL_LUSERUNKNOWN_MSG, Client_ID( target ), cnt )) return DISCONNECTED; - } - - /* Channels im Netz */ - if( ! IRC_WriteStrClient( target, RPL_LUSERCHANNELS_MSG, Client_ID( target ), Channel_Count( ))) return DISCONNECTED; - - /* Channels im Netz */ - if( ! IRC_WriteStrClient( target, RPL_LUSERME_MSG, Client_ID( target ), Client_MyUserCount( ), Client_MyServiceCount( ), Client_MyServerCount( ))) return DISCONNECTED; + Send_LUSERS( target ); return CONNECTED; } /* IRC_LUSERS */ @@ -1721,7 +2107,7 @@ GLOBAL BOOLEAN IRC_LINKS( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_JOIN( CLIENT *Client, REQUEST *Req ) { - CHAR *channame, *flags, modes[8]; + CHAR *channame, *flags, *topic, modes[8]; BOOLEAN is_new_chan; CLIENT *target; CHANNEL *chan; @@ -1801,10 +2187,14 @@ 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!" ); + topic = Channel_Topic( chan ); + if( *topic ) IRC_WriteStrClient( Client, RPL_TOPIC_MSG, Client_ID( Client ), channame, topic ); + /* Mitglieder an Client Melden */ Send_NAMES( Client, chan ); + IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( chan )); } /* naechsten Namen ermitteln */ @@ -1850,6 +2240,121 @@ GLOBAL BOOLEAN IRC_PART( CLIENT *Client, REQUEST *Req ) } /* IRC_PART */ +GLOBAL BOOLEAN IRC_TOPIC( CLIENT *Client, REQUEST *Req ) +{ + CHANNEL *chan; + CLIENT *from; + CHAR *topic; + + assert( Client != NULL ); + assert( Req != NULL ); + + if(( Client_Type( Client ) != CLIENT_USER ) && ( Client_Type( Client ) != CLIENT_USER )) return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); + + /* Falsche Anzahl Parameter? */ + if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_GetFromID( Req->prefix ); + else from = Client; + if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + + /* Welcher Channel? */ + chan = Channel_Search( Req->argv[0] ); + if( ! chan ) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] ); + + /* Ist der User Mitglied in dem Channel? */ + if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] ); + + if( Req->argc == 1 ) + { + /* Topic erfragen */ + topic = Channel_Topic( chan ); + if( *topic ) return IRC_WriteStrClient( from, RPL_TOPIC_MSG, Client_ID( from ), Channel_Name( chan ), topic ); + else return IRC_WriteStrClient( from, RPL_NOTOPIC_MSG, Client_ID( from ), Channel_Name( chan )); + } + + if( strchr( Channel_Modes( chan ), 't' )) + { + /* Topic Lock. Ist der User ein Channel Operator? */ + if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan )); + } + + /* Topic setzen */ + Channel_SetTopic( chan, Req->argv[1] ); + Log( LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s", Client_Mask( from ), Channel_Name( chan ), Req->argv[1][0] ? Req->argv[1] : "" ); + + /* im Channel bekannt machen */ + IRC_WriteStrChannelPrefix( from, chan, from, TRUE, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); + return IRC_WriteStrClientPrefix( from, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); +} /* IRC_TOPIC */ + + +GLOBAL BOOLEAN IRC_VERSION( CLIENT *Client, REQUEST *Req ) +{ + CLIENT *target, *prefix; + + 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 ); + + /* Ziel suchen */ + 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] ); + + /* forwarden */ + IRC_WriteStrClientPrefix( target, prefix, "VERSION %s", Req->argv[0] ); + return CONNECTED; + } + + /* 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 ); @@ -1875,7 +2380,10 @@ LOCAL BOOLEAN Hello_User( CLIENT *Client ) Client_SetType( Client, CLIENT_USER ); - return Show_MOTD( Client ); + if( ! Send_LUSERS( Client )) return DISCONNECTED; + if( ! Show_MOTD( Client )) return DISCONNECTED; + + return CONNECTED; } /* Hello_User */ @@ -1913,16 +2421,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; @@ -1930,6 +2449,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 ); @@ -1937,17 +2459,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 */ @@ -1959,11 +2487,51 @@ 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 */ +LOCAL BOOLEAN Send_LUSERS( CLIENT *Client ) +{ + INT cnt; + + assert( Client != NULL ); + + /* Users, Services und Serevr im Netz */ + if( ! IRC_WriteStrClient( Client, RPL_LUSERCLIENT_MSG, Client_ID( Client ), Client_UserCount( ), Client_ServiceCount( ), Client_ServerCount( ))) return DISCONNECTED; + + /* IRC-Operatoren im Netz */ + cnt = Client_OperCount( ); + if( cnt > 0 ) + { + if( ! IRC_WriteStrClient( Client, RPL_LUSEROP_MSG, Client_ID( Client ), cnt )) return DISCONNECTED; + } + + /* Unbekannt Verbindungen */ + cnt = Client_UnknownCount( ); + if( cnt > 0 ) + { + if( ! IRC_WriteStrClient( Client, RPL_LUSERUNKNOWN_MSG, Client_ID( Client ), cnt )) return DISCONNECTED; + } + + /* Channels im Netz */ + if( ! IRC_WriteStrClient( Client, RPL_LUSERCHANNELS_MSG, Client_ID( Client ), Channel_Count( ))) return DISCONNECTED; + + /* Channels im Netz */ + if( ! IRC_WriteStrClient( Client, RPL_LUSERME_MSG, Client_ID( Client ), Client_MyUserCount( ), Client_MyServiceCount( ), Client_MyServerCount( ))) return DISCONNECTED; + + return CONNECTED; +} /* 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- */