]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - remotemode.patch
remotemode: add remotemode command to allow a service to change certain usermodes...
[irc/quakenet/snircd-patchqueue.git] / remotemode.patch
CommitLineData
6e84167f 1# HG changeset patch
2# Parent 37c9c74606033a6520033f95aa61690a8e4bcadd
3
4# add REMOTEMODE (P10 token RM) allowing a service do change certain modes on users
5# <source numeric server> RM <target numeric user> <mode changes and arguments> (note: no : prefixing last parameter)
6# only allow +-diwRnIx, allow +h <user@host> and -h (so we dont need remote sethost/SH token anymore), other modes are not allowed to be set remotely
7# usermode x can only be set, not unset (same as normal)
8# usermode h can only be unset, if the user is +rx (or +r and allowed to set +x) - this prevents exposing the realhost out of the blue
9# easy to add/remove modes
10# REMOTEMODE travels from source server to victim server, from where a normal usermode change is broadcasted to the network
11# unfortunately the code is a bit messy because of the sethost stuff, should we in the future accept the sethost.patch (see queue), it can be cleaned up quite a bit
12# after snircd1.3.5 this patch will be moved further down in the patch queue, to clean up the sethost part, and add other usermodes such as +q
13
14diff -r 37c9c7460603 include/handlers.h
15--- a/include/handlers.h
16+++ b/include/handlers.h
17@@ -225,6 +225,7 @@
18 extern int ms_privs(struct Client*, struct Client*, int, char*[]);
19 extern int ms_quit(struct Client*, struct Client*, int, char*[]);
20 extern int ms_reburst(struct Client*, struct Client*, int, char*[]);
21+extern int ms_remotemode(struct Client*, struct Client*, int, char*[]);
22 extern int ms_rping(struct Client*, struct Client*, int, char*[]);
23 extern int ms_rpong(struct Client*, struct Client*, int, char*[]);
24 extern int ms_server(struct Client*, struct Client*, int, char*[]);
25diff -r 37c9c7460603 include/msg.h
26--- a/include/msg.h
27+++ b/include/msg.h
28@@ -368,6 +368,10 @@
29 #define TOK_REBURST "RB"
30 #define CMD_REBURST MSG_REBURST, TOK_REBURST
31
32+#define MSG_REMOTEMODE "REMOTEMODE" /* REMOTEMODE */
33+#define TOK_REMOTEMODE "RM"
34+#define CMD_REMOTEMODE MSG_REMOTEMODE, TOK_REMOTEMODE
35+
36 #define MSG_CAP "CAP"
37 #define TOK_CAP "CAP"
38 #define CMD_CAP MSG_CAP, TOK_CAP
39diff -r 37c9c7460603 ircd/Makefile.in
40--- a/ircd/Makefile.in
41+++ b/ircd/Makefile.in
42@@ -163,6 +163,7 @@
43 m_quit.c \
44 m_reburst.c \
45 m_rehash.c \
46+ m_remotemode.c \
47 m_reset.c \
48 m_restart.c \
49 m_rping.c \
50diff -r 37c9c7460603 ircd/m_remotemode.c
51--- /dev/null
52+++ b/ircd/m_remotemode.c
53@@ -0,0 +1,363 @@
54+/*
55+ * IRC - Internet Relay Chat, ircd/m_mode.c
56+ * Copyright (C) 1990 Jarkko Oikarinen and
57+ * University of Oulu, Computing Center
58+ *
59+ * See file AUTHORS in IRC package for additional names of
60+ * the programmers.
61+ *
62+ * This program is free software; you can redistribute it and/or modify
63+ * it under the terms of the GNU General Public License as published by
64+ * the Free Software Foundation; either version 1, or (at your option)
65+ * any later version.
66+ *
67+ * This program is distributed in the hope that it will be useful,
68+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
69+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
70+ * GNU General Public License for more details.
71+ *
72+ * You should have received a copy of the GNU General Public License
73+ * along with this program; if not, write to the Free Software
74+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
75+ *
76+ * $Id: m_mode.c 1818 2007-07-14 02:40:01Z isomer $
77+ */
78+
79+/*
80+ * m_functions execute protocol messages on this server:
81+ *
82+ * cptr is always NON-NULL, pointing to a *LOCAL* client
83+ * structure (with an open socket connected!). This
84+ * identifies the physical socket where the message
85+ * originated (or which caused the m_function to be
86+ * executed--some m_functions may call others...).
87+ *
88+ * sptr is the source of the message, defined by the
89+ * prefix part of the message if present. If not
90+ * or prefix not found, then sptr==cptr.
91+ *
92+ * (!IsServer(cptr)) => (cptr == sptr), because
93+ * prefixes are taken *only* from servers...
94+ *
95+ * (IsServer(cptr))
96+ * (sptr == cptr) => the message didn't
97+ * have the prefix.
98+ *
99+ * (sptr != cptr && IsServer(sptr) means
100+ * the prefix specified servername. (?)
101+ *
102+ * (sptr != cptr && !IsServer(sptr) means
103+ * that message originated from a remote
104+ * user (not local).
105+ *
106+ * combining
107+ *
108+ * (!IsServer(sptr)) means that, sptr can safely
109+ * taken as defining the target structure of the
110+ * message in this server.
111+ *
112+ * *Always* true (if 'parse' and others are working correct):
113+ *
114+ * 1) sptr->from == cptr (note: cptr->from == cptr)
115+ *
116+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
117+ * *cannot* be a local connection, unless it's
118+ * actually cptr!). [MyConnect(x) should probably
119+ * be defined as (x == x->from) --msa ]
120+ *
121+ * parc number of variable parameter strings (if zero,
122+ * parv is allowed to be NULL)
123+ *
124+ * parv a NULL terminated list of parameter pointers,
125+ *
126+ * parv[0], sender (prefix string), if not present
127+ * this points to an empty string.
128+ * parv[1]...parv[parc-1]
129+ * pointers to additional parameters
130+ * parv[parc] == NULL, *always*
131+ *
132+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
133+ * non-NULL pointers.
134+ */
135+
136+#include "config.h"
137+
138+#include "channel.h"
139+#include "client.h"
140+#include "ircd_features.h"
141+#include "ircd_log.h"
142+#include "ircd_reply.h"
143+#include "ircd_string.h"
144+#include "ircd_snprintf.h"
145+#include "msg.h"
146+#include "msgq.h"
147+#include "numeric.h"
148+#include "querycmds.h"
149+#include "s_conf.h"
150+#include "s_debug.h"
151+#include "s_user.h"
152+#include "send.h"
153+#include "struct.h"
154+#include "numnicks.h"
155+
156+#include <stdlib.h>
157+
158+/**
159+ * ms_remotemode - remotemode server message handler
160+ *
161+ * parv[0] = sender prefix
162+ * parv[1] = target user numeric
163+ * parv[2+] = mode and arguments
164+ *
165+ */
166+int ms_remotemode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
167+{
168+ char *target;
169+ struct Client *acptr;
170+ struct Flags setflags;
171+ struct Membership *chan;
172+
173+ char** p;
174+ char* m;
175+ char *hostmask;
176+ char *user = NULL;
177+ char *host = NULL;
178+ char hiddenhost[USERLEN + HOSTLEN + 2];
179+ int what = MODE_ADD;
180+ int do_host_hiding = 0;
181+ int do_set_host = 0;
182+
183+ /* not from a server */
184+ if (!IsServer(sptr))
185+ return protocol_violation(cptr, "Received REMOTEMODE from user %C", sptr);
186+
187+ /* check paramaters */
188+ if (parc < 3)
189+ return protocol_violation(cptr, "Received too few parameters for REMOTEMODE from %C (got %d - need at least 3)", parc, sptr);
190+
191+ target = parv[1];
192+
193+ /* find user */
194+ if(!(acptr = findNUser(target)))
195+ return 0;
196+
197+ /* TODO: how to pass along all params in an easy way? */
198+ /* not for my user, pass it along */
199+ if (!MyConnect(acptr)) {
200+ sendcmdto_one(sptr, CMD_REMOTEMODE, acptr, "%C %s %s %s %s %s %s %s %s %s %s %s %s %s", acptr,
201+ parv[2],
202+ parc > 3 ? parv[3] : "",
203+ parc > 4 ? parv[4] : "",
204+ parc > 5 ? parv[5] : "",
205+ parc > 6 ? parv[6] : "",
206+ parc > 7 ? parv[7] : "",
207+ parc > 8 ? parv[8] : "",
208+ parc > 9 ? parv[9] : "",
209+ parc > 10 ? parv[10] : "",
210+ parc > 11 ? parv[11] : "",
211+ parc > 12 ? parv[12] : "",
212+ parc > 13 ? parv[13] : "",
213+ parc > 14 ? parv[14] : "");
214+ return 0;
215+ }
216+
217+ /* backup flags */
218+ setflags = cli_flags(acptr);
219+
220+ /* parse mode change string(s) */
221+ for (p = &parv[2]; *p && p<&parv[parc]; p++) { /* p is changed in loop too */
222+ for (m = *p; *m; m++) {
223+ switch (*m) {
224+ case '+':
225+ what = MODE_ADD;
226+ break;
227+ case '-':
228+ what = MODE_DEL;
229+ break;
230+ case 'w':
231+ if (what == MODE_ADD)
232+ SetWallops(acptr);
233+ else
234+ ClearWallops(acptr);
235+ break;
236+ case 'i':
237+ if (what == MODE_ADD)
238+ SetInvisible(acptr);
239+ else
240+ ClearInvisible(acptr);
241+ break;
242+ case 'd':
243+ if (what == MODE_ADD)
244+ SetDeaf(acptr);
245+ else
246+ ClearDeaf(acptr);
247+ break;
248+ case 'n':
249+ if (what == MODE_ADD)
250+ SetNoChan(acptr);
251+ else
252+ ClearNoChan(acptr);
253+ break;
254+ case 'I':
255+ if (what == MODE_ADD)
256+ SetNoIdle(acptr);
257+ else
258+ ClearNoIdle(acptr);
259+ break;
260+ case 'R':
261+ if (what == MODE_ADD)
262+ SetAccountOnly(acptr);
263+ else
264+ ClearAccountOnly(acptr);
265+ break;
266+ case 'x':
267+ if (what == MODE_ADD)
268+ do_host_hiding = 1;
269+ break;
270+ case 'h':
271+ if (what == MODE_ADD) {
272+ if (*(p + 1) && is_hostmask(*(p + 1))) {
273+ do_set_host = 1;
274+ hostmask = *++p;
275+ } else {
276+ if (!*(p+1))
277+ protocol_violation(sptr, "Received REMOTEMODE +h without host parameter for user %C", acptr);
278+ else {
279+ protocol_violation(sptr, "Received REMOTEMODE +h with invalid host parameter %s for user %C", *(p+1), acptr);
280+ p++; /* Swallow the arg anyway */
281+ }
282+ }
283+ } else { /* MODE_DEL */
284+ do_set_host = 1;
285+ hostmask = NULL;
286+ }
287+ break;
288+
289+ default:
290+ protocol_violation(sptr, "Received REMOTEMODE %c%c unknown user mode flag or disallowed to set remotely for user %C",
291+ what == MODE_ADD ? '+' : '-', *m, acptr);
292+ break;
293+ }
294+ }
295+ }
296+
297+ /* only send wallops to opers */
298+ if (feature_bool(FEAT_WALLOPS_OPER_ONLY) && !IsAnOper(acptr) && !FlagHas(&setflags, FLAG_WALLOP))
299+ ClearWallops(acptr);
300+
301+ /* do host hiding for +x */
302+ if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding)
303+ hide_hostmask(acptr, FLAG_HIDDENHOST);
304+
305+ /* sanity checks for -h */
306+ if (do_set_host && !hostmask) {
307+ /* user has no sethost or has no account
308+ *
309+ * user has +h - their host is hidden, do not remove it
310+ * unless the user has an account set
311+ * we should not out of the blue expose the real host
312+ */
313+ if (!IsSetHost(acptr) || !IsAccount(acptr))
314+ do_set_host = 0;
315+
316+ /* user not +x and not allowed to set it */
317+ else if (!IsHiddenHost(acptr) && !feature_bool(FEAT_HOST_HIDING))
318+ do_set_host = 0;
319+
320+ /* set +x */
321+ else
322+ SetHiddenHost(acptr);
323+ }
324+
325+ /* sanity checks for +h */
326+ if (do_set_host && hostmask) {
327+ if ((host = strrchr(hostmask, '@'))) {
328+ *host++ = '\0';
329+ user = hostmask;
330+ }
331+ else
332+ host = hostmask;
333+
334+ /* check if new sethost is different from before */
335+ if (IsSetHost(acptr) &&
336+ (!user || strcmp(cli_user(acptr)->username, user) == 0) &&
337+ strcmp(cli_user(acptr)->host, host) == 0)
338+ do_set_host = 0;
339+ }
340+
341+ /* do host hiding for +h/-h */
342+ if (do_set_host) {
343+
344+ /* quit user from channel */
345+ sendcmdto_common_channels_butone(acptr, CMD_QUIT, acptr, ":Host change");
346+
347+ /* set +h */
348+ if (host) {
349+ SetSetHost(acptr);
350+ /* clear +h in old flags so +h is sent out again with new sethost param */
351+ FlagClr(&setflags, FLAG_SETHOST);
352+ if (user)
353+ ircd_strncpy(cli_user(acptr)->username, user, USERLEN);
354+ ircd_strncpy(cli_user(acptr)->host, host, HOSTLEN);
355+
356+ /* set -h */
357+ } else {
358+ ClearSetHost(acptr);
359+ ircd_strncpy(cli_user(acptr)->username, cli_user(acptr)->realusername, USERLEN);
360+ /* user is +rx - need to restore +x host */
361+ if (HasHiddenHost(acptr))
362+ ircd_snprintf(0, cli_user(acptr)->host, HOSTLEN, "%s.%s",
363+ cli_user(acptr)->account, feature_str(FEAT_HIDDEN_HOST));
364+ else
365+ ircd_strncpy(cli_user(acptr)->host, cli_user(acptr)->realhost, HOSTLEN);
366+ }
367+
368+ /* inform user of hidden host */
369+ ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
370+ cli_user(acptr)->username, cli_user(acptr)->host);
371+ send_reply(acptr, RPL_HOSTHIDDEN, hiddenhost);
372+
373+ /*
374+ * Go through all channels the client was on, rejoin him
375+ * and set the modes, if any
376+ */
377+ for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel) {
378+ /* Invalidate bans against the user so we check them again */
379+ ClearBanValid(chan);
380+ if (IsZombie(chan))
381+ continue;
382+ /* If this channel has delayed joins and the user has no modes, just set
383+ * the delayed join flag rather than showing the join, even if the user
384+ * was visible before */
385+ if (!IsChanOp(chan) && !HasVoice(chan)
386+ && (chan->channel->mode.mode & MODE_DELJOINS)) {
387+ SetDelayedJoin(chan);
388+ } else {
389+ sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chan->channel, acptr, 0,
390+ "%H", chan->channel);
391+ }
392+ if (IsChanOp(chan) && HasVoice(chan)) {
393+ sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, acptr, 0,
394+ "%H +ov %C %C", chan->channel, acptr, acptr);
395+ } else if (IsChanOp(chan) || HasVoice(chan)) {
396+ sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, acptr, 0,
397+ "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', acptr);
398+ }
399+ }
400+ }
401+
402+ /* adjust count for invisible/visible users */
403+ if (FlagHas(&setflags, FLAG_INVISIBLE) && !IsInvisible(acptr)) {
404+ assert(UserStats.inv_clients > 0);
405+ --UserStats.inv_clients;
406+ }
407+ if (!FlagHas(&setflags, FLAG_INVISIBLE) && IsInvisible(acptr)) {
408+ ++UserStats.inv_clients;
409+ }
410+ assert(UserStats.inv_clients <= UserStats.clients + UserStats.unknowns);
411+
412+ /* send out the mode */
413+ send_umode_out(acptr, acptr, &setflags, 0);
414+
415+ return 0;
416+}
417diff -r 37c9c7460603 ircd/parse.c
418--- a/ircd/parse.c
419+++ b/ircd/parse.c
420@@ -548,6 +548,13 @@
421 { m_ignore, m_ignore, ms_reburst, m_ignore, m_ignore }
422 },
423 {
424+ MSG_REMOTEMODE,
425+ TOK_REMOTEMODE,
426+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
427+ /* UNREG, CLIENT, SERVER, OPER, SERVICE */
428+ { m_ignore, m_ignore, ms_remotemode, m_ignore, m_ignore }
429+ },
430+ {
431 MSG_HASH,
432 TOK_HASH,
433 0, MAXPARA, MFLG_SLOW, 0, NULL,