# HG changeset patch
# User William Pitcock <nenolod@dereferenced.org>
# Date 1332712182 18000
# Node ID 2f48d320da40fe27e54d4bb6c82f37f937ed0433
# Parent  d32300a1d3ef6276a6c93b5f4ec20acc12f327d1
- Add support for server-enforced mode locks (MLOCK).  This allows the IRCd to
  enforce MLOCKs that are set by services, which eliminates clashes between
  users setting modes and services enforcing it's mlock on channels.  (#3055)

diff -r d32300a1d3ef -r 2f48d320da40 Changes
--- a/Changes	Sun Mar 25 14:33:53 2012 -0500
+++ b/Changes	Sun Mar 25 16:49:42 2012 -0500
@@ -2414,3 +2414,6 @@
   can be used to request passive notifications for accountname changes.
 - Change RPL_WHOISREGNICK (307) text to be more clear, especially with the new
   accountnames support (#3399).
+- Add support for server-enforced mode locks (MLOCK).  This allows the IRCd to
+  enforce MLOCKs that are set by services, which eliminates clashes between
+  users setting modes and services enforcing it's mlock on channels.  (#3055)
diff -r d32300a1d3ef -r 2f48d320da40 include/h.h
--- a/include/h.h	Sun Mar 25 14:33:53 2012 -0500
+++ b/include/h.h	Sun Mar 25 16:49:42 2012 -0500
@@ -274,6 +274,7 @@
 extern void read_authports(aClient *);
 extern void send_authports(aClient *);
 
+extern void set_channel_mlock(aClient *, aClient *, aChannel *, const char *, int);
 
 extern void restart(char *);
 extern void server_reboot(char *);
diff -r d32300a1d3ef -r 2f48d320da40 include/msg.h
--- a/include/msg.h	Sun Mar 25 14:33:53 2012 -0500
+++ b/include/msg.h	Sun Mar 25 16:49:42 2012 -0500
@@ -297,6 +297,9 @@
 #define MSG_EOS		"EOS"
 #define TOK_EOS		"ES"
 
+#define MSG_MLOCK	"MLOCK"
+#define TOK_MLOCK	"ML"
+
 #define MAXPARA    	15
 
 extern int m_error();
diff -r d32300a1d3ef -r 2f48d320da40 include/numeric.h
--- a/include/numeric.h	Sun Mar 25 14:33:53 2012 -0500
+++ b/include/numeric.h	Sun Mar 25 16:49:42 2012 -0500
@@ -370,6 +370,8 @@
 
 #define RPL_WHOISSECURE      671
 
+#define ERR_MLOCKRESTRICTED	742
+
 #define ERR_CANNOTDOCOMMAND 972
 #define ERR_CANNOTCHANGECHANMODE 974
 
diff -r d32300a1d3ef -r 2f48d320da40 include/struct.h
--- a/include/struct.h	Sun Mar 25 14:33:53 2012 -0500
+++ b/include/struct.h	Sun Mar 25 16:49:42 2012 -0500
@@ -372,6 +372,7 @@
 #define PROTO_SASL	0x80000  /* client is doing SASL */
 #define PROTO_AWAY_NOTIFY	0x100000	/* client supports away-notify */
 #define PROTO_ACCOUNT_NOTIFY	0x200000	/* client supports account-notify */
+#define PROTO_MLOCK		0x400000	/* server supports MLOCK */
 
 /*
  * flags macros.
@@ -1627,6 +1628,7 @@
 #ifdef JOINTHROTTLE
 	aJFlood *jflood;
 #endif
+	char *mode_lock;
 	char chname[1];
 };
 
diff -r d32300a1d3ef -r 2f48d320da40 src/channel.c
--- a/src/channel.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/channel.c	Sun Mar 25 16:49:42 2012 -0500
@@ -1416,6 +1416,8 @@
 #ifdef JOINTHROTTLE
 		cmodej_delchannelentries(chptr);
 #endif
+		if (chptr->mode_lock)
+			MyFree(chptr->mode_lock);
 		if (chptr->topic)
 			MyFree(chptr->topic);
 		if (chptr->topic_nick)
@@ -1863,3 +1865,24 @@
 	}
 }
 #endif
+
+/* set_channel_mlock()
+ *
+ * inputs	- client, source, channel, params
+ * output	- 
+ * side effects - channel mlock is changed / MLOCK is propagated
+ */
+void
+set_channel_mlock(aClient *cptr, aClient *sptr, aChannel *chptr, const char *newmlock, int propagate)
+{
+	if (chptr->mode_lock)
+		MyFree(chptr->mode_lock);
+	chptr->mode_lock = newmlock != NULL ? strdup(newmlock) : NULL;
+
+	if (propagate)
+	{
+		sendto_serv_butone_token(cptr, cptr->name, MSG_MLOCK, TOK_MLOCK, "%B %s :%s",
+					 chptr->creationtime, chptr->chname,
+					 BadPtr(chptr->mode_lock) ? "" : chptr->mode_lock);
+	}
+}
diff -r d32300a1d3ef -r 2f48d320da40 src/modules/m_mode.c
--- a/src/modules/m_mode.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/modules/m_mode.c	Sun Mar 25 16:49:42 2012 -0500
@@ -47,6 +47,7 @@
 
 /* Forward declarations */
 DLLFUNC CMD_FUNC(m_mode);
+DLLFUNC CMD_FUNC(m_mlock);
 DLLFUNC void _do_mode(aChannel *chptr, aClient *cptr, aClient *sptr, int parc, char *parv[], time_t sendts, int samode);
 DLLFUNC void _set_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[], u_int *pcount,
     char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce);
