From 2cc21caf32323ebd778c16c8a7b69cd12d6ff01f Mon Sep 17 00:00:00 2001
From: Alexander Barton <alex@barton.de>
Date: Fri, 14 Nov 2008 00:42:58 +0100
Subject: [PATCH] Implement local channels (prefix "&")

This patch implements server-local channels, prefix "&", that are only
visible to users of the same local server and not in the network.

Patch written by Scott Perry (2008-06-04), see:
 - http://arthur.barton.de/cgi-bin/bugzilla/show_bug.cgi?id=87
 - http://arthur.barton.de/cgi-bin/bugzilla/attachment.cgi?id=24&action=view
---
 src/ngircd/channel.c     |   16 +++++++++++-----
 src/ngircd/channel.h     |    5 +++++
 src/ngircd/irc-channel.c |   40 +++++++++++++++++++++++++++-------------
 src/ngircd/irc-info.c    |    9 ++++++++-
 src/ngircd/irc-mode.c    |   24 ++++++++++++++++++------
 src/ngircd/messages.h    |    6 ++----
 src/ngircd/numeric.c     |    4 ++++
 7 files changed, 75 insertions(+), 29 deletions(-)

diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c
index 0f25a19..7c3360a 100644
--- a/src/ngircd/channel.c
+++ b/src/ngircd/channel.c
@@ -509,7 +509,7 @@ Channel_IsValidName( const char *Name )
 	if (strlen(Name) <= 1)
 		return false;
 #endif
-	if (strchr("+#", Name[0]) == NULL)
+	if (strchr("#&+", Name[0]) == NULL)
 		return false;
 	if (strlen(Name) >= CHANNEL_NAME_LEN)
 		return false;
@@ -875,6 +875,11 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch
 	assert( Origin != NULL );
 	assert( Reason != NULL );
 
+	/* Do not inform other servers if the channel is local to this server,
+	 * regardless of what the caller requested! */
+	if(InformServer)
+		InformServer = !Channel_IsLocal(Chan);
+
 	last_cl2chan = NULL;
 	cl2chan = My_Cl2Chan;
 	while( cl2chan )
@@ -896,14 +901,16 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch
 	switch( Type )
 	{
 		case REMOVE_QUIT:
-			/* QUIT: other servers have already been notified, see Client_Destroy();
-			 * so only inform other clients in same channel. */
+			/* QUIT: other servers have already been notified, 
+			 * see Client_Destroy(); so only inform other clients
+			 * in same channel. */
 			assert( InformServer == false );
 			LogDebug("User \"%s\" left channel \"%s\" (%s).",
 					Client_Mask( Client ), c->name, Reason );
 			break;
 		case REMOVE_KICK:
-			/* User was KICKed: inform other servers and all users in channel */
+			/* User was KICKed: inform other servers (public
+			 * channels) and all users in the channel */
 			if( InformServer )
 				IRC_WriteStrServersPrefix( Client_NextHop( Origin ),
 					Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason);
@@ -1062,5 +1069,4 @@ Delete_Channel( CHANNEL *Chan )
 	return true;
 } /* Delete_Channel */
 
-
 /* -eof- */
diff --git a/src/ngircd/channel.h b/src/ngircd/channel.h
index 792f269..91cc278 100644
--- a/src/ngircd/channel.h
+++ b/src/ngircd/channel.h
@@ -124,5 +124,10 @@ GLOBAL bool Channel_AddBan PARAMS((CHANNEL *c, const char *Mask ));
 
 GLOBAL bool Channel_ShowBans PARAMS((CLIENT *client, CHANNEL *c));
 GLOBAL bool Channel_ShowInvites PARAMS((CLIENT *client, CHANNEL *c));
+
+#define Channel_IsLocal(c) (Channel_Name(c)[0] == '&')
+
+
 #endif
+
 /* -eof- */
diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c
index 586d46c..27414d3 100644
--- a/src/ngircd/irc-channel.c
+++ b/src/ngircd/irc-channel.c
@@ -156,16 +156,24 @@ join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
 	else
 		modes[0] = '\0';
 
-	/* forward to other servers */
-	snprintf(str, sizeof(str), "%s%s", channame, modes);
-	IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0', cb_join_forward, str);
+	/* forward to other servers (if it is not a local channel) */
+	if (!Channel_IsLocal(chan)) {
+		snprintf(str, sizeof(str), "%s%s", channame, modes);
+		IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0',
+						 cb_join_forward, str);
+	}
 
 	/* tell users in this channel about the new client */
-	IRC_WriteStrChannelPrefix(Client, chan, target, false, "JOIN :%s", channame);
-	if (modes[1])
-		IRC_WriteStrChannelPrefix(Client, chan, target, false, "MODE %s +%s %s",
-						channame, &modes[1], Client_ID(target));
-}
+	IRC_WriteStrChannelPrefix(Client, chan, target, false,
+				  "JOIN :%s",  channame);
+
+	/* syncronize channel modes */
+	if (modes[1]) {
+		IRC_WriteStrChannelPrefix(Client, chan, target, false,
+					  "MODE %s +%s %s", channame,
+					  &modes[1], Client_ID(target));
+	}
+} /* join_forward */
 
 
 static bool
