]> arthur.barton.de Git - ngircd-alex.git/commitdiff
Announce IRC services in the network.
authorAlexander Barton <alex@barton.de>
Sun, 17 Aug 2008 15:29:41 +0000 (17:29 +0200)
committerAlexander Barton <alex@barton.de>
Tue, 23 Sep 2008 09:53:16 +0000 (11:53 +0200)
This patch
 - introduces a new server flag "S" to indicate that the server can handle
   the SERVICE command (on server links),
 - implements the IRC command "SERVICE" for server-server links,
 - uses the "SERVICE" command to announce IRC services when a new
   server connects to it,
 - and fixes the Send_Message() function to let it send messages to
   services using a "target mask".

If the remote server doesn't indicate that it can handle the "SERVICE"
command (it has not set the "S" flag), services are announced as regular
users as before.

src/ngircd/defines.h
src/ngircd/irc-login.c
src/ngircd/irc.c
src/ngircd/numeric.c

index cccf48b43e5f719d3a19146f995956e36fd6803e..7abe641994ea49953acb3d1d690f1fa15d3a96f4 100644 (file)
@@ -82,7 +82,7 @@
                                           protocol, see doc/Protocol.txt */
 
 #ifdef IRCPLUS
                                           protocol, see doc/Protocol.txt */
 
 #ifdef IRCPLUS
-# define IRCPLUSFLAGS "CHL           /* Standard IRC+ flags */
+# define IRCPLUSFLAGS "CHLS"           /* Standard IRC+ flags */
 #endif
 
 #define STARTUP_DELAY 1                        /* Delay outgoing connections n seconds
 #endif
 
 #define STARTUP_DELAY 1                        /* Delay outgoing connections n seconds
index c189228a124617679a361cb516ea62608170f7b5..943612edf03f06ef7204890e27fa0fdbe1544670 100644 (file)
@@ -464,17 +464,25 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 
 
 /**
 
 
 /**
- * Service registration.
- * ngIRCd does not support services at the moment, so this function is a
- * dummy that returns ERR_ERRONEUSNICKNAME on each call.
+ * Handler for the IRC command "SERVICE".
+ * This function implements IRC Services registration using the SERVICE command
+ * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
+ * At the moment ngIRCd doesn't support directly linked services, so this
+ * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
+ * received from a peer server.
  */
 GLOBAL bool
 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 {
  */
 GLOBAL bool
 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 {
+       CLIENT *c, *intr_c;
+       char *nick, *user, *host, *info, *modes, *ptr;
+       int token, hops;
+
        assert(Client != NULL);
        assert(Req != NULL);
 
        assert(Client != NULL);
        assert(Req != NULL);
 
-       if (Client_Type(Client) != CLIENT_GOTPASS)
+       if (Client_Type(Client) != CLIENT_GOTPASS &&
+           Client_Type(Client) != CLIENT_SERVER)
                return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
                                          Client_ID(Client));
 
                return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
                                          Client_ID(Client));
 
@@ -482,8 +490,74 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
                return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
                                          Client_ID(Client), Req->command);
 
                return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
                                          Client_ID(Client), Req->command);
 
-       return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+       if (Client_Type(Client) != CLIENT_SERVER)
+               return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
                                  Client_ID(Client), Req->argv[0]);
                                  Client_ID(Client), Req->argv[0]);
+
+       /* Bad number of parameters? */
+       if (Req->argc != 6)
+               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+                                         Client_ID(Client), Req->command);
+
+       nick = Req->argv[0];
+       user = NULL; host = NULL;
+       token = atoi(Req->argv[1]);
+       hops = atoi(Req->argv[4]);
+       info = Req->argv[5];
+
+       /* Validate service name ("nick name") */
+       c = Client_Search(nick);
+       if(c) {
+               /* Nick name collission: disconnect (KILL) both clients! */
+               Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
+                   Client_ID(Client), nick);
+               Kill_Nick(nick, "Nick collision");
+               return CONNECTED;
+       }
+
+       /* Get the server to which the service is connected */
+       intr_c = Client_GetFromToken(Client, token);
+       if (! intr_c) {
+               Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
+                   Client_ID(Client), nick);
+               Kill_Nick(nick, "Unknown server");
+               return CONNECTED;
+       }
+
+       /* Get user and host name */
+       ptr = strchr(nick, '@');
+       if (ptr) {
+               *ptr = '\0';
+               host = ++ptr;
+       }
+       if (!host)
+               host = Client_Hostname(intr_c);
+       ptr = strchr(nick, '!');
+       if (ptr) {
+               *ptr = '\0';
+               user = ++ptr;
+       }
+       if (!user)
+               user = nick;
+
+       /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
+        * for future usage"; but we use it to transfer the modes and check
+        * that the first character is a '+' sign and ignore it otherwise. */
+       modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
+
+       c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
+                                token, modes, info, true);
+       if (! c) {
+               /* Couldn't create client structure, so KILL the service to
+                * keep network status consistent ... */
+               Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
+                   Client_Conn(Client));
+               Kill_Nick(nick, "Server error");
+               return CONNECTED;
+       }
+
+       Introduce_Client(Client, c, CLIENT_SERVICE);
+       return CONNECTED;
 } /* IRC_SERVICE */
 
 
 } /* IRC_SERVICE */
 
 