@@ -96,6 +97,7 @@
 DLLFUNC int MOD_INIT(m_mode)(ModuleInfo *modinfo)
 {
 	CommandAdd(modinfo->handle, MSG_MODE, TOK_MODE, m_mode, MAXPARA, M_USER|M_SERVER);
+	CommandAdd(modinfo->handle, MSG_MLOCK, TOK_MLOCK, m_mlock, MAXPARA, M_SERVER);
 	MARK_AS_OFFICIAL_MODULE(modinfo);
 	return MOD_SUCCESS;
 }
@@ -1935,6 +1937,7 @@
 	aCtab *tab = &cFlagTab[0];
 	aCtab foundat;
 	int  found = 0;
+	int  sent_mlock_warning = 0;
 	unsigned int htrig = 0;
 	long oldm, oldl;
 	int checkrestr = 0, warnrestr = 1;
@@ -1978,6 +1981,15 @@
 			  break;
 #endif
 		  default:
+			  if (MyClient(cptr) && chptr->mode_lock && strchr(chptr->mode_lock, *curchr) != NULL)
+			  {
+				  if (!sent_mlock_warning)
+				  {
+					  sendto_one(cptr, err_str(ERR_MLOCKRESTRICTED), me.name, cptr->name, chptr->chname, *curchr, chptr->mode_lock);
+					  sent_mlock_warning++;
+				  }
+				  continue;
+			  }
 			  found = 0;
 			  tab = &cFlagTab[0];
 			  while ((tab->mode != 0x0) && found == 0)
@@ -2490,3 +2502,28 @@
 
 	return 0;
 }
+
+CMD_FUNC(m_mlock)
+{
+	aChannel *chptr = NULL;
+	TS chants;
+
+	if (*parv[1] == '!')
+		chants = (TS) base64dec(parv[1] + 1);
+	else
+		chants = (TS) atol(parv[1]);
+
+	/* Now, try to find the channel in question */
+	chptr = find_channel(parv[2], NullChn);
+	if(chptr == NULL)
+		return 0;
+
+	/* TS is higher, drop it. */
+	if(chants > chptr->creationtime)
+		return 0;
+
+	if(IsServer(sptr))
+		set_channel_mlock(cptr, sptr, chptr, parv[3], TRUE);
+
+	return 0;
+}
diff -r d32300a1d3ef -r 2f48d320da40 src/modules/m_protoctl.c
--- a/src/modules/m_protoctl.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/modules/m_protoctl.c	Sun Mar 25 16:49:42 2012 -0500
@@ -432,6 +432,18 @@
 			if (s[8] != '*')
 				send_protoctl_servers(sptr, 1);
 		}
