X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fngircd%2Firc.c;h=cf25001a56c57dd3dfff40d77be30dd61e070beb;hb=213fa0994fab0a56a6d06395348bbe1c326b7af5;hp=f5b01d90db2969937f58da0bf8a148e251eb6fcd;hpb=00f86465ef0d751a14997559331638efce2747e4;p=ngircd-alex.git diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index f5b01d90..cf25001a 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -9,11 +9,83 @@ * 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.38 2002/01/18 15:31:32 alex Exp $ + * $Id: irc.c,v 1.57 2002/02/13 23:05:29 alex Exp $ * * irc.c: IRC-Befehle * * $Log: irc.c,v $ + * 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 + * angelegt wird, werden Channel-Operator, etc. pp. ... + * - NJOIN's von Servern werden nun korrekt an andere Server weitergeleitet. + * + * Revision 1.49 2002/02/06 16:51:22 alex + * - neue Funktion zur MODE-Behandlung, fuer Channel-Modes vorbereitet. + * + * Revision 1.48 2002/01/29 00:13:45 alex + * - WHOIS zeigt nun auch die Channels an, in denen der jeweilige User Mitglied ist. + * - zu jedem Server wird nun der "Top-Server" gespeichert, somit funktioniert + * LINKS wieder korrekt. + * + * Revision 1.47 2002/01/28 13:05:48 alex + * - nach einem JOIN wird die Liste der Mitglieder an den Client geschickt. + * - MODE fuer Channels wird nun komplett ignoriert (keine Fehlermeldung mehr). + * + * Revision 1.46 2002/01/28 01:45:43 alex + * - SERVER-Meldungen an neue Server sind nun in der richtigen Reihenfolge. + * + * Revision 1.45 2002/01/28 01:18:14 alex + * - connectierenden Servern werden Channels nun mit NJOIN bekannt gemacht. + * + * Revision 1.44 2002/01/28 00:55:08 alex + * - ein neu connectierender Server wird nun korrekt im Netz bekannt gemacht. + * + * Revision 1.43 2002/01/27 21:56:39 alex + * - IRC_WriteStrServersPrefixID() und IRC_WriteStrClientPrefixID() wieder entfernt. + * - einige kleinere Fixes bezueglich Channels ... + * + * Revision 1.42 2002/01/27 18:28:01 alex + * - bei NICK wurde das falsche Prefix an andere Server weitergegeben. + * + * Revision 1.41 2002/01/27 17:15:49 alex + * - anderungen an den Funktions-Prototypen von IRC_WriteStrChannel() und + * IRC_WriteStrChannelPrefix(), + * - neue: IRC_WriteStrClientPrefixID() und IRC_WriteStrServersPrefixID(). + * + * Revision 1.40 2002/01/26 18:43:11 alex + * - neue Funktionen IRC_WriteStrChannelPrefix() und IRC_WriteStrChannel(), + * die IRC_Write_xxx_Related() sind dafuer entfallen. + * - IRC_PRIVMSG() kann nun auch mit Channels als Ziel umgehen. + * + * Revision 1.39 2002/01/21 00:05:11 alex + * - neue Funktionen IRC_JOIN und IRC_PART begonnen, ebenso die Funktionen + * IRC_WriteStrRelatedPrefix und IRC_WriteStrRelatedChannelPrefix(). + * - diverse Aenderungen im Zusammenhang mit Channels. + * * Revision 1.38 2002/01/18 15:31:32 alex * - die User-Modes bei einem NICK von einem Server wurden falsch uebernommen. * @@ -160,6 +232,7 @@ #include #include "ngircd.h" +#include "channel.h" #include "client.h" #include "conf.h" #include "conn.h" @@ -181,6 +254,9 @@ LOCAL BOOLEAN Show_MOTD( CLIENT *Client ); LOCAL VOID Kill_Nick( CHAR *Nick ); +LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ); +LOCAL BOOLEAN Send_LUSERS( CLIENT *Client ); + GLOBAL VOID IRC_Init( VOID ) { @@ -231,10 +307,9 @@ GLOBAL BOOLEAN IRC_WriteStrClientPrefix( CLIENT *Client, CLIENT *Prefix, CHAR *F } /* IRC_WriteStrClientPrefix */ -GLOBAL BOOLEAN IRC_WriteStrRelated( CLIENT *Client, CHAR *Format, ... ) +GLOBAL BOOLEAN IRC_WriteStrChannel( CLIENT *Client, CHANNEL *Chan, BOOLEAN Remote, CHAR *Format, ... ) { CHAR buffer[1000]; - BOOLEAN ok = CONNECTED; va_list ap; assert( Client != NULL ); @@ -244,13 +319,65 @@ GLOBAL BOOLEAN IRC_WriteStrRelated( CLIENT *Client, CHAR *Format, ... ) vsnprintf( buffer, 1000, Format, ap ); va_end( ap ); - /* an den Client selber */ - ok = IRC_WriteStrClient( Client, buffer ); + return IRC_WriteStrChannelPrefix( Client, Chan, Client_ThisServer( ), Remote, buffer ); +} /* IRC_WriteStrChannel */ - /* FIXME */ - + +GLOBAL BOOLEAN IRC_WriteStrChannelPrefix( CLIENT *Client, CHANNEL *Chan, CLIENT *Prefix, BOOLEAN Remote, CHAR *Format, ... ) +{ + CHAR buffer[1000]; + BOOLEAN sock[MAX_CONNECTIONS], ok = CONNECTED, i; + CL2CHAN *cl2chan; + CLIENT *c; + INT s; + va_list ap; + + assert( Client != NULL ); + assert( Chan != NULL ); + assert( Prefix != NULL ); + assert( Format != NULL ); + + va_start( ap, Format ); + vsnprintf( buffer, 1000, Format, ap ); + va_end( ap ); + + for( i = 0; i < MAX_CONNECTIONS; i++ ) sock[i] = FALSE; + + /* An alle Clients, die in den selben Channels sind. + * Dabei aber nur einmal je Remote-Server */ + 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; + } + cl2chan = Channel_NextMember( Chan, cl2chan ); + } + + /* Senden ... */ + for( i = 0; i < MAX_CONNECTIONS; i++ ) + { + if( sock[i] ) + { + ok = Conn_WriteStr( i, ":%s %s", Client_ID( Prefix ), buffer ); + if( ! ok ) break; + } + } return ok; -} /* IRC_WriteStrRelated */ +} /* IRC_WriteStrChannelPrefix */ GLOBAL VOID IRC_WriteStrServers( CLIENT *ExceptOf, CHAR *Format, ... ) @@ -337,9 +464,11 @@ GLOBAL BOOLEAN IRC_PASS( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) { CHAR str[LINE_LEN], *ptr; + CLIENT *from, *c, *cl; + CL2CHAN *cl2chan; + INT max_hops, i; + CHANNEL *chan; BOOLEAN ok; - CLIENT *c; - INT i; assert( Client != NULL ); assert( Req != NULL ); @@ -401,24 +530,36 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) Client_SetType( Client, CLIENT_SERVER ); - /* Alle bisherigen Server dem neuen Server bekannt machen, - * die bisherigen Server ueber den neuen informierenn */ + /* maximalen Hop Count ermitteln */ + max_hops = 0; c = Client_First( ); while( c ) { - if(( Client_Type( c ) == CLIENT_SERVER ) && ( c != Client ) && ( c != Client_ThisServer( ))) + if( Client_Hops( c ) > max_hops ) max_hops = Client_Hops( c ); + c = Client_Next( c ); + } + + /* Alle bisherigen Server dem neuen Server bekannt machen, + * die bisherigen Server ueber den neuen informierenn */ + for( i = 0; i < ( max_hops + 1 ); i++ ) + { + c = Client_First( ); + while( c ) { - if( Client_Conn( c ) > NONE ) + if(( Client_Type( c ) == CLIENT_SERVER ) && ( c != Client ) && ( c != Client_ThisServer( )) && ( Client_Hops( c ) == i )) { - /* Dem gefundenen Server gleich den neuen - * 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; + if( Client_Conn( c ) > NONE ) + { + /* Dem gefundenen Server gleich den neuen + * 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; + } + + /* Den neuen Server ueber den alten informieren */ + if( ! IRC_WriteStrClientPrefix( Client, Client_Hops( c ) == 1 ? Client_ThisServer( ) : Client_Introducer( c ), "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ))) return DISCONNECTED; } - - /* Den neuen Server ueber den alten informieren */ - if( ! IRC_WriteStrClientPrefix( Client, Client_Introducer( c ), "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ))) return DISCONNECTED; + c = Client_Next( c ); } - c = Client_Next( c ); } /* alle User dem neuen Server bekannt machen */ @@ -432,6 +573,46 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) } c = Client_Next( c ); } + + /* Channels dem neuen Server bekannt machen */ + chan = Channel_First( ); + while( chan ) + { + sprintf( str, "NJOIN %s :", Channel_Name( chan )); + + /* alle Member suchen */ + cl2chan = Channel_FirstMember( chan ); + while( cl2chan ) + { + cl = Channel_GetClient( cl2chan ); + assert( cl != NULL ); + + /* Nick, ggf. mit Modes, 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 senden */ + if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; + sprintf( str, "NJOIN %s :", Channel_Name( chan )); + } + + cl2chan = Channel_NextMember( chan, cl2chan ); + } + + /* noch Daten da? */ + if( str[strlen( str ) - 1] != ':') + { + /* Ja; Also senden ... */ + if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; + } + + /* naechsten Channel suchen */ + chan = Channel_Next( chan ); + } return CONNECTED; } @@ -449,8 +630,17 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) ptr = strchr( Req->argv[3] + 2, '[' ); if( ! ptr ) ptr = Req->argv[3]; + from = Client_GetFromID( Req->prefix ); + if( ! from ) + { + /* Hm, Server, der diesen einfuehrt, ist nicht bekannt!? */ + Log( LOG_ALERT, "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)", Req->prefix, Client_Conn( Client )); + Conn_Close( Client_Conn( Client ), NULL, "Unknown ID in prefix of SERVER", TRUE ); + return DISCONNECTED; + } + /* Neue Client-Struktur anlegen */ - c = Client_NewRemoteServer( Client, Req->argv[0], atoi( Req->argv[1] ), atoi( Req->argv[2] ), ptr, TRUE ); + c = Client_NewRemoteServer( Client, Req->argv[0], from, atoi( Req->argv[1] ), atoi( Req->argv[2] ), ptr, TRUE ); if( ! c ) { /* Neue Client-Struktur konnte nicht angelegt werden */ @@ -460,10 +650,13 @@ GLOBAL BOOLEAN IRC_SERVER( CLIENT *Client, REQUEST *Req ) } /* Log-Meldung zusammenbauen und ausgeben */ - if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) sprintf( str, "connected to %s, ", Req->prefix ); + if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) sprintf( str, "connected to %s, ", Client_ID( from )); else strcpy( str, "" ); Log( LOG_NOTICE, "Server \"%s\" registered (via %s, %s%d hop%s).", Client_ID( c ), Client_ID( Client ), str, Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" ); - + + /* Andere Server informieren */ + IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c )); + return CONNECTED; } else return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); @@ -472,11 +665,63 @@ 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; + CHANNEL *chan; + CLIENT *c; + assert( Client != NULL ); assert( Req != NULL ); if( Client_Type( Client ) != CLIENT_SERVER ) return IRC_WriteStrClient( Client, ERR_NOTREGISTEREDSERVER_MSG, Client_ID( Client )); + /* Falsche Anzahl Parameter? */ + if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + channame = Req->argv[0]; + ptr = strtok( Req->argv[1], "," ); + while( ptr ) + { + is_op = is_voiced = FALSE; + + /* Prefixe abschneiden */ + while(( *ptr == '@' ) || ( *ptr == '+' )) + { + if( *ptr == '@' ) is_op = TRUE; + if( *ptr == '+' ) is_voiced = TRUE; + ptr++; + } + + c = Client_GetFromID( ptr ); + if( c ) + { + Channel_Join( c, channame ); + chan = Channel_Search( channame ); + assert( chan != NULL ); + + 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 ); + + /* naechsten Nick suchen */ + ptr = strtok( NULL, "," ); + } + + /* an andere Server weiterleiten */ + IRC_WriteStrServersPrefix( Client, Client_ThisServer( ), "NJOIN %s :%s", Req->argv[0], Req->argv[1] ); + return CONNECTED; } /* IRC_NJOIN */ @@ -514,10 +759,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 @@ -533,14 +778,14 @@ GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req ) { /* Nick-Aenderung: allen mitteilen! */ Log( LOG_INFO, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] ); - IRC_WriteStrRelated( Client, "NICK :%s", 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, Client, "NICK :%s", Req->argv[0] ); + IRC_WriteStrServersPrefix( Client, target, "NICK :%s", Req->argv[0] ); } /* Client-Nick registrieren */ @@ -722,6 +967,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 ); @@ -729,7 +976,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( Client_NextHop( 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 )); @@ -738,6 +1000,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 ); @@ -747,10 +1011,27 @@ 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( Client_NextHop( 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. */ - Log( LOG_DEBUG, "Connection %d: received PONG.", Client_Conn( Client )); + 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 ))); + else Log( LOG_DEBUG, "Connection %d: received PONG.", Client_Conn( Client )); + return CONNECTED; } /* IRC_PONG */ @@ -772,6 +1053,7 @@ GLOBAL BOOLEAN IRC_MOTD( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req ) { CLIENT *to, *from; + CHANNEL *chan; assert( Client != NULL ); assert( Req != NULL ); @@ -791,10 +1073,19 @@ GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req ) if( to ) { /* Okay, Ziel ist ein User */ - if( Client_Conn( from ) >= 0 ) Conn_UpdateIdle( Client_Conn( from )); + if( Client_Conn( from ) > NONE ) Conn_UpdateIdle( Client_Conn( from )); return IRC_WriteStrClientPrefix( to, from, "PRIVMSG %s :%s", Client_ID( to ), Req->argv[1] ); } - else return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] ); + + chan = Channel_Search( Req->argv[0] ); + if( chan ) + { + /* Okay, Ziel ist ein Channel */ + 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] ); + } + + return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] ); } /* IRC_PRIVMSG */ @@ -826,119 +1117,319 @@ GLOBAL BOOLEAN IRC_NOTICE( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req ) { - CHAR x[2], new_modes[CLIENT_MODE_LEN], *ptr; + CHAR *mode_ptr, the_modes[CLIENT_MODE_LEN], x[2]; + CLIENT *cl, *chan_cl, *prefix; BOOLEAN set, ok; - CLIENT *target; + CHANNEL *chan; assert( Client != NULL ); assert( Req != NULL ); + cl = chan_cl = prefix = NULL; + chan = NULL; + + /* Valider 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 > 2 ) || ( Req->argc < 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + /* Keine Parameter? */ + if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - /* "Ziel-Client" suchen */ - target = Client_Search( Req->argv[0] ); + /* Ziel suchen: Client bzw. Channel */ + if( Client_IsValidNick( Req->argv[0] )) cl = Client_Search( Req->argv[0] ); + if( Channel_IsValidName( Req->argv[0] )) chan = Channel_Search( Req->argv[0] ); - /* Wer ist der Anfragende? */ - if( Client_Type( Client ) == CLIENT_USER ) + /* Kein Ziel gefunden? */ + if(( ! cl ) && ( ! chan )) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] ); + + assert(( cl && chan ) != TRUE ); + + /* Falsche Anzahl Parameter? */ + if(( cl ) && ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if(( chan ) && ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + /* Client ermitteln, wenn bei Channel-Modes mit 3 Parametern */ + if(( chan ) && (Req->argc == 3 )) { - /* User: MODE ist nur fuer sich selber zulaessig */ - if( target != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client )); + chan_cl = Client_Search( Req->argv[2] ); + if( ! chan_cl ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] ); } - else + + /* Wenn Anfragender ein User ist: Zugriff erlaubt? */ + if( Client_Type( Client ) == CLIENT_USER ) { - /* Server: gibt es den Client ueberhaupt? */ - if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] ); + if( cl ) + { + /* MODE ist nur fuer sich selber zulaessig! */ + if( cl != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client )); + } + if( chan ) + { + /* Darf der User die Channel-Modes ermitteln? */ + } } - /* Werden die Modes erfragt? */ - if( Req->argc == 1 ) return IRC_WriteStrClient( Client, RPL_UMODEIS_MSG, Client_ID( Client ), Client_Modes( Client )); + /* 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_CHANNELMODEIS_MSG, Client_ID( Client ), Channel_Name( chan ), Channel_Modes( chan )); - ptr = Req->argv[1]; + mode_ptr = Req->argv[1]; /* Sollen Modes gesetzt oder geloescht werden? */ - if( *ptr == '+' ) set = TRUE; - else if( *ptr == '-' ) set = FALSE; - else return IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + if( cl ) + { + if( *mode_ptr == '+' ) set = TRUE; + else if( *mode_ptr == '-' ) set = FALSE; + else return IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + mode_ptr++; + } + else + { + if( *mode_ptr == '-' ) set = FALSE; + 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( new_modes, "+" ); - else strcpy( new_modes, "-" ); + if( set ) strcpy( the_modes, "+" ); + else strcpy( the_modes, "-" ); - ptr++; ok = TRUE; x[1] = '\0'; - while( *ptr ) + while( *mode_ptr ) { x[0] = '\0'; if( Client_Type( Client ) == CLIENT_SERVER ) { - x[0] = *ptr; - ok = TRUE; + /* Befehl kommt von einem Server, daher + * trauen wir ihm "unbesehen" ... */ + x[0] = *mode_ptr; } else { - switch( *ptr ) + /* Modes validieren */ + if( cl ) { - case 'i': - /* invisible */ - x[0] = 'i'; - break; - case 'r': - /* restricted (kann nur gesetzt werden) */ - if( set ) x[0] = 'r'; - else ok = IRC_WriteStrClient( target, ERR_RESTRICTED_MSG, Client_ID( target )); + /* User-Modes */ + switch( *mode_ptr ) + { + case 'i': + /* invisible */ + x[0] = 'i'; + break; + case 'r': + /* restricted (kann nur gesetzt werden) */ + if( set ) x[0] = 'r'; + else ok = IRC_WriteStrClient( Client, ERR_RESTRICTED_MSG, Client_ID( Client )); + break; + case 'o': + /* operator (kann nur geloescht werden) */ + if( ! set ) + { + Client_SetOperByMe( Client, FALSE ); + x[0] = 'o'; + } + else ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + break; + default: + Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Client )); + ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + 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; - case 'o': - /* operator (kann nur geloescht werden) */ - if( ! set ) + } + + /* Channel-Modes oder Channel-User-Modes */ + if( chan_cl ) + { + /* Channel-User-Modes */ + switch( *mode_ptr ) { - Client_SetOperByMe( target, FALSE ); - x[0] = 'o'; + 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_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + x[0] = '\0'; } - else ok = IRC_WriteStrClient( target, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( target )); - break; - default: - ok = IRC_WriteStrClient( target, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( target )); - x[0] = '\0'; + } + else + { + /* Channel-Modes */ + 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': + /* Quite */ + 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_UMODEUNKNOWNFLAG_MSG, Client_ID( Client )); + x[0] = '\0'; + } + } } } if( ! ok ) break; - - ptr++; + + mode_ptr++; if( ! x[0] ) continue; /* Okay, gueltigen Mode gefunden */ - if( set ) + if( cl ) { - /* Mode setzen. Wenn der Client ihn noch nicht hatte: merken */ - if( Client_ModeAdd( target, x[0] )) strcat( new_modes, x ); + /* Es geht um User-Modes */ + if( set ) + { + /* Mode setzen. Wenn der Client ihn noch nicht hatte: merken */ + if( Client_ModeAdd( cl, x[0] )) strcat( the_modes, x ); + } + else + { + /* Modes geloescht. Wenn der Client ihn hatte: merken */ + if( Client_ModeDel( cl, x[0] )) strcat( the_modes, x ); + } } - else + if( chan ) { - /* Modes geloescht. Wenn der Client ihn hatte: merken */ - if( Client_ModeDel( target, x[0] )) strcat( new_modes, x ); + /* Es geht um Channel-Modes oder Channel-User-Modes */ + if( chan_cl ) + { + /* Channel-User-Modes */ + if( set ) + { + /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */ + if( Channel_UserModeAdd( chan, chan_cl, x[0] )) strcat( the_modes, x ); + } + else + { + /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */ + if( Channel_UserModeDel( chan, chan_cl, x[0] )) strcat( the_modes, x ); + } + } + else + { + /* Channel-Mode */ + if( set ) + { + /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */ + if( Channel_ModeAdd( chan, x[0] )) strcat( the_modes, x ); + } + else + { + /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */ + if( Channel_ModeDel( chan, x[0] )) strcat( the_modes, x ); + } + } } } - - /* Geanderte Modes? */ - if( new_modes[1] ) + + /* Wurden Modes geaendert? */ + if( the_modes[1] ) { - if( Client_Type( Client ) == CLIENT_SERVER ) + if( cl ) { - /* Modes an andere Server forwarden */ - IRC_WriteStrServersPrefix( Client, Client, "MODE %s :%s", Client_ID( target ), new_modes ); + /* Client-Mode */ + if( Client_Type( Client ) == CLIENT_SERVER ) + { + /* Modes an andere Server forwarden */ + IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes ); + } + else + { + /* Bestaetigung an Client schicken & andere Server informieren */ + 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 )); } - else + if( chan ) { - /* Bestaetigung an Client schicken & andere Server informieren */ - ok = IRC_WriteStrRelated( Client, "MODE %s :%s", Client_ID( target ), new_modes ); - IRC_WriteStrServers( Client, "MODE %s :%s", Client_ID( target ), new_modes ); + /* Channel-Modes oder Channel-User-Mode */ + if( chan_cl ) + { + /* Channel-User-Mode */ + if( Client_Type( Client ) == CLIENT_SERVER ) + { + /* Modes an andere Server und Channel-User forwarden */ + 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 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 )); + } + else + { + /* Channel-Mode */ + if( Client_Type( Client ) == CLIENT_SERVER ) + { + /* 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 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 )); + } } - Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( target ), Client_Modes( target )); } + return ok; } /* IRC_MODE */ @@ -977,7 +1468,7 @@ GLOBAL BOOLEAN IRC_OPER( CLIENT *Client, REQUEST *Req ) { /* noch kein o-Mode gesetzt */ Client_ModeAdd( Client, 'o' ); - if( ! IRC_WriteStrRelated( Client, "MODE %s :+o", Client_ID( Client ))) return DISCONNECTED; + if( ! IRC_WriteStrClient( Client, "MODE %s :+o", Client_ID( Client ))) return DISCONNECTED; IRC_WriteStrServersPrefix( NULL, Client, "MODE %s :+o", Client_ID( Client )); } @@ -1110,7 +1601,9 @@ GLOBAL BOOLEAN IRC_ISON( CLIENT *Client, REQUEST *Req ) GLOBAL BOOLEAN IRC_WHOIS( CLIENT *Client, REQUEST *Req ) { CLIENT *from, *target, *c; - CHAR *ptr = NULL; + CHAR str[LINE_LEN + 1], *ptr = NULL; + CL2CHAN *cl2chan; + CHANNEL *chan; assert( Client != NULL ); assert( Req != NULL ); @@ -1155,6 +1648,36 @@ GLOBAL BOOLEAN IRC_WHOIS( CLIENT *Client, REQUEST *Req ) /* 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 */ + sprintf( str, RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c )); + cl2chan = Channel_FirstChannelOf( c ); + while( cl2chan ) + { + chan = Channel_GetChannel( cl2chan ); + assert( chan != NULL ); + + /* Channel-Name anhaengen */ + if( str[strlen( str ) - 1] != ':' ) strcat( str, " " ); + if( strchr( Channel_UserModes( chan, c ), 'v' )) strcat( str, "+" ); + if( strchr( Channel_UserModes( chan, c ), 'o' )) strcat( str, "@" ); + strcat( str, Channel_Name( chan )); + + if( strlen( str ) > ( LINE_LEN - CHANNEL_NAME_LEN - 4 )) + { + /* Zeile wird zu lang: senden! */ + if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; + sprintf( str, RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c )); + } + + /* naechstes Mitglied suchen */ + cl2chan = Channel_NextChannelOf( c, cl2chan ); + } + if( str[strlen( str ) - 1] != ':') + { + /* Es sind noch Daten da, die gesendet werden muessen */ + if( ! IRC_WriteStrClient( Client, str )) return DISCONNECTED; + } + /* IRC-Operator? */ if( Client_HasMode( c, 'o' )) { @@ -1228,7 +1751,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 ); @@ -1256,28 +1778,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 */ @@ -1323,7 +1824,7 @@ GLOBAL BOOLEAN IRC_LINKS( CLIENT *Client, REQUEST *Req ) { if( Client_Type( c ) == CLIENT_SERVER ) { - if( ! IRC_WriteStrClient( target, RPL_LINKS_MSG, Client_ID( target ), Client_ID( c ), Client_ID( Client_Introducer( c )), Client_Hops( c ), Client_Info( c ))) return DISCONNECTED; + if( ! IRC_WriteStrClient( target, RPL_LINKS_MSG, Client_ID( target ), Client_ID( c ), Client_ID( Client_TopServer( c ) ? Client_TopServer( c ) : Client_ThisServer( )), Client_Hops( c ), Client_Info( c ))) return DISCONNECTED; } c = Client_Next( c ); } @@ -1332,6 +1833,137 @@ GLOBAL BOOLEAN IRC_LINKS( CLIENT *Client, REQUEST *Req ) } /* IRC_LINKS */ +GLOBAL BOOLEAN IRC_JOIN( CLIENT *Client, REQUEST *Req ) +{ + CHAR *channame, *flags, modes[8]; + BOOLEAN is_new_chan; + CLIENT *target; + CHANNEL *chan; + + assert( Client != NULL ); + assert( Req != NULL ); + + 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 > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + /* Wer ist der Absender? */ + if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_GetFromID( Req->prefix ); + else target = Client; + if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + + /* Channel-Namen durchgehen */ + channame = strtok( Req->argv[0], "," ); + while( channame ) + { + /* wird der Channel neu angelegt? */ + flags = NULL; + + if( Channel_Search( channame )) is_new_chan = FALSE; + else is_new_chan = TRUE; + + /* Hat ein Server Channel-User-Modes uebergeben? */ + if( Client_Type( Client ) == CLIENT_SERVER ) + { + /* Channel-Flags extrahieren */ + flags = strchr( channame, 0x7 ); + if( flags ) *flags++ = '\0'; + } + + /* neuer Channel udn lokaler Client? */ + if( is_new_chan && ( Client_Type( Client ) == CLIENT_USER )) + { + /* Dann soll der Client Channel-Operator werden! */ + flags = "o"; + } + + /* Channel joinen (und ggf. anlegen) */ + if( ! Channel_Join( target, channame )) + { + /* naechsten Namen ermitteln */ + channame = strtok( NULL, "," ); + continue; + } + chan = Channel_Search( channame ); + assert( chan != NULL ); + + /* Modes setzen (wenn vorhanden) */ + while( flags && *flags ) + { + Channel_UserModeAdd( chan, target, *flags ); + flags++; + } + + /* Muessen Modes an andere Server gemeldet werden? */ + strcpy( &modes[1], Channel_UserModes( chan, target )); + if( modes[1] ) modes[0] = 0x7; + else modes[0] = '\0'; + + /* An andere Server weiterleiten */ + IRC_WriteStrServersPrefix( Client, target, "JOIN :%s%s", channame, modes ); + + /* im Channel bekannt machen */ + IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "JOIN :%s", channame ); + if( modes[1] ) + { + /* Modes im Channel bekannt machen */ + IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "MODE %s %s :%s", channame, modes, Client_ID( target )); + } + + if( Client_Type( Client ) == CLIENT_USER ) + { + /* 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 ); + } + + /* naechsten Namen ermitteln */ + channame = strtok( NULL, "," ); + } + return CONNECTED; +} /* IRC_JOIN */ + + +GLOBAL BOOLEAN IRC_PART( CLIENT *Client, REQUEST *Req ) +{ + CLIENT *target; + CHAR *chan; + + assert( Client != NULL ); + assert( Req != NULL ); + + 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 > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + /* Wer ist der Absender? */ + if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_GetFromID( Req->prefix ); + else target = Client; + if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + + /* Channel-Namen durchgehen */ + chan = strtok( Req->argv[0], "," ); + while( chan ) + { + if( ! Channel_Part( target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID( target ))) + { + /* naechsten Namen ermitteln */ + chan = strtok( NULL, "," ); + continue; + } + + /* naechsten Namen ermitteln */ + chan = strtok( NULL, "," ); + } + return CONNECTED; +} /* IRC_PART */ + + LOCAL BOOLEAN Hello_User( CLIENT *Client ) { assert( Client != NULL ); @@ -1357,7 +1989,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 */ @@ -1403,4 +2038,82 @@ LOCAL VOID Kill_Nick( CHAR *Nick ) } /* Kill_Nick */ +LOCAL BOOLEAN Send_NAMES( CLIENT *Client, CHANNEL *Chan ) +{ + CHAR str[LINE_LEN + 1]; + CL2CHAN *cl2chan; + CLIENT *cl; + + assert( Client != NULL ); + assert( Chan != NULL ); + + /* Alle Mitglieder suchen */ + sprintf( str, RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan )); + cl2chan = Channel_FirstMember( Chan ); + while( cl2chan ) + { + 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( 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 */ + cl2chan = Channel_NextMember( Chan, cl2chan ); + } + if( str[strlen( str ) - 1] != ':') + { + /* Es sind noch Daten da, die gesendet werden muessen */ + 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 */ + + /* -eof- */