@@ -782,8 +856,17 @@ cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
                        Conn_WriteStr(conn, ":%s MODE %s +%s",
                                      Client_ID(c), Client_ID(c), modes);
        } else {
                        Conn_WriteStr(conn, ":%s MODE %s +%s",
                                      Client_ID(c), Client_ID(c), modes);
        } else {
-               /* RFC 2813 mode: one combined NICK command */
-               IRC_WriteStrClientPrefix(To, Prefix,
+               /* RFC 2813 mode: one combined NICK or SERVICE command */
+               if (Client_Type(c) == CLIENT_SERVICE
+                   && strchr(Client_Flags(To), 'S'))
+                       IRC_WriteStrClientPrefix(To, Prefix,
+                                        "SERVICE %s %d * +%s %d :%s",
+                                        Client_Mask(c),
+                                        Client_MyToken(Client_Introducer(c)),
+                                        Client_Modes(c), Client_Hops(c) + 1,
+                                        Client_Info(c));
+               else
+                       IRC_WriteStrClientPrefix(To, Prefix,
                                         "NICK %s %d %s %s %d +%s :%s",
                                         Client_ID(c), Client_Hops(c) + 1,
                                         user, host,
                                         "NICK %s %d %s %s %d +%s :%s",
                                         Client_ID(c), Client_Hops(c) + 1,
                                         user, host,
index 0bfb3eed6a32e366a8d16d00a5a78348dc3ae647..47f86528004d24a009504076108ded7fc7b2f8c6 100644 (file)
@@ -400,7 +400,8 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
                        }
 
                        for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
                        }
 
                        for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
-                               if (Client_Type(cl) != CLIENT_USER)
+                               if (Client_Type(cl) != CLIENT_USER &&
+                                   Client_Type(cl) != CLIENT_SERVICE)
                                        continue;
                                if (nick != NULL && host != NULL) {
                                        if (strcmp(nick, Client_ID(cl)) == 0 &&
                                        continue;
                                if (nick != NULL && host != NULL) {
                                        if (strcmp(nick, Client_ID(cl)) == 0 &&
index 927989db9923d99567703938a9800ec60b8803ab..ab6476654bc08c25a84f2e1789324bd0b633b03a 100644 (file)
@@ -100,8 +100,17 @@ Announce_User(CLIENT * Client, CLIENT * User)
                }
                return CONNECTED;
        } else {
                }
                return CONNECTED;
        } else {
-               /* RFC 2813 mode: one combined NICK command */
-               return IRC_WriteStrClient(Client, "NICK %s %d %s %s %d +%s :%s",
+               /* RFC 2813 mode: one combined NICK or SERVICE command */
+               if (Client_Type(User) == CLIENT_SERVICE
+                   && strchr(Client_Flags(Client), 'S'))
+                       return IRC_WriteStrClient(Client,
+                               "SERVICE %s %d * +%s %d :%s", Client_Mask(User),
+                               Client_MyToken(Client_Introducer(User)),
+                               Client_Modes(User), Client_Hops(User) + 1,
+                               Client_Info(User));
+               else
+                       return IRC_WriteStrClient(Client,
+                               "NICK %s %d %s %s %d +%s :%s",
                                Client_ID(User), Client_Hops(User) + 1,
                                Client_User(User), Client_Hostname(User),
                                Client_MyToken(Client_Introducer(User)),
                                Client_ID(User), Client_Hops(User) + 1,
                                Client_User(User), Client_Hostname(User),
                                Client_MyToken(Client_Introducer(User)),