Commit Diff


commit - 6356418ae5fd66f94abda78f1ae67bbc7a59b0e4
commit + 178f9cbdac3bbeb58600268791916f3bfbcbd958
blob - cccf48b43e5f719d3a19146f995956e36fd6803e
blob + 7abe641994ea49953acb3d1d690f1fa15d3a96f4
--- src/ngircd/defines.h
+++ src/ngircd/defines.h
@@ -82,7 +82,7 @@
 					   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
blob - c189228a124617679a361cb516ea62608170f7b5
blob + 943612edf03f06ef7204890e27fa0fdbe1544670
--- src/ngircd/irc-login.c
+++ src/ngircd/irc-login.c
@@ -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)
 {
+	CLIENT *c, *intr_c;
+	char *nick, *user, *host, *info, *modes, *ptr;
+	int token, hops;
+
 	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));
 
@@ -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_ERRONEUSNICKNAME_MSG,
+	if (Client_Type(Client) != CLIENT_SERVER)
+		return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
 				  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 */
 
 
@@ -782,8 +856,17 @@ cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *d
 			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,
blob - 0bfb3eed6a32e366a8d16d00a5a78348dc3ae647
blob + 47f86528004d24a009504076108ded7fc7b2f8c6
--- src/ngircd/irc.c
+++ src/ngircd/irc.c
@@ -400,7 +400,8 @@ Send_Message(CLIENT * Client, REQUEST * Req, int Force
 			}
 
 			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 &&
blob - 927989db9923d99567703938a9800ec60b8803ab
blob + ab6476654bc08c25a84f2e1789324bd0b633b03a
--- src/ngircd/numeric.c
+++ src/ngircd/numeric.c
@@ -100,8 +100,17 @@ Announce_User(CLIENT * Client, CLIENT * User)
 		}
 		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)),