@@ -423,12 +431,18 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 		 Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
 		 Req->argv[1][0] ? Req->argv[1] : "<none>");
 
-	/* im Channel bekannt machen und an Server weiterleiten */
-	IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
-	IRC_WriteStrChannelPrefix( Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
+	/* Update channel and forward new topic to other servers */
+	if (!Channel_IsLocal(chan))
+		IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
+					  Req->argv[0], Req->argv[1]);
+	IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s",
+				  Req->argv[0], Req->argv[1]);
 
-	if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
-	else return CONNECTED;
+	if (Client_Type(Client) == CLIENT_USER)
+		return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
+						Req->argv[0], Req->argv[1]);
+	else
+		return CONNECTED;
 } /* IRC_TOPIC */
 
 
diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c
index 34198bf..ee60566 100644
--- a/src/ngircd/irc-info.c
+++ b/src/ngircd/irc-info.c
@@ -932,7 +932,14 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 		cl2chan = Channel_NextChannelOf( c, cl2chan );
 
 		/* Secret channel? */
-		if( strchr( Channel_Modes( chan ), 's' ) && ! Channel_IsMemberOf( chan, Client )) continue;
+		if (strchr(Channel_Modes(chan), 's')
+		    && !Channel_IsMemberOf(chan, Client))
+			continue;
+
+		/* Local channel and request is not from a user? */
+		if (Client_Type(Client) == CLIENT_SERVER
+		    && Channel_IsLocal(chan))
+			continue;
 
 		/* Concatenate channel names */
 		if( str[strlen( str ) - 1] != ':' ) strlcat( str, " ", sizeof( str ));
diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c
index 76e3ab4..ed70a9b 100644
--- a/src/ngircd/irc-mode.c
+++ b/src/ngircd/irc-mode.c
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2005 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
  *
  * 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
@@ -558,7 +558,15 @@ chan_exit:
 			the_modes[len] = '\0';
 
 		if (Client_Type(Client) == CLIENT_SERVER) {
-			/* Forward mode changes to channel users and other servers */
+			/* MODE requests for local channels from other servers
+			 * are definitely invalid! */
+			if (Channel_IsLocal(Channel)) {
+				Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
+				return CONNECTED;
+			}
+
+			/* Forward mode changes to channel users and all the
+			 * other remote servers: */
 			IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args);
 			IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args);
 		} else {
@@ -567,10 +575,14 @@ chan_exit:
 			/* Send reply to client and inform other servers and channel users */
 			ok = IRC_WriteStrClientPrefix(Client, Origin, "MODE %s %s%s",
 					Channel_Name(Channel), the_modes, the_args);
-			IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s",
-					Channel_Name(Channel), the_modes, the_args);
-			IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s",
-						Channel_Name(Channel), the_modes, the_args);
+			/* Only forward requests for non-local channels */
+			if (!Channel_IsLocal(Channel))
+				IRC_WriteStrServersPrefix(Client, Origin,
+					"MODE %s %s%s", Channel_Name(Channel),
+					the_modes, the_args);
+			IRC_WriteStrChannelPrefix(Client, Channel, Origin,
+				false, "MODE %s %s%s", Channel_Name(Channel),
+				the_modes, the_args);
 		}
 	}
 
diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h
index 36d6f48..d6a92c9 100644
--- a/src/ngircd/messages.h
+++ b/src/ngircd/messages.h
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2004 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2008 Alexander Barton <alex@barton.de>
  *
  * 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
@@ -8,8 +8,6 @@
  * (at your option) any later version.
  * Please read the file COPYING, README and AUTHORS for more information.
  *
- * $Id: messages.h,v 1.75 2008/02/17 13:26:42 alex Exp $
- *
  * IRC numerics (Header)
  */
 
@@ -22,7 +20,7 @@
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=# CHANMODES=bI,k,l,imnPst CHANLIMIT=#:%d :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=bI,k,l,imnPst CHANLIMIT=#&+:%d :are supported on this server"
 #define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d PENALTY :are supported on this server"
 
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c
index fa32097..74c5c12 100644
--- a/src/ngircd/numeric.c
+++ b/src/ngircd/numeric.c
@@ -341,6 +341,10 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	/* Announce all channels to the new server */
 	chan = Channel_First();
 	while (chan) {
+		if (Channel_IsLocal(chan)) {
+			chan = Channel_Next(chan);
+			continue;
+		}
 #ifdef IRCPLUS
 		/* Send CHANINFO if the peer supports it */
 		if (strchr(Client_Flags(Client), 'C')) {
-- 
1.5.6.5