+		else if ((strcmp(s, "MLOCK")) == 0)
+		{
+#ifdef PROTOCTL_MADNESS
+			if (remove)
+			{
+				cptr->proto &= ~PROTO_MLOCK;
+				continue;
+			}
+#endif
+			Debug((DEBUG_ERROR, "Chose protocol %s for link %s", proto, cptr->name));
+			cptr->proto |= PROTO_MLOCK;
+		}
 		/*
 		 * Add other protocol extensions here, with proto
 		 * containing the base option, and options containing
diff -r d32300a1d3ef -r 2f48d320da40 src/modules/m_server.c
--- a/src/modules/m_server.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/modules/m_server.c	Sun Mar 25 16:49:42 2012 -0500
@@ -1204,6 +1204,14 @@
 	if (modebuf[1] || *parabuf)
 		sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
 		    parabuf, chptr->creationtime);
+
+	/* send MLOCK here too... --nenolod */
+	if (CHECKPROTO(cptr, PROTO_MLOCK))
+	{
+		sendto_one(cptr, (CHECKPROTO(cptr, PROTO_SJB64) ? "%s %B %s :%s" : "%s %lu %s :%s"),
+			   (IsToken(cptr) ? TOK_MLOCK : MSG_MLOCK), chptr->creationtime, chptr->chname,
+			   BadPtr(chptr->mode_lock) ? "" : chptr->mode_lock);
+	}
 }
 
 
diff -r d32300a1d3ef -r 2f48d320da40 src/modules/m_sjoin.c
--- a/src/modules/m_sjoin.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/modules/m_sjoin.c	Sun Mar 25 16:49:42 2012 -0500
@@ -375,6 +375,9 @@
 			    sptr->name, chptr->chname, modebuf, parabuf);
 
 		}
+
+		/* since we're dropping our modes, we want to clear the mlock as well. --nenolod */
+		set_channel_mlock(cptr, sptr, chptr, NULL, FALSE);
 	}
 	/* Mode setting done :), now for our beloved clients */
 	parabuf[0] = 0;
diff -r d32300a1d3ef -r 2f48d320da40 src/s_err.c
--- a/src/s_err.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/s_err.c	Sun Mar 25 16:49:42 2012 -0500
@@ -798,7 +798,7 @@
 /* 739 */ NULL,
 /* 740 */ NULL,
 /* 741 */ NULL,
-/* 742 */ NULL,
+/* 742 ERR_MLOCKRESTRICTED */ ":%s 742 %s %s %c %s :MODE cannot be set due to channel having an active MLOCK restriction policy",
 /* 743 */ NULL,
 /* 744 */ NULL,
 /* 745 */ NULL,
diff -r d32300a1d3ef -r 2f48d320da40 src/s_serv.c
--- a/src/s_serv.c	Sun Mar 25 14:33:53 2012 -0500
+++ b/src/s_serv.c	Sun Mar 25 16:49:42 2012 -0500
@@ -228,12 +228,13 @@
  * should be enabled or not.
  *
  * ESVID added to denote support of extended SVID values. --nenolod
+ * MLOCK added to denote support of MLOCK command. --nenolod
  */
 void send_proto(aClient *cptr, ConfigItem_link *aconf)
 {
 char buf[1024];
 
-	sprintf(buf, "CHANMODES=%s%s,%s%s,%s%s,%s%s NICKCHARS=%s ESVID",
+	sprintf(buf, "CHANMODES=%s%s,%s%s,%s%s,%s%s NICKCHARS=%s ESVID MLOCK",
 		CHPAR1, EXPAR1, CHPAR2, EXPAR2, CHPAR3, EXPAR3, CHPAR4, EXPAR4, langsinuse);
 #ifdef ZIP_LINKS
 	if (aconf->options & CONNECT_ZIP)
