]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc-server.c
ngIRCd Release 27
[ngircd-alex.git] / src / ngircd / irc-server.c
index 5ac8810f2b4ce12707bbc20d3d356545164e40cc..6aa37574baf415eab29ae30d940200a888b9ebcc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2024 Alexander Barton (alex@barton.de) and Contributors.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include "parse.h"
 #include "numeric.h"
 #include "ngircd.h"
+#include "irc.h"
+#include "irc-channel.h"
 #include "irc-info.h"
 #include "irc-write.h"
 #include "op.h"
 
+#include "irc-server.h"
+
 /**
  * Handler for the IRC "SERVER" command.
  *
@@ -84,6 +88,19 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
                        return DISCONNECTED;
                }
 
+#ifdef SSL_SUPPORT
+               /* Does this server require an SSL connection? */
+               if (Conf_Server[i].SSLConnect &&
+                   !(Conn_Options(Client_Conn(Client)) & CONN_SSL)) {
+                       Log(LOG_ERR,
+                           "Connection %d: Server \"%s\" requires a secure connection!",
+                           Client_Conn(Client), Req->argv[0]);
+                       Conn_Close(Client_Conn(Client), NULL,
+                                  "Secure connection required", true);
+                       return DISCONNECTED;
+               }
+#endif
+
                /* Check server password */
                if (strcmp(Conn_Password(Client_Conn(Client)),
                    Conf_Server[i].pwd_in) != 0) {
@@ -183,6 +200,15 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
                if (!Client_CheckID(Client, Req->argv[0]))
                        return DISCONNECTED;
 
+               if (!Req->prefix) {
+                       /* We definitely need a prefix here! */
+                       Log(LOG_ALERT, "Got SERVER command without prefix! (on connection %d)",
+                           Client_Conn(Client));
+                       Conn_Close(Client_Conn(Client), NULL,
+                                  "SERVER command without prefix", true);
+                       return DISCONNECTED;
+               }
+
                from = Client_Search( Req->prefix );
                if (! from) {
                        /* Uh, Server, that introduced the new server is unknown?! */
@@ -243,66 +269,102 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
        CHANNEL *chan;
        CLIENT *c;
 
-       assert( Client != NULL );
-       assert( Req != NULL );
+       assert(Client != NULL);
+       assert(Req != NULL);
 
-       strlcpy( nick_in, Req->argv[1], sizeof( nick_in ));
-       strcpy( nick_out, "" );
+       strlcpy(nick_in, Req->argv[1], sizeof(nick_in));
+       strcpy(nick_out, "");
 
        channame = Req->argv[0];
-       ptr = strtok( nick_in, "," );
-       while( ptr )
-       {
+
+       ptr = strtok(nick_in, ",");
+       while (ptr) {
                is_owner = is_chanadmin = is_op = is_halfop = is_voiced = false;
 
                /* cut off prefixes */
-               while(( *ptr == '~') || ( *ptr == '&' ) || ( *ptr == '@' ) ||
-                       ( *ptr == '%') || ( *ptr == '+' ))
-               {
-                       if( *ptr == '~' ) is_owner = true;
-                       if( *ptr == '&' ) is_chanadmin = true;
-                       if( *ptr == '@' ) is_op = true;
-                       if( *ptr == 'h' ) is_halfop = true;
-                       if( *ptr == '+' ) is_voiced = true;
+               while ((*ptr == '~') || (*ptr == '&') || (*ptr == '@') ||
+                      (*ptr == '%') || (*ptr == '+')) {
+                       if (*ptr == '~')
+                               is_owner = true;
+                       if (*ptr == '&')
+                               is_chanadmin = true;
+                       if (*ptr == '@')
+                               is_op = true;
+                       if (*ptr == '%')
+                               is_halfop = true;
+                       if (*ptr == '+')
+                               is_voiced = true;
                        ptr++;
                }
 
-               c = Client_Search( ptr );
-               if( c )
-               {
-                       Channel_Join( c, channame );
-                       chan = Channel_Search( channame );
-                       assert( chan != NULL );
-
-                       if( is_owner ) Channel_UserModeAdd( chan, c, 'q' );
-                       if( is_chanadmin ) Channel_UserModeAdd( chan, c, 'a' );
-                       if( is_op ) Channel_UserModeAdd( chan, c, 'o' );
-                       if( is_halfop ) Channel_UserModeAdd( chan, c, 'h' );
-                       if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' );
-
-                       /* announce to channel... */
-                       IRC_WriteStrChannelPrefix( Client, chan, c, false, "JOIN :%s", channame );
-
-                       /* set Channel-User-Modes */
-                       strlcpy( modes, Channel_UserModes( chan, c ), sizeof( modes ));
-                       if( modes[0] )
-                       {
-                               /* send modes to channel */
-                               IRC_WriteStrChannelPrefix( Client, chan, Client, false, "MODE %s +%s %s", channame, modes, Client_ID( c ));
-                       }
+               c = Client_Search(ptr);
+               if (!c) {
+                       /* Client not found? */
+                       Log(LOG_ERR,
+                           "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!",
+                           ptr, channame);
+                       goto skip_njoin;
+               }
 
-                       if( nick_out[0] != '\0' ) strlcat( nick_out, ",", sizeof( nick_out ));
-                       if( is_owner ) strlcat( nick_out, "~", sizeof( nick_out ));
-                       if( is_chanadmin ) strlcat( nick_out, "&", sizeof( nick_out ));
-                       if( is_op ) strlcat( nick_out, "@", sizeof( nick_out ));
-                       if( is_halfop ) strlcat( nick_out, "%", sizeof( nick_out ));
-                       if( is_voiced ) strlcat( nick_out, "+", sizeof( nick_out ));
-                       strlcat( nick_out, ptr, sizeof( nick_out ));
+               if (!Channel_Join(c, channame)) {
+                       /* Failed to join channel. Ooops!? */
+                       Log(LOG_ALERT,
+                           "Failed to join client \"%s\" to channel \"%s\" (NJOIN): killing it!",
+                           ptr, channame);
+                       IRC_KillClient(NULL, NULL, ptr, "Internal NJOIN error!");
+                       LogDebug("... done.");
+                       goto skip_njoin;
                }
-               else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame );
 
-               /* search for next Nick */
-               ptr = strtok( NULL, "," );
+               chan = Channel_Search(channame);
+               assert(chan != NULL);
+
+               if (is_owner)
+                       Channel_UserModeAdd(chan, c, 'q');
+               if (is_chanadmin)
+                       Channel_UserModeAdd(chan, c, 'a');
+               if (is_op)
+                       Channel_UserModeAdd(chan, c, 'o');
+               if (is_halfop)
+                       Channel_UserModeAdd(chan, c, 'h');
+               if (is_voiced)
+                       Channel_UserModeAdd(chan, c, 'v');
+
+               /* Announce client to the channel */
+               IRC_WriteStrChannelPrefix(Client, chan, c, false,
+                                         "JOIN :%s", channame);
+
+               /* If the client is connected to this server, it was remotely
+                * joined to the channel by another server/service: So send
+                * TOPIC and NAMES messages like on a regular JOIN command! */
+               if(Client_Conn(c) != NONE)
+                       IRC_Send_Channel_Info(c, chan);
+
+               /* Announce "channel user modes" to the channel, if any */
+               strlcpy(modes, Channel_UserModes(chan, c), sizeof(modes));
+               if (modes[0])
+                       IRC_WriteStrChannelPrefix(Client, chan, Client, false,
+                                                 "MODE %s +%s %s", channame,
+                                                 modes, Client_ID(c));
+
+               /* Build nick list for forwarding command */
+               if (nick_out[0] != '\0')
+                       strlcat(nick_out, ",", sizeof(nick_out));
+               if (is_owner)
+                       strlcat(nick_out, "~", sizeof(nick_out));
+               if (is_chanadmin)
+                       strlcat(nick_out, "&", sizeof(nick_out));
+               if (is_op)
+                       strlcat(nick_out, "@", sizeof(nick_out));
+               if (is_halfop)
+                       strlcat(nick_out, "%", sizeof(nick_out));
+               if (is_voiced)
+                       strlcat(nick_out, "+", sizeof(nick_out));
+               strlcat(nick_out, ptr, sizeof(nick_out));
+
+             skip_njoin:
+               /* Get next nick, if any ... */
+               ptr = strtok(NULL, ",");
        }
 
        /* forward to other servers */
@@ -325,7 +387,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 {
        char msg[COMMAND_LEN], logmsg[COMMAND_LEN];
        CLIENT *from, *target;
-       CONN_ID con;
+       CONN_ID con, client_con;
        int loglevel;
 
        assert(Client != NULL);
@@ -365,6 +427,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
                return CONNECTED;
        }
 
+       client_con = Client_Conn(Client);
        con = Client_Conn(target);
 
        if (Req->argv[1][0])
@@ -386,7 +449,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
                                Req->argv[0], Client_ID(from),
                                Req->argv[1][0] ? Req->argv[1] : "-");
                Conn_Close(con, NULL, msg, true);
-               if (con == Client_Conn(Client))
+               if (con == client_con)
                        return DISCONNECTED;
        } else {
                /* This server is not directly connected, so the SQUIT must