1 /*
2  *   IRC - Internet Relay Chat, src/modules/m_mode.c
3  *   (C) 2005 The UnrealIRCd Team
4  *
5  *   See file AUTHORS in IRC package for additional names of
6  *   the programmers.
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation; either version 1, or (at your option)
11  *   any later version.
12  *
13  *   This program is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 #include "config.h"
23 #include "struct.h"
24 #include "common.h"
25 #include "sys.h"
26 #include "numeric.h"
27 #include "msg.h"
28 #include "proto.h"
29 #include "channel.h"
30 #include "macros.h"
31 #include <time.h>
32 #include <sys/stat.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #ifdef _WIN32
37 #include <io.h>
38 #endif
39 #include <fcntl.h>
40 #include "h.h"
41 #ifdef STRIPBADWORDS
42 #include "badwords.h"
43 #endif
44 #ifdef _WIN32
45 #include "version.h"
46 #endif
47 
48 /* Forward declarations */
49 DLLFUNC CMD_FUNC(m_mode);
50 DLLFUNC CMD_FUNC(m_mlock);
51 DLLFUNC void _do_mode(aChannel *chptr, aClient *cptr, aClient *sptr, int parc, char *parv[], time_t sendts, int samode);
52 DLLFUNC void _set_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[], u_int *pcount,
53     char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce);
54 DLLFUNC CMD_FUNC(_m_umode);
55 
56 /* local: */
57 static void bounce_mode(aChannel *, aClient *, int, char **);
58 int do_mode_char(aChannel *chptr, long modetype, char modechar, char *param,
59     u_int what, aClient *cptr,
60      u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char bounce, long my_access);
61 int do_extmode_char(aChannel *chptr, int modeindex, char *param, u_int what,
62                     aClient *cptr, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3],
63                     char bounce);
64 #ifdef EXTCMODE
65 void make_mode_str(aChannel *chptr, long oldm, Cmode_t oldem, long oldl, int pcount,
66     char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce);
67 #else
68 void make_mode_str(aChannel *chptr, long oldm, long oldl, int pcount,
69     char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce);
70 #endif
71 static void mode_cutoff(char *s);
72 static void mode_cutoff2(aClient *sptr, aChannel *chptr, int *parc_out, char *parv[]);
73 
74 static int samode_in_progress = 0;
75 
76 #define MSG_MODE 	"MODE"
77 #define TOK_MODE 	"G"
78 
79 ModuleHeader MOD_HEADER(m_mode)
80   = {
81 	"m_mode",
82 	"$Id$",
83 	"command /mode",
84 	"3.2-b8-1",
85 	NULL
86     };
87 
MOD_TEST(m_mode)88 DLLFUNC int MOD_TEST(m_mode)(ModuleInfo *modinfo)
89 {
90 	MARK_AS_OFFICIAL_MODULE(modinfo);
91 	EfunctionAddVoid(modinfo->handle, EFUNC_DO_MODE, _do_mode);
92 	EfunctionAddVoid(modinfo->handle, EFUNC_SET_MODE, _set_mode);
93 	EfunctionAdd(modinfo->handle, EFUNC_M_UMODE, _m_umode);
94 	return MOD_SUCCESS;
95 }
96 
MOD_INIT(m_mode)97 DLLFUNC int MOD_INIT(m_mode)(ModuleInfo *modinfo)
98 {
99 	CommandAdd(modinfo->handle, MSG_MODE, TOK_MODE, m_mode, MAXPARA, M_USER|M_SERVER);
100 	CommandAdd(modinfo->handle, MSG_MLOCK, TOK_MLOCK, m_mlock, MAXPARA, M_SERVER);
101 	MARK_AS_OFFICIAL_MODULE(modinfo);
102 	return MOD_SUCCESS;
103 }
104 
MOD_LOAD(m_mode)105 DLLFUNC int MOD_LOAD(m_mode)(int module_load)
106 {
107 	return MOD_SUCCESS;
108 }
109 
MOD_UNLOAD(m_mode)110 DLLFUNC int MOD_UNLOAD(m_mode)(int module_unload)
111 {
112 	return MOD_SUCCESS;
113 }
114 
115 /*
116  * m_mode -- written by binary (garryb@binary.islesfan.net)
117  *	Completely rewrote it.  The old mode command was 820 lines of ICKY
118  * coding, which is a complete waste, because I wrote it in 570 lines of
119  * *decent* coding.  This is also easier to read, change, and fine-tune.  Plus,
120  * everything isn't scattered; everything's grouped where it should be.
121  *
122  * parv[0] - sender
123  * parv[1] - channel
124  */
CMD_FUNC(m_mode)125 CMD_FUNC(m_mode)
126 {
127 	long unsigned sendts = 0;
128 	Ban *ban;
129 	aChannel *chptr;
130 
131 
132 	/* Now, try to find the channel in question */
133 	if (parc > 1)
134 	{
135 		if (*parv[1] == '#')
136 		{
137 			chptr = find_channel(parv[1], NullChn);
138 			if (chptr == NullChn)
139 			{
140 				return m_umode(cptr, sptr, parc, parv);
141 			}
142 		}
143 		else
144 			return m_umode(cptr, sptr, parc, parv);
145 	}
146 	else
147 	{
148 		sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
149 		    me.name, parv[0], "MODE");
150 		return 0;
151 	}
152 
153 	if (MyConnect(sptr))
154 		clean_channelname(parv[1]);
155 	if (check_channelmask(sptr, cptr, parv[1]))
156 		return 0;
157 
158 	if (parc < 3)
159 	{
160 		*modebuf = *parabuf = '\0';
161 
162 		modebuf[1] = '\0';
163 		channel_modes(sptr, modebuf, parabuf, chptr);
164 		sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
165 		    chptr->chname, modebuf, parabuf);
166 		sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0],
167 		    chptr->chname, chptr->creationtime);
168 		return 0;
169 	}
170 
171 	if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'b'
172 	    && parv[2][1] == '\0') || (parv[2][1] == 'b' && parv[2][2] == '\0'
173 	    && (*parv[2] == '+' || *parv[2] == '-'))))
174 	{
175 		if (!IsMember(sptr, chptr) && !IsAnOper(sptr))
176 			return 0;
177 		/* send ban list */
178 		for (ban = chptr->banlist; ban; ban = ban->next)
179 			sendto_one(sptr, rpl_str(RPL_BANLIST), me.name,
180 			    sptr->name, chptr->chname, ban->banstr,
181 			    ban->who, ban->when);
182 		sendto_one(cptr,
183 		    rpl_str(RPL_ENDOFBANLIST), me.name, sptr->name,
184 		    chptr->chname);
185 		return 0;
186 	}
187 
188 	if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'e'
189 	    && parv[2][1] == '\0') || (parv[2][1] == 'e' && parv[2][2] == '\0'
190 	    && (*parv[2] == '+' || *parv[2] == '-'))))
191 	{
192 		if (!IsMember(sptr, chptr) && !IsAnOper(sptr))
193 			return 0;
194 		/* send exban list */
195 		for (ban = chptr->exlist; ban; ban = ban->next)
196 			sendto_one(sptr, rpl_str(RPL_EXLIST), me.name,
197 			    sptr->name, chptr->chname, ban->banstr,
198 			    ban->who, ban->when);
199 		sendto_one(cptr,
200 		    rpl_str(RPL_ENDOFEXLIST), me.name, sptr->name,
201 		    chptr->chname);
202 		return 0;
203 	}
204 
205 	if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'q'
206 	    && parv[2][1] == '\0') || (parv[2][1] == 'q' && parv[2][2] == '\0'
207 	    && (*parv[2] == '+' || *parv[2] == '-'))))
208 	{
209 		if (!IsMember(sptr, chptr) && !IsAnOper(sptr))
210 			return 0;
211 		{
212 			Member *member;
213 			/* send chanowner list */
214 			/* [Whole story about bad loops removed, sorry ;)]
215 			 * Now rewritten so it works (was: bad logic) -- Syzop
216 			 */
217 			for (member = chptr->members; member; member = member->next)
218 			{
219 				if (is_chanowner(member->cptr, chptr))
220 					sendto_one(sptr, rpl_str(RPL_QLIST),
221 					    me.name, sptr->name, chptr->chname,
222 					    member->cptr->name);
223 			}
224 			sendto_one(cptr,
225 			    rpl_str(RPL_ENDOFQLIST), me.name, sptr->name,
226 			    chptr->chname);
227 			return 0;
228 		}
229 	}
230 
231 	if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'a'
232 	    && parv[2][1] == '\0') || (parv[2][1] == 'a' && parv[2][2] == '\0'
233 	    && (*parv[2] == '+' || *parv[2] == '-'))))
234 	{
235 		if (!IsMember(sptr, chptr) && !IsAnOper(sptr))
236 			return 0;
237 		{
238 			Member *member;
239 			/* send chanowner list */
240 			/* [Whole story about bad loops removed, sorry ;)]
241 			 * Now rewritten so it works (was: bad logic) -- Syzop
242 			 */
243 			for (member = chptr->members; member; member = member->next)
244 			{
245 				if (is_chanprot(member->cptr, chptr))
246 					sendto_one(sptr, rpl_str(RPL_ALIST),
247 					    me.name, sptr->name, chptr->chname,
248 					    member->cptr->name);
249 			}
250 			sendto_one(cptr,
251 			    rpl_str(RPL_ENDOFALIST), me.name, sptr->name,
252 			    chptr->chname);
253 			return 0;
254 		}
255 	}
256 
257 
258 	if (IsPerson(sptr) && parc < 4 && ((*parv[2] == 'I'
259 	    && parv[2][1] == '\0') || (parv[2][1] == 'I' && parv[2][2] == '\0'
260 	    && (*parv[2] == '+' || *parv[2] == '-'))))
261 	{
262 		if (!IsMember(sptr, chptr) && !IsAnOper(sptr))
263 			return 0;
264 		for (ban = chptr->invexlist; ban; ban = ban->next)
265 			sendto_one(sptr, rpl_str(RPL_INVEXLIST), me.name,
266 			    sptr->name, chptr->chname, ban->banstr,
267 			    ban->who, ban->when);
268 		sendto_one(sptr, rpl_str(RPL_ENDOFINVEXLIST), me.name,
269 		    sptr->name, chptr->chname);
270 		return 0;
271 	}
272 	opermode = 0;
273 
274 #ifndef NO_OPEROVERRIDE
275         if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
276             && !is_half_op(sptr, chptr) && (MyClient(sptr) ? (IsOper(sptr) &&
277 	    OPCanOverride(sptr)) : IsOper(sptr)))
278         {
279                 sendts = 0;
280                 opermode = 1;
281                 goto aftercheck;
282         }
283 
284         if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
285             && is_half_op(sptr, chptr) && (MyClient(sptr) ? (IsOper(sptr) &&
286 	    OPCanOverride(sptr)) : IsOper(sptr)))
287         {
288                 opermode = 2;
289                 goto aftercheck;
290         }
291 #endif
292 
293 	if (IsPerson(sptr) && !IsULine(sptr) && !is_chan_op(sptr, chptr)
294 	    && !is_half_op(sptr, chptr)
295 	    && (cptr == sptr || !IsSAdmin(sptr) || !IsOper(sptr)))
296 	{
297 		if (cptr == sptr)
298 		{
299 			sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
300 			    me.name, parv[0], chptr->chname);
301 			return 0;
302 		}
303 		sendto_one(cptr, ":%s MODE %s -oh %s %s 0",
304 		    me.name, chptr->chname, parv[0], parv[0]);
305 		/* Tell the other server that the user is
306 		 * de-opped.  Fix op desyncs. */
307 		bounce_mode(chptr, cptr, parc - 2, parv + 2);
308 		return 0;
309 	}
310 
311 	if (IsServer(sptr) && (sendts = TS2ts(parv[parc - 1]))
312 	    && !IsULine(sptr) && chptr->creationtime
313 	    && sendts > chptr->creationtime)
314 	{
315 		if (!(*parv[2] == '&'))	/* & denotes a bounce */
316 		{
317 			/* !!! */
318 			sendto_snomask(SNO_EYES,
319 			    "*** TS bounce for %s - %lu(ours) %lu(theirs)",
320 			    chptr->chname, chptr->creationtime, sendts);
321 			bounce_mode(chptr, cptr, parc - 2, parv + 2);
322 		}
323 		return 0;
324 	}
325 	if (IsServer(sptr) && !sendts && *parv[parc - 1] != '0')
326 		sendts = -1;
327 	if (IsServer(sptr) && sendts != -1)
328 		parc--;		/* server supplied a time stamp, remove it now */
329 
330       aftercheck:
331 /*	if (IsPerson(sptr) && IsOper(sptr)) {
332 		if (!is_chan_op(sptr, chptr)) {
333 			if (MyClient(sptr) && !IsULine(cptr) && mode_buf[1])
334 				sendto_snomask(SNO_EYES, "*** OperMode [IRCop: %s] - [Channel: %s] - [Mode: %s %s]",
335         	 		   sptr->name, chptr->chname, mode_buf, parabuf);
336 			sendts = 0;
337 		}
338 	}
339 */
340 
341 	/* This is to prevent excess +<whatever> modes. -- Syzop */
342 	if (MyClient(sptr) && parv[2])
343 	{
344 		mode_cutoff(parv[2]);
345 		mode_cutoff2(sptr, chptr, &parc, parv);
346 	}
347 
348 	/* Filter out the unprivileged FIRST. *
349 	 * Now, we can actually do the mode.  */
350 
351 	(void)do_mode(chptr, cptr, sptr, parc - 2, parv + 2, sendts, 0);
352 	opermode = 0; /* Important since sometimes forgotten. -- Syzop */
353 	return 0;
354 }
355 
356 /** Cut off mode string (eg: +abcdfjkdsgfgs) at MAXMODEPARAMS modes.
357  * @param s The mode string (modes only, no parameters)
358  * @notes Should only used on local clients
359  * @author Syzop
360  */
mode_cutoff(char * s)361 static void mode_cutoff(char *s)
362 {
363 unsigned short modesleft = MAXMODEPARAMS * 2; /* be generous... */
364 
365 	for (; *s && modesleft; s++)
366 		if ((*s != '-') && (*s != '+'))
367 			modesleft--;
368 	*s = '\0';
369 }
370 
371 /** Another mode cutoff routine - this one for the server-side
372  * amplification/enlargement problem that happens with bans/exempts/invex
373  * as explained in #2837. -- Syzop
374  */
mode_cutoff2(aClient * sptr,aChannel * chptr,int * parc_out,char * parv[])375 static void mode_cutoff2(aClient *sptr, aChannel *chptr, int *parc_out, char *parv[])
376 {
377 int modes = 0;
378 char *s;
379 int len, i;
380 int parc = *parc_out;
381 
382 	if (parc-2 <= 3)
383 		return; /* Less than 3 mode parameters? Then we don't even have to check */
384 
385 	/* Calculate length of MODE if it would go through fully as-is */
386 	/* :nick!user@host MODE #channel +something param1 param2 etc... */
387 	len = strlen(sptr->name) + strlen(sptr->user->username) + strlen(GetHost(sptr)) +
388 	      strlen(chptr->chname) + 11;
389 
390 	len += strlen(parv[2]);
391 
392 	if (*parv[2] != '+' && *parv[2] != '-')
393 		len++;
394 
395 	for (i = 3; parv[i]; i++)
396 	{
397 		len += strlen(parv[i]) + 1; /* (+1 for the space character) */
398 		/* +4 is another potential amplification (per-param).
399 		 * If we were smart we would only check this for b/e/I and only for
400 		 * relevant cases (not for all extended), but this routine is dumb,
401 		 * so we just +4 for any case where the full mask is missing.
402 		 * It's better than assuming +4 for all cases, though...
403 		 */
404 		if (match("*!*@*", parv[i]))
405 			len += 4;
406 	}
407 
408 	/* Now check if the result is acceptable... */
409 	if (len < 510)
410 		return; /* Ok, no problem there... */
411 
412 	/* Ok, we have a potential problem...
413 	 * we just dump the last parameter... check how much space we saved...
414 	 * and try again if that did not help
415 	 */
416 	for (i = parc-1; parv[i] && (i > 3); i--)
417 	{
418 		len -= strlen(parv[i]);
419 		if (match("*!*@*", parv[i]))
420 			len -= 4; /* must adjust accordingly.. */
421 		parv[i] = NULL;
422 		*parc_out--;
423 		if (len < 510)
424 			break;
425 	}
426 	/* This may be reached if like the first parameter is really insane long..
427 	 * which is no problem, as other layers (eg: ban) takes care of that.
428 	 * We're done...
429 	 */
430 }
431 
432 /* bounce_mode -- written by binary
433  *	User or server is NOT authorized to change the mode.  This takes care
434  * of making the bounce string and bounce it.  Because of the 1 for the bounce
435  * param (last param) of the calls to set_mode and make_mode_str, it will not
436  * set the mode, but create the bounce string.
437  */
bounce_mode(aChannel * chptr,aClient * cptr,int parc,char * parv[])438 static void bounce_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[])
439 {
440 	char pvar[MAXMODEPARAMS][MODEBUFLEN + 3];
441 	int  pcount;
442 
443 	set_mode(chptr, cptr, parc, parv, &pcount, pvar, 1);
444 
445 	if (chptr->creationtime)
446 		sendto_one(cptr, ":%s MODE %s &%s %s %lu", me.name,
447 		    chptr->chname, modebuf, parabuf, chptr->creationtime);
448 	else
449 		sendto_one(cptr, ":%s MODE %s &%s %s", me.name, chptr->chname,
450 		    modebuf, parabuf);
451 
452 	/* the '&' denotes a bounce so servers won't bounce a bounce */
453 }
454 
455 /* do_mode -- written by binary
456  *	User or server is authorized to do the mode.  This takes care of
457  * setting the mode and relaying it to other users and servers.
458  */
_do_mode(aChannel * chptr,aClient * cptr,aClient * sptr,int parc,char * parv[],time_t sendts,int samode)459 DLLFUNC void _do_mode(aChannel *chptr, aClient *cptr, aClient *sptr, int parc, char *parv[], time_t sendts, int samode)
460 {
461 	char pvar[MAXMODEPARAMS][MODEBUFLEN + 3];
462 	int  pcount;
463 	char tschange = 0, isbounce = 0;	/* fwd'ing bounce */
464 
465 	if (**parv == '&')
466 		isbounce = 1;
467 
468 	/* Please keep the next 3 lines next to each other */
469 	samode_in_progress = samode;
470 	set_mode(chptr, sptr, parc, parv, &pcount, pvar, 0);
471 	samode_in_progress = 0;
472 
473 	if (IsServer(sptr))
474 	{
475 		if (sendts > 0)
476 		{
477 			if (!chptr->creationtime
478 			    || sendts < chptr->creationtime)
479 			{
480 				tschange = 1;
481 /*
482 				if (chptr->creationtime != 0)
483 					sendto_snomask(SNO_EYES, "*** TS fix for %s - %lu(ours) %lu(theirs)",
484 					chptr->chname, chptr->creationtime, sendts);
485 					*/
486 				chptr->creationtime = sendts;
487 				if (sendts < 750000)
488 				{
489 					sendto_realops(
490 						"Warning! Possible desynch: MODE for channel %s ('%s %s') has fishy timestamp (%ld) (from %s/%s)",
491 						chptr->chname, modebuf, parabuf, sendts, cptr->name, sptr->name);
492 					ircd_log(LOG_ERROR, "Possible desynch: MODE for channel %s ('%s %s') has fishy timestamp (%ld) (from %s/%s)",
493 						chptr->chname, modebuf, parabuf, sendts, cptr->name, sptr->name);
494 				}
495 				/* new chan or our timestamp is wrong */
496 				/* now works for double-bounce prevention */
497 
498 			}
499 			if (sendts > chptr->creationtime && chptr->creationtime)
500 			{
501 				/* theirs is wrong but we let it pass anyway */
502 				sendts = chptr->creationtime;
503 				sendto_one(cptr, ":%s MODE %s + %lu", me.name,
504 				    chptr->chname, chptr->creationtime);
505 			}
506 		}
507 		if (sendts == -1 && chptr->creationtime)
508 			sendts = chptr->creationtime;
509 	}
510 	if (*modebuf == '\0' || (*(modebuf + 1) == '\0' && (*modebuf == '+'
511 	    || *modebuf == '-')))
512 	{
513 		if (tschange || isbounce) {	/* relay bounce time changes */
514 			if (chptr->creationtime)
515 				sendto_serv_butone_token(cptr, me.name,
516 				    MSG_MODE, TOK_MODE, "%s %s+ %lu",
517 				    chptr->chname, isbounce ? "&" : "",
518 				    chptr->creationtime);
519 			else
520 				sendto_serv_butone_token(cptr, me.name,
521 				    MSG_MODE, TOK_MODE, "%s %s+ ",
522 				    chptr->chname, isbounce ? "&" : "");
523 		return;		/* nothing to send */
524 		}
525 	}
526 	/* opermode for twimodesystem --sts */
527 #ifndef NO_OPEROVERRIDE
528 	if (opermode == 1)
529 	{
530 		if (modebuf[1])
531 			sendto_snomask(SNO_EYES,
532 			    "*** OperOverride -- %s (%s@%s) MODE %s %s %s",
533 			    sptr->name, sptr->user->username, sptr->user->realhost,
534 			    chptr->chname, modebuf, parabuf);
535 
536 			/* Logging Implementation added by XeRXeS */
537 			ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s",
538 				sptr->name, sptr->user->username, sptr->user->realhost,
539 				chptr->chname, modebuf, parabuf);
540 
541 		sendts = 0;
542 	}
543 #endif
544 
545 	/* Should stop null modes */
546 	if (*(modebuf + 1) == '\0')
547 		return;
548 	if (IsPerson(sptr) && samode && MyClient(sptr))
549 	{
550 		sendto_serv_butone_token(NULL, me.name, MSG_GLOBOPS,
551 		    TOK_GLOBOPS, ":%s used SAMODE %s (%s%s%s)", sptr->name,
552 		    chptr->chname, modebuf, *parabuf ? " " : "", parabuf);
553 		sendto_failops_whoare_opers
554 		    ("from %s: %s used SAMODE %s (%s%s%s)", me.name, sptr->name,
555 		    chptr->chname, modebuf, *parabuf ? " " : "", parabuf);
556 		sptr = &me;
557 		sendts = 0;
558 	}
559 
560 
561 	sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
562 	    sptr->name, chptr->chname, modebuf, parabuf);
563 	if (IsServer(sptr) && sendts != -1)
564 		sendto_serv_butone_token(cptr, sptr->name, MSG_MODE, TOK_MODE,
565 		    "%s %s%s %s %lu", chptr->chname, isbounce ? "&" : "",
566 		    modebuf, parabuf, sendts);
567 	else if (samode && IsMe(sptr)) /* SAMODE is a special case: always send a TS of 0 (omitting TS==desynch) */
568 		sendto_serv_butone_token(cptr, sptr->name, MSG_MODE, TOK_MODE,
569 		    "%s %s %s 0", chptr->chname, modebuf, parabuf);
570 	else
571 		sendto_serv_butone_token(cptr, sptr->name, MSG_MODE, TOK_MODE,
572 		    "%s %s%s %s", chptr->chname, isbounce ? "&" : "",
573 		    modebuf, parabuf);
574 	/* tell them it's not a timestamp, in case the last param
575 	   ** is a number. */
576 
577 	if (MyConnect(sptr))
578 		RunHook7(HOOKTYPE_LOCAL_CHANMODE, cptr, sptr, chptr, modebuf, parabuf, sendts, samode);
579 	else
580 		RunHook7(HOOKTYPE_REMOTE_CHANMODE, cptr, sptr, chptr, modebuf, parabuf, sendts, samode);
581 }
582 /* make_mode_str -- written by binary
583  *	Reconstructs the mode string, to make it look clean.  mode_buf will
584  *  contain the +x-y stuff, and the parabuf will contain the parameters.
585  *  If bounce is set to 1, it will make the string it needs for a bounce.
586  */
587 #ifdef EXTCMODE
make_mode_str(aChannel * chptr,long oldm,Cmode_t oldem,long oldl,int pcount,char pvar[MAXMODEPARAMS][MODEBUFLEN+3],char * mode_buf,char * para_buf,char bounce)588 void make_mode_str(aChannel *chptr, long oldm, Cmode_t oldem, long oldl, int pcount,
589 	char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce)
590 #else
591 void make_mode_str(aChannel *chptr, long oldm, long oldl, int pcount,
592 	char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, char bounce)
593 #endif
594 {
595 
596 	char tmpbuf[MODEBUFLEN+3], *tmpstr;
597 	aCtab *tab = &cFlagTab[0];
598 	char *x = mode_buf;
599 	int  what, cnt, z;
600 #ifdef EXTCMODE
601 	int i;
602 #endif
603 	char *m;
604 	what = 0;
605 
606 	*tmpbuf = '\0';
607 	*mode_buf = '\0';
608 	*para_buf = '\0';
609 	what = 0;
610 	/* + param-less modes */
611 	tab = &cFlagTab[0];
612 	while (tab->mode != 0x0)
613 	{
614 		if (chptr->mode.mode & tab->mode)
615 		{
616 			if (!(oldm & tab->mode))
617 			{
618 				if (what != MODE_ADD)
619 				{
620 					*x++ = bounce ? '-' : '+';
621 					what = MODE_ADD;
622 				}
623 				*x++ = tab->flag;
624 			}
625 		}
626 		tab++;
627 	}
628 #ifdef EXTCMODE
629 	/* + paramless extmodes... */
630 	for (i=0; i <= Channelmode_highest; i++)
631 	{
632 		if (!Channelmode_Table[i].flag || Channelmode_Table[i].paracount)
633 			continue;
634 		/* have it now and didn't have it before? */
635 		if ((chptr->mode.extmode & Channelmode_Table[i].mode) &&
636 		    !(oldem & Channelmode_Table[i].mode))
637 		{
638 			if (what != MODE_ADD)
639 			{
640 				*x++ = bounce ? '-' : '+';
641 				what = MODE_ADD;
642 			}
643 			*x++ = Channelmode_Table[i].flag;
644 		}
645 	}
646 #endif
647 
648 	*x = '\0';
649 	/* - param-less modes */
650 	tab = &cFlagTab[0];
651 	while (tab->mode != 0x0)
652 	{
653 		if (!(chptr->mode.mode & tab->mode))
654 		{
655 			if (oldm & tab->mode)
656 			{
657 				if (what != MODE_DEL)
658 				{
659 					*x++ = bounce ? '+' : '-';
660 					what = MODE_DEL;
661 				}
662 				*x++ = tab->flag;
663 			}
664 		}
665 		tab++;
666 	}
667 
668 #ifdef EXTCMODE
669 	/* - extmodes (both "param modes" and paramless don't have
670 	 * any params when unsetting...
671 	 */
672 	for (i=0; i <= Channelmode_highest; i++)
673 	{
674 		if (!Channelmode_Table[i].flag /* || Channelmode_Table[i].paracount */)
675 			continue;
676 		/* don't have it now and did have it before */
677 		if (!(chptr->mode.extmode & Channelmode_Table[i].mode) &&
678 		    (oldem & Channelmode_Table[i].mode))
679 		{
680 			if (what != MODE_DEL)
681 			{
682 				*x++ = bounce ? '+' : '-';
683 				what = MODE_DEL;
684 			}
685 			*x++ = Channelmode_Table[i].flag;
686 		}
687 	}
688 #endif
689 
690 	*x = '\0';
691 	/* user limit */
692 	if (chptr->mode.limit != oldl)
693 	{
694 		if ((!bounce && chptr->mode.limit == 0) ||
695 		    (bounce && chptr->mode.limit != 0))
696 		{
697 			if (what != MODE_DEL)
698 			{
699 				*x++ = '-';
700 				what = MODE_DEL;
701 			}
702 			if (bounce)
703 				chptr->mode.limit = 0;	/* set it back */
704 			*x++ = 'l';
705 		}
706 		else
707 		{
708 			if (what != MODE_ADD)
709 			{
710 				*x++ = '+';
711 				what = MODE_ADD;
712 			}
713 			*x++ = 'l';
714 			if (bounce)
715 				chptr->mode.limit = oldl;	/* set it back */
716 			ircsprintf(para_buf, "%s%d ", para_buf, chptr->mode.limit);
717 		}
718 	}
719 	/* reconstruct bkov chain */
720 	for (cnt = 0; cnt < pcount; cnt++)
721 	{
722 		if ((*(pvar[cnt]) == '+') && what != MODE_ADD)
723 		{
724 			*x++ = bounce ? '-' : '+';
725 			what = MODE_ADD;
726 		}
727 		if ((*(pvar[cnt]) == '-') && what != MODE_DEL)
728 		{
729 			*x++ = bounce ? '+' : '-';
730 			what = MODE_DEL;
731 		}
732 		*x++ = *(pvar[cnt] + 1);
733 		tmpstr = &pvar[cnt][2];
734 		z = (MODEBUFLEN * MAXMODEPARAMS);
735 		m = para_buf;
736 		while ((*m)) { m++; }
737 		while ((*tmpstr) && ((m-para_buf) < z))
738 		{
739 			*m = *tmpstr;
740 			m++;
741 			tmpstr++;
742 		}
743 		*m++ = ' ';
744 		*m = '\0';
745 	}
746 	if (bounce)
747 	{
748 		chptr->mode.mode = oldm;
749 #ifdef EXTCMODE
750 		chptr->mode.extmode = oldem;
751 #endif
752 	}
753 	z = strlen(para_buf);
754 	if (para_buf[z - 1] == ' ')
755 		para_buf[z - 1] = '\0';
756 	*x = '\0';
757 	if (*mode_buf == '\0')
758 	{
759 		*mode_buf = '+';
760 		mode_buf++;
761 		*mode_buf = '\0';
762 		/* Don't send empty lines. */
763 	}
764 	return;
765 }
766 
767 
768 /* do_mode_char
769  *  processes one mode character
770  *  returns 1 if it ate up a param, otherwise 0
771  *	written by binary
772  *  modified for Unreal by stskeeps..
773  */
774 
775 #define REQUIRE_PARAMETER() { if (!param || *pcount >= MAXMODEPARAMS) { retval = 0; break; } retval = 1; }
776 
777 #ifdef PREFIX_AQ
778 #define is_xchanop(x) ((x & (CHFL_CHANOP|CHFL_CHANPROT|CHFL_CHANOWNER)))
779 #else
780 #define is_xchanop(x) ((x & CHFL_CHANOP))
781 #endif
782 
do_mode_char(aChannel * chptr,long modetype,char modechar,char * param,u_int what,aClient * cptr,u_int * pcount,char pvar[MAXMODEPARAMS][MODEBUFLEN+3],char bounce,long my_access)783 int  do_mode_char(aChannel *chptr, long modetype, char modechar, char *param,
784 	u_int what, aClient *cptr,
785 	 u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char bounce, long my_access)
786 {
787 	aCtab *tab = &cFlagTab[0];
788 
789 
790 	int  retval = 0;
791 	Member *member = NULL;
792 	Membership *membership = NULL;
793 	aClient *who;
794 	unsigned int tmp = 0;
795 	char tmpbuf[512], *tmpstr;
796 	char tc = ' ';		/* */
797 	int  chasing, x;
798 	int xxi, xyi, xzi, hascolon;
799 	char *xp;
800 	int  notsecure;
801 	chasing = 0;
802 
803 	if ((my_access & CHFL_HALFOP) && !is_xchanop(my_access) && !IsULine(cptr)
804 	    && !op_can_override(cptr) && !samode_in_progress)
805 	{
806 		if (MyClient(cptr) && (modetype == MODE_HALFOP) && (what == MODE_DEL) &&
807 		    param && (find_client(param, NULL) == cptr))
808 		{
809 			/* halfop -h'ing him/herself */
810 			/* ALLOW */
811 		} else
812 		{
813 			/* Ugly halfop hack --sts
814 			   - this allows halfops to do +b +e +v and so on */
815 			/* (Syzop/20040413: Allow remote halfop modes */
816 			if ((Halfop_mode(modetype) == FALSE) && MyClient(cptr))
817 			{
818 				int eaten = 0;
819 				while (tab->mode != 0x0)
820 				{
821 					if (tab->mode == modetype)
822 					{
823 						sendto_one(cptr,
824 						    err_str(ERR_NOTFORHALFOPS), me.name,
825 						    cptr->name, tab->flag);
826 						eaten = tab->parameters;
827 						break;
828 					}
829 					tab++;
830 				}
831 				return eaten;
832 			}
833 		} /* not -h self */
834 	}
835 	switch (modetype)
836 	{
837 	  case MODE_AUDITORIUM:
838 		  if (IsULine(cptr) || IsServer(cptr) || samode_in_progress)
839 			  goto auditorium_ok;
840 		  if (MyClient(cptr) && !is_chanowner(cptr, chptr) && !op_can_override(cptr))
841 		  {
842 			sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name,
843 				   chptr->chname);
844 			break;
845 		  }
846 		  if (op_can_override(cptr) && !is_chanowner(cptr, chptr))
847 		  {
848 		  	opermode = 1;
849 		  }
850 
851 		auditorium_ok:
852 		  goto setthephuckingmode;
853 	  case MODE_OPERONLY:
854 		  if (MyClient(cptr) && !IsAnOper(cptr))
855 		  {
856 			sendto_one(cptr, err_str(ERR_NOPRIVILEGES), me.name, cptr->name);
857 			break;
858 		  }
859 		  goto setthephuckingmode;
860 	  case MODE_ADMONLY:
861 		  if (!IsSkoAdmin(cptr) && !IsServer(cptr)
862 		      && !IsULine(cptr))
863 		  {
864 			sendto_one(cptr, err_str(ERR_NOPRIVILEGES), me.name, cptr->name);
865 			break;
866 		  }
867 		  goto setthephuckingmode;
868 	  case MODE_RGSTR:
869 		  if (!IsServer(cptr) && !IsULine(cptr))
870 		  {
871 			sendto_one(cptr, err_str(ERR_ONLYSERVERSCANCHANGE), me.name, cptr->name,
872 				   chptr->chname);
873 			break;
874 		  }
875 		  goto setthephuckingmode;
876 	  case MODE_SECRET:
877 	  case MODE_PRIVATE:
878 	  case MODE_MODERATED:
879 	  case MODE_TOPICLIMIT:
880 	  case MODE_NOPRIVMSGS:
881 	  case MODE_RGSTRONLY:
882 	  case MODE_MODREG:
883 	  case MODE_NOCOLOR:
884 	  case MODE_NOKICKS:
885 	  case MODE_STRIP:
886 	  	goto setthephuckingmode;
887 
888 	  case MODE_INVITEONLY:
889 		if (what == MODE_DEL && modetype == MODE_INVITEONLY && (chptr->mode.mode & MODE_NOKNOCK))
890 			chptr->mode.mode &= ~MODE_NOKNOCK;
891 		goto setthephuckingmode;
892 	  case MODE_NOKNOCK:
893 		if (what == MODE_ADD && modetype == MODE_NOKNOCK && !(chptr->mode.mode & MODE_INVITEONLY))
894 		{
895 			sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
896 				me.name, cptr->name, 'K', "+i must be set");
897 			break;
898 		}
899 		goto setthephuckingmode;
900 	  case MODE_ONLYSECURE:
901 	  case MODE_NOCTCP:
902 	  case MODE_NONICKCHANGE:
903 	  case MODE_NOINVITE:
904 		setthephuckingmode:
905 		  retval = 0;
906 		  if (what == MODE_ADD) {
907 			  /* +sp bugfix.. (by JK/Luke)*/
908 		 	 if (modetype == MODE_SECRET
909 			      && (chptr->mode.mode & MODE_PRIVATE))
910 				  chptr->mode.mode &= ~MODE_PRIVATE;
911 			  if (modetype == MODE_PRIVATE
912 			      && (chptr->mode.mode & MODE_SECRET))
913 				  chptr->mode.mode &= ~MODE_SECRET;
914 			  if (modetype == MODE_NOCOLOR
915 			      && (chptr->mode.mode & MODE_STRIP))
916 				  chptr->mode.mode &= ~MODE_STRIP;
917 			  if (modetype == MODE_STRIP
918 			      && (chptr->mode.mode & MODE_NOCOLOR))
919 				  chptr->mode.mode &= ~MODE_NOCOLOR;
920 			  chptr->mode.mode |= modetype;
921 		  }
922 		  else
923 		  {
924 			  chptr->mode.mode &= ~modetype;
925 #ifdef NEWCHFLOODPROT
926 			  /* reset joinflood on -i, reset msgflood on -m, etc.. */
927 			  if (chptr->mode.floodprot)
928 			  {
929 				switch(modetype)
930 				{
931 				case MODE_NOCTCP:
932 					chptr->mode.floodprot->c[FLD_CTCP] = 0;
933 					chanfloodtimer_del(chptr, 'C', MODE_NOCTCP);
934 					break;
935 				case MODE_NONICKCHANGE:
936 					chptr->mode.floodprot->c[FLD_NICK] = 0;
937 					chanfloodtimer_del(chptr, 'N', MODE_NONICKCHANGE);
938 					break;
939 				case MODE_MODERATED:
940 					chptr->mode.floodprot->c[FLD_MSG] = 0;
941 					chptr->mode.floodprot->c[FLD_CTCP] = 0;
942 					chanfloodtimer_del(chptr, 'm', MODE_MODERATED);
943 					break;
944 				case MODE_NOKNOCK:
945 					chptr->mode.floodprot->c[FLD_KNOCK] = 0;
946 					chanfloodtimer_del(chptr, 'K', MODE_NOKNOCK);
947 					break;
948 				case MODE_INVITEONLY:
949 					chptr->mode.floodprot->c[FLD_JOIN] = 0;
950 					chanfloodtimer_del(chptr, 'i', MODE_INVITEONLY);
951 					break;
952 				case MODE_MODREG:
953 					chptr->mode.floodprot->c[FLD_MSG] = 0;
954 					chptr->mode.floodprot->c[FLD_CTCP] = 0;
955 					chanfloodtimer_del(chptr, 'M', MODE_MODREG);
956 					break;
957 				case MODE_RGSTRONLY:
958 					chptr->mode.floodprot->c[FLD_JOIN] = 0;
959 					chanfloodtimer_del(chptr, 'R', MODE_RGSTRONLY);
960 					break;
961 				default:
962 					break;
963 				}
964 			  }
965 #endif
966 		  }
967 		  break;
968 
969 /* do pro-opping here (popping) */
970 	  case MODE_CHANOWNER:
971 		  REQUIRE_PARAMETER()
972 		  if (!IsULine(cptr) && !IsServer(cptr) && !is_chanowner(cptr, chptr) && !samode_in_progress)
973 		  {
974 		  	  if (MyClient(cptr) && !op_can_override(cptr))
975 		  	  {
976 		  	  	sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name, chptr->chname);
977 		  	  	break;
978 		  	  }
979 			  if (IsOper(cptr))
980 			  {
981 				if (!is_halfop(cptr, chptr)) /* htrig will take care of halfop override notices */
982 				   opermode = 1;
983 			  }
984 		  }
985 	  case MODE_CHANPROT:
986 		  REQUIRE_PARAMETER()
987 		  /* not uline, not server, not chanowner, not an samode, not -a'ing yourself... */
988 		  if (!IsULine(cptr) && !IsServer(cptr) && !is_chanowner(cptr, chptr) && !samode_in_progress &&
989 		      !(param && (what == MODE_DEL) && (find_client(param, NULL) == cptr)))
990 		  {
991 		  	  if (MyClient(cptr) && !op_can_override(cptr))
992 		  	  {
993 		  	  	sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name, chptr->chname);
994 		  	  	break;
995 		  	  }
996 			  if (IsOper(cptr))
997 			  {
998 				if (!is_halfop(cptr, chptr)) /* htrig will take care of halfop override notices */
999 				   opermode = 1;
1000 			  }
1001 		  }
1002 
1003 
1004 	  case MODE_HALFOP:
1005 	  case MODE_CHANOP:
1006 	  case MODE_VOICE:
1007 		  REQUIRE_PARAMETER()
1008 		  if (!(who = find_chasing(cptr, param, &chasing)))
1009 			  break;
1010 		  if (!who->user)
1011 		  	break;
1012    		  /* codemastr: your patch is a good idea here, but look at the
1013    		     member->flags stuff longer down. this caused segfaults */
1014    		  if (!(membership = find_membership_link(who->user->channel, chptr)))
1015 		  {
1016 			  sendto_one(cptr, err_str(ERR_USERNOTINCHANNEL),
1017 			      me.name, cptr->name, who->name, chptr->chname);
1018 			  break;
1019 		  }
1020 		  member = find_member_link(chptr->members, who);
1021 		  if (!member)
1022 		  {
1023 		  	/* should never happen */
1024 		  	sendto_realops("crap! find_membership_link && !find_member_link !!. Report to unreal team");
1025 		  	break;
1026 		  }
1027 		  /* we make the rules, we bend the rules */
1028 		  if (IsServer(cptr) || IsULine(cptr))
1029 			  goto breaktherules;
1030 
1031 		  /* Services are special! */
1032 		  if (IsServices(member->cptr) && MyClient(cptr) && !IsNetAdmin(cptr) && (what == MODE_DEL))
1033 		  {
1034 			char errbuf[NICKLEN+50];
1035 			ircsprintf(errbuf, "%s is a network service", member->cptr->name);
1036 			sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
1037 				   modechar, errbuf);
1038 			break;
1039 		  }
1040 
1041 		  /* This check not only prevents unprivileged users from doing a -q on chanowners,
1042 		   * it also protects against -o/-h/-v on them.
1043 		   */
1044 		  if (is_chanowner(member->cptr, chptr)
1045 		      && member->cptr != cptr
1046 		      && !is_chanowner(cptr, chptr) && !IsServer(cptr)
1047 		      && !IsULine(cptr) && !opermode && !samode_in_progress && (what == MODE_DEL))
1048 		  {
1049 			  if (MyClient(cptr))
1050 			  {
1051 			  	/* Need this !op_can_override() here again, even with the !opermode
1052 			  	 * check a few lines up, all due to halfops. -- Syzop
1053 			  	 */
1054 				if (!op_can_override(cptr))
1055 				{
1056 					char errbuf[NICKLEN+30];
1057 					ircsprintf(errbuf, "%s is a channel owner", member->cptr->name);
1058 					sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
1059 					   modechar, errbuf);
1060 					break;
1061 				}
1062 			  } else
1063 			  if (IsOper(cptr))
1064 			      opermode = 1;
1065 		  }
1066 
1067 		  /* This check not only prevents unprivileged users from doing a -a on chanadmins,
1068 		   * it also protects against -o/-h/-v on them.
1069 		   */
1070 		  if (is_chanprot(member->cptr, chptr)
1071 		      && member->cptr != cptr
1072 		      && !is_chanowner(cptr, chptr) && !IsServer(cptr) && !opermode && !samode_in_progress
1073 		      && modetype != MODE_CHANOWNER && (what == MODE_DEL))
1074 		  {
1075 			  if (MyClient(cptr))
1076 			  {
1077 			  	/* Need this !op_can_override() here again, even with the !opermode
1078 			  	 * check a few lines up, all due to halfops. -- Syzop
1079 			  	 */
1080 			  	if (!op_can_override(cptr))
1081 			  	{
1082 					char errbuf[NICKLEN+30];
1083 					ircsprintf(errbuf, "%s is a channel admin", member->cptr->name);
1084 					sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE), me.name, cptr->name,
1085 					   modechar, errbuf);
1086 					break;
1087 				}
1088 			  } else
1089 			  if (IsOper(cptr))
1090 			      opermode = 1;
1091 		  }
1092 		breaktherules:
1093 		  tmp = member->flags;
1094 		  if (what == MODE_ADD)
1095 			  member->flags |= modetype;
1096 		  else
1097 			  member->flags &= ~modetype;
1098 		  if ((tmp == member->flags) && (bounce || !IsULine(cptr)))
1099 			  break;
1100 		  /* It's easier to undo the mode here instead of later
1101 		   * when you call make_mode_str for a bounce string.
1102 		   * Why set it if it will be instantly removed?
1103 		   * Besides, pvar keeps a log of it. */
1104 		  if (bounce)
1105 			  member->flags = tmp;
1106 		  if (modetype == MODE_CHANOWNER)
1107 			  tc = 'q';
1108 		  if (modetype == MODE_CHANPROT)
1109 			  tc = 'a';
1110 		  if (modetype == MODE_CHANOP)
1111 			  tc = 'o';
1112 		  if (modetype == MODE_HALFOP)
1113 			  tc = 'h';
1114 		  if (modetype == MODE_VOICE)
1115 			  tc = 'v';
1116 		  /* Make sure membership->flags and member->flags is the same */
1117 		  membership->flags = member->flags;
1118 		  (void)ircsprintf(pvar[*pcount], "%c%c%s",
1119 		      what == MODE_ADD ? '+' : '-', tc, who->name);
1120 		  (*pcount)++;
1121 		  break;
1122 	  case MODE_LIMIT:
1123 		  if (what == MODE_ADD)
1124 		  {
1125 			  if (!param)
1126 			  {
1127 				  retval = 0;
1128 				  break;
1129 			  }
1130 			  retval = 1;
1131 			  tmp = atoi(param);
1132 			  if (chptr->mode.limit == tmp)
1133 				  break;
1134 			  chptr->mode.limit = tmp;
1135 		  }
1136 		  else
1137 		  {
1138 			  retval = 0;
1139 			  if (!chptr->mode.limit)
1140 				  break;
1141 			  chptr->mode.limit = 0;
1142 		  }
1143 		  break;
1144 	  case MODE_KEY:
1145 		  if (!param || *pcount >= MAXMODEPARAMS)
1146 		  {
1147 			  retval = 0;
1148 			  break;
1149 		  }
1150 		  retval = 1;
1151 		  for (x = 0; x < *pcount; x++)
1152 		  {
1153 			  if (pvar[x][1] == 'k')
1154 			  {	/* don't allow user to change key
1155 				 * more than once per command. */
1156 				  retval = 0;
1157 				  break;
1158 			  }
1159 		  }
1160 		  if (retval == 0)	/* you can't break a case from loop */
1161 			  break;
1162 		  if (what == MODE_ADD)
1163 		  {
1164 			  if (!bounce) {	/* don't do the mode at all. */
1165 				  char *tmp;
1166 				  if ((tmp = strchr(param, ' ')))
1167 					*tmp = '\0';
1168 				  if ((tmp = strchr(param, ':')))
1169 					*tmp = '\0';
1170 				  if ((tmp = strchr(param, ',')))
1171 					*tmp = '\0';
1172 				  if (*param == '\0')
1173 					break;
1174 				  if (strlen(param) > KEYLEN)
1175 				    param[KEYLEN] = '\0';
1176 				  if (!strcmp(chptr->mode.key, param))
1177 					break;
1178 				  strncpyzt(chptr->mode.key, param,
1179 				      sizeof(chptr->mode.key));
1180 			  }
1181 			  tmpstr = param;
1182 		  }
1183 		  else
1184 		  {
1185 			  if (!*chptr->mode.key)
1186 				  break;	/* no change */
1187 			  strncpyzt(tmpbuf, chptr->mode.key, sizeof(tmpbuf));
1188 			  tmpstr = tmpbuf;
1189 			  if (!bounce)
1190 				  strcpy(chptr->mode.key, "");
1191 		  }
1192 		  retval = 1;
1193 
1194 		  (void)ircsprintf(pvar[*pcount], "%ck%s",
1195 		      what == MODE_ADD ? '+' : '-', tmpstr);
1196 		  (*pcount)++;
1197 		  break;
1198 
1199 	  case MODE_BAN:
1200 		  if (!param || *pcount >= MAXMODEPARAMS)
1201 		  {
1202 			  retval = 0;
1203 			  break;
1204 		  }
1205 		  retval = 1;
1206 		  tmpstr = clean_ban_mask(param, what, cptr);
1207 		  if (BadPtr(tmpstr))
1208 		      break; /* ignore ban, but eat param */
1209 		  if ((tmpstr[0] == '~') && MyClient(cptr) && !bounce)
1210 		  {
1211 		      /* extban: check access if needed */
1212 		      Extban *p = findmod_by_bantype(tmpstr[1]);
1213 		      if (p && p->is_ok)
1214 		      {
1215 			if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_BAN))
1216 		        {
1217 		            if (IsAnOper(cptr))
1218 		            {
1219 		                /* TODO: send operoverride notice */
1220   		            } else {
1221 		                p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_BAN);
1222 		                break;
1223 		            }
1224 		        }
1225 			if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_PARAM, what, EXBTYPE_BAN))
1226 		            break;
1227 		     }
1228 		  }
1229 		  /* For bounce, we don't really need to worry whether
1230 		   * or not it exists on our server.  We'll just always
1231 		   * bounce it. */
1232 		  if (!bounce &&
1233 		      ((what == MODE_ADD && add_listmode(&chptr->banlist, cptr, chptr, tmpstr))
1234 		      || (what == MODE_DEL && del_listmode(&chptr->banlist, chptr, tmpstr))))
1235 			  break;	/* already exists */
1236 		  (void)ircsprintf(pvar[*pcount], "%cb%s",
1237 		      what == MODE_ADD ? '+' : '-', tmpstr);
1238 		  (*pcount)++;
1239 		  break;
1240 	  case MODE_EXCEPT:
1241 		  if (!param || *pcount >= MAXMODEPARAMS)
1242 		  {
1243 			  retval = 0;
1244 			  break;
1245 		  }
1246 		  retval = 1;
1247 		  tmpstr = clean_ban_mask(param, what, cptr);
1248 		  if (BadPtr(tmpstr))
1249 		     break; /* ignore except, but eat param */
1250 		  if ((tmpstr[0] == '~') && MyClient(cptr) && !bounce)
1251 		  {
1252 		      /* extban: check access if needed */
1253 		      Extban *p = findmod_by_bantype(tmpstr[1]);
1254 		      if (p && p->is_ok)
1255        		      {
1256 			 if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_EXCEPT))
1257 		         {
1258 		            if (IsAnOper(cptr))
1259 		            {
1260 		                /* TODO: send operoverride notice */
1261 		            } else {
1262 		                p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_EXCEPT);
1263 		                break;
1264 		            }
1265 		        }
1266 			if (!p->is_ok(cptr, chptr, tmpstr, EXBCHK_PARAM, what, EXBTYPE_EXCEPT))
1267 		            break;
1268 		     }
1269 		  }
1270 		  /* For bounce, we don't really need to worry whether
1271 		   * or not it exists on our server.  We'll just always
1272 		   * bounce it. */
1273 		  if (!bounce &&
1274 		      ((what == MODE_ADD && add_listmode(&chptr->exlist, cptr, chptr, tmpstr))
1275 		      || (what == MODE_DEL && del_listmode(&chptr->exlist, chptr, tmpstr))))
1276 			  break;	/* already exists */
1277 		  (void)ircsprintf(pvar[*pcount], "%ce%s",
1278 		      what == MODE_ADD ? '+' : '-', tmpstr);
1279 		  (*pcount)++;
1280 		  break;
1281 	  case MODE_INVEX:
1282 		  if (!param || *pcount >= MAXMODEPARAMS)
1283 		  {
1284 			  retval = 0;
1285 			  break;
1286 		  }
1287 		  retval = 1;
1288 		  tmpstr = clean_ban_mask(param, what, cptr);
1289 		  if (BadPtr(tmpstr))
1290 		     break; /* ignore except, but eat param */
1291 		  if ((tmpstr[0] == '~') && MyClient(cptr) && !bounce)
1292 		  {
1293 		      /* extban: check access if needed */
1294 		      Extban *p = findmod_by_bantype(tmpstr[1]);
1295 		      if (p)
1296        		      {
1297        		        if (!(p->options & EXTBOPT_INVEX))
1298 				break; /* this extended ban type does not support INVEX */
1299 
1300 			if (p->is_ok && !p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_EXCEPT))
1301 		        {
1302 		            if (IsAnOper(cptr))
1303 		            {
1304 		                /* TODO: send operoverride notice */
1305 		            } else {
1306 		                p->is_ok(cptr, chptr, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_EXCEPT);
1307 		                break;
1308 		            }
1309 		        }
1310 			if (p->is_ok && !p->is_ok(cptr, chptr, tmpstr, EXBCHK_PARAM, what, EXBTYPE_EXCEPT))
1311 		            break;
1312 		     }
1313 		  }
1314 		  /* For bounce, we don't really need to worry whether
1315 		   * or not it exists on our server.  We'll just always
1316 		   * bounce it. */
1317 		  if (!bounce &&
1318 		      ((what == MODE_ADD && add_listmode(&chptr->invexlist, cptr, chptr, tmpstr))
1319 		      || (what == MODE_DEL && del_listmode(&chptr->invexlist, chptr, tmpstr))))
1320 			  break;	/* already exists */
1321 		  (void)ircsprintf(pvar[*pcount], "%cI%s",
1322 		      what == MODE_ADD ? '+' : '-', tmpstr);
1323 		  (*pcount)++;
1324 		  break;
1325 	  case MODE_LINK:
1326 		  if (IsULine(cptr) || IsServer(cptr))
1327 		  {
1328 			  goto linkok;
1329 		  }
1330 
1331 		  if (MyClient(cptr) && !is_chanowner(cptr, chptr) && !op_can_override(cptr) && !samode_in_progress)
1332 		  {
1333 			sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name,
1334 				   chptr->chname);
1335 			break;
1336 		  }
1337 
1338 		linkok:
1339 		  retval = 1;
1340 		  for (x = 0; x < *pcount; x++)
1341 		  {
1342 			  if (pvar[x][1] == 'L')
1343 			  {	/* don't allow user to change link
1344 				 * more than once per command. */
1345 				  retval = 0;
1346 				  break;
1347 			  }
1348 		  }
1349 		  if (retval == 0)	/* you can't break a case from loop */
1350 			  break;
1351 		  if (what == MODE_ADD)
1352 		  {
1353 		      char *tmp;
1354 			  if (!param || *pcount >= MAXMODEPARAMS)
1355 			  {
1356 				  retval = 0;
1357 				  break;
1358 			  }
1359 			  if (strchr(param, ','))
1360 				  break;
1361 			  if (!IsChannelName(param))
1362 			  {
1363 				  if (MyClient(cptr))
1364 					  sendto_one(cptr,
1365 					      err_str(ERR_NOSUCHCHANNEL),
1366 					      me.name, cptr->name, param);
1367 				  break;
1368 			  }
1369 			  /* Now make it a clean channelname.. This has to be done before all checking
1370 			   * because it could have been changed later to something disallowed (like
1371 			   * self-linking). -- Syzop
1372 			   */
1373 			  strlcpy(tmpbuf, param, CHANNELLEN+1);
1374 			  clean_channelname(tmpbuf);
1375 			  /* don't allow linking to local chans either.. */
1376 			  if ((tmp = strchr(tmpbuf, ':')))
1377 				*tmp = '\0';
1378 
1379 			  if (!stricmp(tmpbuf, chptr->mode.link))
1380 				break;
1381 			  if (!stricmp(tmpbuf, chptr->chname))
1382 			  {
1383 				if (MyClient(cptr))
1384 					sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
1385 						   me.name, cptr->name, 'L',
1386 					    	   "a channel cannot be linked to itself");
1387 				break;
1388 			  }
1389 			  if (!bounce)	/* don't do the mode at all. */
1390 			  {
1391 				  strncpyzt(chptr->mode.link, tmpbuf,
1392 				      sizeof(chptr->mode.link));
1393 			      tmpstr = tmpbuf;
1394 			  } else
1395 			      tmpstr = param; /* Use the original value if bounce?? -- Syzop */
1396 		  }
1397 		  else
1398 		  {
1399 			  if (!*chptr->mode.link)
1400 				  break;	/* no change */
1401 			  strncpyzt(tmpbuf, chptr->mode.link, sizeof(tmpbuf));
1402 			  tmpstr = tmpbuf;
1403 			  if (!bounce)
1404 			  {
1405 				  strcpy(chptr->mode.link, "");
1406 			  }
1407 		  }
1408 		  if (!IsULine(cptr) && IsPerson(cptr) && op_can_override(cptr) && !is_chanowner(cptr, chptr))
1409 		  {
1410 		  	opermode = 1;
1411 		  }
1412 		  retval = 1;
1413 
1414 		  (void)ircsprintf(pvar[*pcount], "%cL%s",
1415 		      what == MODE_ADD ? '+' : '-', tmpstr);
1416 		  (*pcount)++;
1417 		  break;
1418 	  case MODE_FLOODLIMIT:
1419 		  retval = 1;
1420 		  for (x = 0; x < *pcount; x++)
1421 		  {
1422 			  if (pvar[x][1] == 'f')
1423 			  {	/* don't allow user to change flood
1424 				 * more than once per command. */
1425 				  retval = 0;
1426 				  break;
1427 			  }
1428 		  }
1429 		  if (retval == 0)	/* you can't break a case from loop */
1430 			  break;
1431 #ifndef NEWCHFLOODPROT
1432 		  if (what == MODE_ADD)
1433 		  {
1434 			  if (!bounce)	/* don't do the mode at all. */
1435 			  {
1436 				  if (!param || *pcount >= MAXMODEPARAMS)
1437 				  {
1438 					  retval = 0;
1439 					  break;
1440 				  }
1441 
1442 				  /* like 1:1 and if its less than 3 chars then ahem.. */
1443 				  if (strlen(param) < 3)
1444 				  {
1445 					  break;
1446 				  }
1447 				  /* may not contain other chars
1448 				     than 0123456789: & NULL */
1449 				  hascolon = 0;
1450 				  for (xp = param; *xp; xp++)
1451 				  {
1452 					  if (*xp == ':')
1453 						hascolon++;
1454 					  /* fast alpha check */
1455 					  if (((*xp < '0') || (*xp > '9'))
1456 					      && (*xp != ':')
1457 					      && (*xp != '*'))
1458 						goto break_flood;
1459 					  /* uh oh, not the first char */
1460 					  if (*xp == '*' && (xp != param))
1461 						goto break_flood;
1462 				  }
1463 				  /* We can avoid 2 strchr() and a strrchr() like this
1464 				   * it should be much faster. -- codemastr
1465 				   */
1466 				  if (hascolon != 1)
1467 					break;
1468 				  if (*param == '*')
1469 				  {
1470 					  xzi = 1;
1471 					  //                      chptr->mode.kmode = 1;
1472 				  }
1473 				  else
1474 				  {
1475 					  xzi = 0;
1476 
1477 					  //                   chptr->mode.kmode = 0;
1478 				  }
1479 				  xp = index(param, ':');
1480 				  *xp = '\0';
1481 				  xxi =
1482 				      atoi((*param ==
1483 				      '*' ? (param + 1) : param));
1484 				  xp++;
1485 				  xyi = atoi(xp);
1486 				  if (xxi > 500 || xyi > 500)
1487 					break;
1488 				  xp--;
1489 				  *xp = ':';
1490 				  if ((xxi == 0) || (xyi == 0))
1491 					  break;
1492 				  if ((chptr->mode.msgs == xxi)
1493 				      && (chptr->mode.per == xyi)
1494 				      && (chptr->mode.kmode == xzi))
1495 					  break;
1496 				  chptr->mode.msgs = xxi;
1497 				  chptr->mode.per = xyi;
1498 				  chptr->mode.kmode = xzi;
1499 			  }
1500 			  tmpstr = param;
1501 			  retval = 1;
1502 		  }
1503 		  else
1504 		  {
1505 			  if (!chptr->mode.msgs || !chptr->mode.per)
1506 				  break;	/* no change */
1507 			  ircsprintf(tmpbuf,
1508 			      (chptr->mode.kmode > 0 ? "*%i:%i" : "%i:%i"),
1509 			      chptr->mode.msgs, chptr->mode.per);
1510 			  tmpstr = tmpbuf;
1511 			  if (!bounce)
1512 			  {
1513 				  chptr->mode.msgs = chptr->mode.per =
1514 				      chptr->mode.kmode = 0;
1515 			  }
1516 			  retval = 0;
1517 		  }
1518 #else
1519 		/* NEW */
1520 		if (what == MODE_ADD)
1521 		{
1522 			if (!bounce)	/* don't do the mode at all. */
1523 			{
1524 				ChanFloodProt newf;
1525 				memset(&newf, 0, sizeof(newf));
1526 
1527 				if (!param || *pcount >= MAXMODEPARAMS)
1528 				{
1529 					retval = 0;
1530 					break;
1531 				}
1532 
1533 				/* old +f was like +f 10:5 or +f *10:5
1534 				 * new is +f [5c,30j,10t#b]:15
1535 				 * +f 10:5  --> +f [10t]:5
1536 				 * +f *10:5 --> +f [10t#b]:5
1537 				 */
1538 				if (param[0] != '[')
1539 				{
1540 					/* <<OLD +f>> */
1541 				  /* like 1:1 and if its less than 3 chars then ahem.. */
1542 				  if (strlen(param) < 3)
1543 				  {
1544 					  break;
1545 				  }
1546 				  /* may not contain other chars
1547 				     than 0123456789: & NULL */
1548 				  hascolon = 0;
1549 				  for (xp = param; *xp; xp++)
1550 				  {
1551 					  if (*xp == ':')
1552 						hascolon++;
1553 					  /* fast alpha check */
1554 					  if (((*xp < '0') || (*xp > '9'))
1555 					      && (*xp != ':')
1556 					      && (*xp != '*'))
1557 						goto break_flood;
1558 					  /* uh oh, not the first char */
1559 					  if (*xp == '*' && (xp != param))
1560 						goto break_flood;
1561 				  }
1562 				  /* We can avoid 2 strchr() and a strrchr() like this
1563 				   * it should be much faster. -- codemastr
1564 				   */
1565 				  if (hascolon != 1)
1566 					break;
1567 				  if (*param == '*')
1568 				  {
1569 					  xzi = 1;
1570 					  //                      chptr->mode.kmode = 1;
1571 				  }
1572 				  else
1573 				  {
1574 					  xzi = 0;
1575 
1576 					  //                   chptr->mode.kmode = 0;
1577 				  }
1578 				  xp = index(param, ':');
1579 				  *xp = '\0';
1580 				  xxi =
1581 				      atoi((*param ==
1582 				      '*' ? (param + 1) : param));
1583 				  xp++;
1584 				  xyi = atoi(xp);
1585 				  if (xxi > 500 || xyi > 500)
1586 					break;
1587 				  xp--;
1588 				  *xp = ':';
1589 				  if ((xxi == 0) || (xyi == 0))
1590 					  break;
1591 
1592 				  /* ok, we passed */
1593 				  newf.l[FLD_TEXT] = xxi;
1594 				  newf.per = xyi;
1595 				  if (xzi == 1)
1596 				      newf.a[FLD_TEXT] = 'b';
1597 				} else {
1598 					/* NEW +F */
1599 					char xbuf[256], c, a, *p, *p2, *x = xbuf+1;
1600 					int v;
1601 					unsigned short warnings = 0, breakit;
1602 					unsigned char r;
1603 
1604 					/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
1605 					strlcpy(xbuf, param, sizeof(xbuf));
1606 					p2 = strchr(xbuf+1, ']');
1607 					if (!p2)
1608 						break;
1609 					*p2 = '\0';
1610 					if (*(p2+1) != ':')
1611 						break;
1612 					breakit = 0;
1613 					for (x = strtok(xbuf+1, ","); x; x = strtok(NULL, ","))
1614 					{
1615 						/* <number><1 letter>[optional: '#'+1 letter] */
1616 						p = x;
1617 						while(isdigit(*p)) { p++; }
1618 						if ((*p == '\0') ||
1619 						    !((*p == 'c') || (*p == 'j') || (*p == 'k') ||
1620 						      (*p == 'm') || (*p == 'n') || (*p == 't')))
1621 						{
1622 							if (MyClient(cptr) && *p && (warnings++ < 3))
1623 								sendto_one(cptr, ":%s NOTICE %s :warning: channelmode +f: floodtype '%c' unknown, ignored.",
1624 									me.name, cptr->name, *p);
1625 							continue; /* continue instead of break for forward compatability. */
1626 						}
1627 						c = *p;
1628 						*p = '\0';
1629 						v = atoi(x);
1630 						if ((v < 1) || (v > 999)) /* out of range... */
1631 						{
1632 							if (MyClient(cptr))
1633 							{
1634 								sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
1635 									   me.name, cptr->name,
1636 									   'f', "value should be from 1-999");
1637 								breakit = 1;
1638 								break;
1639 							} else
1640 								continue; /* just ignore for remote servers */
1641 						}
1642 						p++;
1643 						a = '\0';
1644 						r = MyClient(cptr) ? MODEF_DEFAULT_UNSETTIME : 0;
1645 						if (*p != '\0')
1646 						{
1647 							if (*p == '#')
1648 							{
1649 								p++;
1650 								a = *p;
1651 								p++;
1652 								if (*p != '\0')
1653 								{
1654 									int tv;
1655 									tv = atoi(p);
1656 									if (tv <= 0)
1657 										tv = 0; /* (ignored) */
1658 									if (tv > (MyClient(cptr) ? MODEF_MAX_UNSETTIME : 255))
1659 										tv = (MyClient(cptr) ? MODEF_MAX_UNSETTIME : 255); /* set to max */
1660 									r = (unsigned char)tv;
1661 								}
1662 							}
1663 						}
1664 
1665 						switch(c)
1666 						{
1667 							case 'c':
1668 								newf.l[FLD_CTCP] = v;
1669 								if ((a == 'm') || (a == 'M'))
1670 									newf.a[FLD_CTCP] = a;
1671 								else
1672 									newf.a[FLD_CTCP] = 'C';
1673 								newf.r[FLD_CTCP] = r;
1674 								break;
1675 							case 'j':
1676 								newf.l[FLD_JOIN] = v;
1677 								if (a == 'R')
1678 									newf.a[FLD_JOIN] = a;
1679 								else
1680 									newf.a[FLD_JOIN] = 'i';
1681 								newf.r[FLD_JOIN] = r;
1682 								break;
1683 							case 'k':
1684 								newf.l[FLD_KNOCK] = v;
1685 								newf.a[FLD_KNOCK] = 'K';
1686 								newf.r[FLD_KNOCK] = r;
1687 								break;
1688 							case 'm':
1689 								newf.l[FLD_MSG] = v;
1690 								if (a == 'M')
1691 									newf.a[FLD_MSG] = a;
1692 								else
1693 									newf.a[FLD_MSG] = 'm';
1694 								newf.r[FLD_MSG] = r;
1695 								break;
1696 							case 'n':
1697 								newf.l[FLD_NICK] = v;
1698 								newf.a[FLD_NICK] = 'N';
1699 								newf.r[FLD_NICK] = r;
1700 								break;
1701 							case 't':
1702 								newf.l[FLD_TEXT] = v;
1703 								if (a == 'b')
1704 									newf.a[FLD_TEXT] = a;
1705 								/** newf.r[FLD_TEXT] ** not supported */
1706 								break;
1707 							default:
1708 								breakit=1;
1709 								break;
1710 						}
1711 						if (breakit)
1712 							break;
1713 					} /* for */
1714 					if (breakit)
1715 						break;
1716 					/* parse 'per' */
1717 					p2++;
1718 					if (*p2 != ':')
1719 						break;
1720 					p2++;
1721 					if (!*p2)
1722 						break;
1723 					v = atoi(p2);
1724 					if ((v < 1) || (v > 999)) /* 'per' out of range */
1725 					{
1726 						if (MyClient(cptr))
1727 							sendto_one(cptr, err_str(ERR_CANNOTCHANGECHANMODE),
1728 								   me.name, cptr->name, 'f',
1729 								   "time range should be 1-999");
1730 						break;
1731 					}
1732 					newf.per = v;
1733 
1734 					/* Is anything turned on? (to stop things like '+f []:15' */
1735 					breakit = 1;
1736 					for (v=0; v < NUMFLD; v++)
1737 						if (newf.l[v])
1738 							breakit=0;
1739 					if (breakit)
1740 						break;
1741 
1742 				} /* if param[0] == '[' */
1743 
1744 				if (chptr->mode.floodprot &&
1745 				    !memcmp(chptr->mode.floodprot, &newf, sizeof(ChanFloodProt)))
1746 					break; /* They are identical */
1747 
1748 				/* Good.. store the mode (and alloc if needed) */
1749 				if (!chptr->mode.floodprot)
1750 					chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt));
1751 				memcpy(chptr->mode.floodprot, &newf, sizeof(ChanFloodProt));
1752 				strcpy(tmpbuf, channel_modef_string(chptr->mode.floodprot));
1753 				tmpstr = tmpbuf;
1754 			} else {
1755 				/* bounce... */
1756 				tmpstr = param;
1757 			}
1758 			retval = 1;
1759 		} else
1760 		{ /* MODE_DEL */
1761 			if (!chptr->mode.floodprot)
1762 				break; /* no change */
1763 			if (!bounce)
1764 			{
1765 				strcpy(tmpbuf, channel_modef_string(chptr->mode.floodprot));
1766 				tmpstr = tmpbuf;
1767 				free(chptr->mode.floodprot);
1768 				chptr->mode.floodprot = NULL;
1769 				chanfloodtimer_stopchantimers(chptr);
1770 			} else {
1771 				/* bounce.. */
1772 				tmpstr = param;
1773 			}
1774 			retval = 1;
1775 		}
1776 #endif
1777 
1778 		  (void)ircsprintf(pvar[*pcount], "%cf%s",
1779 		      what == MODE_ADD ? '+' : '-', tmpstr);
1780 		  (*pcount)++;
1781 		  break_flood:
1782 		  break;
1783 	}
1784 	return retval;
1785 }
1786 
1787 #ifdef EXTCMODE
1788 /** Check access and if granted, set the extended chanmode to the requested value in memory.
1789   * note: if bounce is requested then the mode will not be set.
1790   * @returns amount of params eaten (0 or 1)
1791   */
do_extmode_char(aChannel * chptr,int modeindex,char * param,u_int what,aClient * cptr,u_int * pcount,char pvar[MAXMODEPARAMS][MODEBUFLEN+3],char bounce)1792 int do_extmode_char(aChannel *chptr, int modeindex, char *param, u_int what,
1793                     aClient *cptr, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3],
1794                     char bounce)
1795 {
1796 int paracnt = (what == MODE_ADD) ? Channelmode_Table[modeindex].paracount : 0;
1797 int x;
1798 
1799 	/* Expected a param and it isn't there? */
1800 	if (paracnt && (!param || (*pcount >= MAXMODEPARAMS)))
1801 		return 0;
1802 
1803 	/* Prevent remote users from setting local channel modes */
1804 	if ((Channelmode_Table[modeindex].local) && !MyClient(cptr))
1805 		return paracnt;
1806 
1807 	if (MyClient(cptr))
1808 	{
1809 		x = Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS, what);
1810 		if ((x == EX_ALWAYS_DENY) ||
1811 		    ((x == EX_DENY) && !op_can_override(cptr) && !samode_in_progress))
1812 		{
1813 			Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS_ERR, what);
1814 			return paracnt; /* Denied & error msg sent */
1815 		}
1816 		if (x == EX_DENY)
1817 			opermode = 1; /* override in progress... */
1818 	} else {
1819 		/* remote user: we only need to check if we need to generate an operoverride msg */
1820 		if (!IsULine(cptr) && IsPerson(cptr) && op_can_override(cptr) &&
1821 		    (Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_ACCESS, what) != EX_ALLOW))
1822 			opermode = 1; /* override in progress... */
1823 	}
1824 
1825 	/* Check for multiple changes in 1 command (like +y-y+y 1 2, or +yy 1 2). */
1826 	for (x = 0; x < *pcount; x++)
1827 	{
1828 		if (pvar[x][1] == Channelmode_Table[modeindex].flag)
1829 		{
1830 			/* this is different than the old chanmode system, coz:
1831 			 * "mode #chan +kkL #a #b #c" will get "+kL #a #b" which is wrong :p.
1832 			 * we do eat the parameter. -- Syzop
1833 			 */
1834 			return paracnt;
1835 		}
1836 	}
1837 
1838 	/* w00t... a parameter mode */
1839 	if (Channelmode_Table[modeindex].paracount)
1840 	{
1841 		if (what == MODE_DEL)
1842 		{
1843 			if (!(chptr->mode.extmode & Channelmode_Table[modeindex].mode))
1844 				return paracnt; /* There's nothing to remove! */
1845 			/* del means any parameter is ok, the one-who-is-set will be used */
1846 			ircsprintf(pvar[*pcount], "-%c", Channelmode_Table[modeindex].flag);
1847 		} else {
1848 			/* add: is the parameter ok? */
1849 			if (Channelmode_Table[modeindex].is_ok(cptr, chptr, param, EXCHK_PARAM, what) == FALSE)
1850 				return paracnt;
1851 			/* is it already set at the same value? if so, ignore it. */
1852 			if (chptr->mode.extmode & Channelmode_Table[modeindex].mode)
1853 			{
1854 				char *p, *p2;
1855 				p = Channelmode_Table[modeindex].get_param(extcmode_get_struct(chptr->mode.extmodeparam,Channelmode_Table[modeindex].flag));
1856 				p2 = Channelmode_Table[modeindex].conv_param(param);
1857 				if (p && p2 && !strcmp(p, p2))
1858 					return paracnt; /* ignore... */
1859 			}
1860 				ircsprintf(pvar[*pcount], "+%c%s",
1861 					Channelmode_Table[modeindex].flag, Channelmode_Table[modeindex].conv_param(param));
1862 			(*pcount)++;
1863 		}
1864 	}
1865 
1866 	if (bounce) /* bounce here means: only check access and return return value */
1867 		return paracnt;
1868 
1869 	if (what == MODE_ADD)
1870 	{	/* + */
1871 		chptr->mode.extmode |= Channelmode_Table[modeindex].mode;
1872 		if (Channelmode_Table[modeindex].paracount)
1873 		{
1874 			CmodeParam *p = extcmode_get_struct(chptr->mode.extmodeparam, Channelmode_Table[modeindex].flag);
1875 			CmodeParam *r;
1876 			r = Channelmode_Table[modeindex].put_param(p, param);
1877 			if (r != p)
1878 				AddListItem(r, chptr->mode.extmodeparam);
1879 		}
1880 	} else
1881 	{	/* - */
1882 		chptr->mode.extmode &= ~(Channelmode_Table[modeindex].mode);
1883 		if (Channelmode_Table[modeindex].paracount)
1884 		{
1885 			CmodeParam *p = extcmode_get_struct(chptr->mode.extmodeparam, Channelmode_Table[modeindex].flag);
1886 			if (p)
1887 			{
1888 				DelListItem(p, chptr->mode.extmodeparam);
1889 				Channelmode_Table[modeindex].free_param(p);
1890 			}
1891 		}
1892 	}
1893 	return paracnt;
1894 }
1895 #endif /* EXTCMODE */
1896 
1897 /*
1898  * ListBits(bitvalue, bitlength);
1899  * written by Stskeeps
1900 */
1901 #ifdef DEVELOP
ListBits(long bits,long length)1902 char *ListBits(long bits, long length)
1903 {
1904 	char *bitstr, *p;
1905 	long l, y;
1906 	y = 1;
1907 	bitstr = (char *)MyMalloc(length + 1);
1908 	p = bitstr;
1909 	for (l = 1; l <= length; l++)
1910 	{
1911 		if (bits & y)
1912 			*p = '1';
1913 		else
1914 			*p = '0';
1915 		p++;
1916 		y = y + y;
1917 	}
1918 	*p = '\0';
1919 	return (bitstr);
1920 }
1921 #endif
1922 
1923 
1924 /* set_mode
1925  *	written by binary
1926  */
_set_mode(aChannel * chptr,aClient * cptr,int parc,char * parv[],u_int * pcount,char pvar[MAXMODEPARAMS][MODEBUFLEN+3],int bounce)1927 DLLFUNC void _set_mode(aChannel *chptr, aClient *cptr, int parc, char *parv[], u_int *pcount,
1928 	char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce)
1929 {
1930 	char *curchr;
1931 	u_int what = MODE_ADD;
1932 	long modetype = 0;
1933 	int  paracount = 1;
1934 #ifdef DEVELOP
1935 	char *tmpo = NULL;
1936 #endif
1937 	aCtab *tab = &cFlagTab[0];
1938 	aCtab foundat;
1939 	int  found = 0;
1940 	int  sent_mlock_warning = 0;
1941 	unsigned int htrig = 0;
1942 	long oldm, oldl;
1943 	int checkrestr = 0, warnrestr = 1;
1944 #ifdef EXTCMODE
1945 	int extm = 1000000; /* (default value not used but stops gcc from complaining) */
1946 	Cmode_t oldem;
1947 #endif
1948 	long my_access;
1949 	paracount = 1;
1950 	*pcount = 0;
1951 
1952 	oldm = chptr->mode.mode;
1953 	oldl = chptr->mode.limit;
1954 #ifdef EXTCMODE
1955 	oldem = chptr->mode.extmode;
1956 #endif
1957 	if (RESTRICT_CHANNELMODES && MyClient(cptr) && !IsAnOper(cptr) && !IsServer(cptr)) /* "cache" this */
1958 		checkrestr = 1;
1959 
1960 	/* Set access to the status we have */
1961 	my_access = IsPerson(cptr) ? get_access(cptr, chptr) : 0;
1962 
1963 	for (curchr = parv[0]; *curchr; curchr++)
1964 	{
1965 		switch (*curchr)
1966 		{
1967 		  case '+':
1968 			  what = MODE_ADD;
1969 			  break;
1970 		  case '-':
1971 			  what = MODE_DEL;
1972 			  break;
1973 #ifdef DEVELOP
1974 		  case '^':
1975 			  tmpo = (char *)ListBits(chptr->mode.mode, 64);
1976 			  sendto_one(cptr,
1977 			      ":%s NOTICE %s :*** %s mode is %li (0x%lx) [%s]",
1978 			      me.name, cptr->name, chptr->chname,
1979 			      chptr->mode.mode, chptr->mode.mode, tmpo);
1980 			  MyFree(tmpo);
1981 			  break;
1982 #endif
1983 		  default:
1984 			  if (MyClient(cptr) && chptr->mode_lock && strchr(chptr->mode_lock, *curchr) != NULL)
1985 			  {
1986 				  if (!sent_mlock_warning)
1987 				  {
1988 					  sendto_one(cptr, err_str(ERR_MLOCKRESTRICTED), me.name, cptr->name, chptr->chname, *curchr, chptr->mode_lock);
1989 					  sent_mlock_warning++;
1990 				  }
1991 				  continue;
1992 			  }
1993 			  found = 0;
1994 			  tab = &cFlagTab[0];
1995 			  while ((tab->mode != 0x0) && found == 0)
1996 			  {
1997 				  if (tab->flag == *curchr)
1998 				  {
1999 					  found = 1;
2000 					  foundat = *tab;
2001 				  }
2002 				  tab++;
2003 			  }
2004 			  if (found == 1)
2005 			  {
2006 				  modetype = foundat.mode;
2007 			  } else {
2008 #ifdef EXTCMODE
2009 					/* Maybe in extmodes */
2010 					for (extm=0; extm <= Channelmode_highest; extm++)
2011 					{
2012 						if (Channelmode_Table[extm].flag == *curchr)
2013 						{
2014 							found = 2;
2015 							break;
2016 						}
2017 					}
2018 #endif
2019 			  }
2020 			  if (found == 0) /* Mode char unknown */
2021 			  {
2022 			      /* temporary hack: eat parameters of certain future chanmodes.. */
2023 			      if (*curchr == 'I')
2024 				      paracount++;
2025 				  if ((*curchr == 'j') && (what == MODE_ADD))
2026 					  paracount++;
2027 
2028 				  if (MyClient(cptr))
2029 					  sendto_one(cptr, err_str(ERR_UNKNOWNMODE),
2030 					     me.name, cptr->name, *curchr);
2031 				  break;
2032 			  }
2033 
2034 			  if (checkrestr && strchr(RESTRICT_CHANNELMODES, *curchr))
2035 			  {
2036 				  if (warnrestr)
2037 				  {
2038 					sendto_one(cptr, ":%s %s %s :Setting/removing of channelmode(s) '%s' has been disabled.",
2039 						me.name, IsWebTV(cptr) ? "PRIVMSG" : "NOTICE", cptr->name, RESTRICT_CHANNELMODES);
2040 					warnrestr = 0;
2041 				  }
2042 				  paracount += foundat.parameters;
2043 				  break;
2044 			  }
2045 
2046 #ifndef NO_OPEROVERRIDE
2047 				if (found == 1)
2048 				{
2049                           if ((Halfop_mode(modetype) == FALSE) && opermode == 2 && htrig != 1)
2050                           {
2051                           	/* YUCK! */
2052 				if ((foundat.flag == 'h') && !(parc <= paracount) && parv[paracount] &&
2053 				    (find_person(parv[paracount], NULL) == cptr))
2054 				{
2055 					/* ircop with halfop doing a -h on himself. no warning. */
2056 				} else {
2057 					opermode = 0;
2058 					htrig = 1;
2059 				}
2060                           }
2061 				}
2062 #ifdef EXTCMODE
2063 				else if (found == 2) {
2064 					/* Extended mode: all override stuff is in do_extmode_char which will set
2065 					 * opermode if appropriate. -- Syzop
2066 					 */
2067 				}
2068 #endif /* EXTCMODE */
2069 #endif /* !NO_OPEROVERRIDE */
2070 
2071 			  /* We can afford to send off a param */
2072 			  if (parc <= paracount)
2073 			  	parv[paracount] = NULL;
2074 			  if (parv[paracount] &&
2075 			      strlen(parv[paracount]) >= MODEBUFLEN)
2076 			        parv[paracount][MODEBUFLEN-1] = '\0';
2077 			if (found == 1)
2078 			{
2079 			  paracount +=
2080 			      do_mode_char(chptr, modetype, *curchr,
2081 			      parv[paracount], what, cptr, pcount, pvar,
2082 			      bounce, my_access);
2083 			}
2084 #ifdef EXTCMODE
2085 			else if (found == 2)
2086 			{
2087 				paracount += do_extmode_char(chptr, extm, parv[paracount],
2088 				                             what, cptr, pcount, pvar, bounce);
2089 			}
2090 #endif /* EXTCMODE */
2091 			  break;
2092 		}
2093 	}
2094 
2095 #ifdef EXTCMODE
2096 	make_mode_str(chptr, oldm, oldem, oldl, *pcount, pvar, modebuf, parabuf, bounce);
2097 #else
2098 	make_mode_str(chptr, oldm, oldl, *pcount, pvar, modebuf, parabuf, bounce);
2099 #endif
2100 
2101 #ifndef NO_OPEROVERRIDE
2102         if (htrig == 1)
2103         {
2104                 /* This is horrible. Just horrible. */
2105                 if (!((modebuf[0] == '+' || modebuf[0] == '-') && modebuf[1] == '\0'))
2106                 sendto_snomask(SNO_EYES, "*** OperOverride -- %s (%s@%s) MODE %s %s %s",
2107                       cptr->name, cptr->user->username, cptr->user->realhost,
2108                       chptr->chname, modebuf, parabuf);
2109 
2110 		/* Logging Implementation added by XeRXeS */
2111 		ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s",
2112 			cptr->name, cptr->user->username, cptr->user->realhost,
2113 			chptr->chname, modebuf, parabuf);
2114 
2115                 htrig = 0;
2116                 opermode = 0; /* stop double override notices... but is this ok??? -- Syzop */
2117         }
2118 #endif
2119 
2120 }
2121 
2122 /*
2123  * m_umode() added 15/10/91 By Darren Reed.
2124  * parv[0] - sender
2125  * parv[1] - username to change mode for
2126  * parv[2] - modes to change
2127  */
CMD_FUNC(_m_umode)2128 DLLFUNC CMD_FUNC(_m_umode)
2129 {
2130 	int  i;
2131 	char **p, *m;
2132 	aClient *acptr;
2133 	int  what, setflags, setsnomask = 0;
2134 	/* (small note: keep 'what' as an int. -- Syzop). */
2135 	short rpterror = 0, umode_restrict_err = 0, chk_restrict = 0, modex_err = 0;
2136 
2137 	what = MODE_ADD;
2138 
2139 	if (parc < 2)
2140 	{
2141 		sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
2142 		    me.name, parv[0], "MODE");
2143 		return 0;
2144 	}
2145 
2146 	if (!(acptr = find_person(parv[1], NULL)))
2147 	{
2148 		if (MyConnect(sptr))
2149 			sendto_one(sptr, err_str(ERR_NOSUCHNICK),
2150 			    me.name, parv[0], parv[1]);
2151 		return 0;
2152 	}
2153 	if (acptr != sptr)
2154 		return 0;
2155 
2156 	if (parc < 3)
2157 	{
2158 		sendto_one(sptr, rpl_str(RPL_UMODEIS),
2159 		    me.name, parv[0], get_mode_str(sptr));
2160 		if (sptr->user->snomask)
2161 			sendto_one(sptr, rpl_str(RPL_SNOMASK),
2162 				me.name, parv[0], get_sno_str(sptr));
2163 		return 0;
2164 	}
2165 
2166 	/* find flags already set for user */
2167 	setflags = 0;
2168 
2169 	for (i = 0; i <= Usermode_highest; i++)
2170 		if ((sptr->umodes & Usermode_Table[i].mode))
2171 			setflags |= Usermode_Table[i].mode;
2172 
2173 	if (RESTRICT_USERMODES && MyClient(sptr) && !IsAnOper(sptr) && !IsServer(sptr))
2174 		chk_restrict = 1;
2175 
2176 	if (MyConnect(sptr))
2177 		setsnomask = sptr->user->snomask;
2178 	/*
2179 	 * parse mode change string(s)
2180 	 */
2181 	p = &parv[2];
2182 	for (m = *p; *m; m++)
2183 	{
2184 		if (chk_restrict && strchr(RESTRICT_USERMODES, *m))
2185 		{
2186 			if (!umode_restrict_err)
2187 			{
2188 				sendto_one(sptr, ":%s %s %s :Setting/removing of usermode(s) '%s' has been disabled.",
2189 					me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, RESTRICT_USERMODES);
2190 				umode_restrict_err = 1;
2191 			}
2192 			continue;
2193 		}
2194 		switch (*m)
2195 		{
2196 		  case '+':
2197 			  what = MODE_ADD;
2198 			  break;
2199 		  case '-':
2200 			  what = MODE_DEL;
2201 			  break;
2202 				  /* we may not get these,
2203 			   * but they shouldnt be in default
2204 			   */
2205 		  case ' ':
2206 		  case '\t':
2207 			  break;
2208 		  case 'r':
2209 		  case 't':
2210 			  if (MyClient(sptr))
2211 				  break;
2212 			  /* since we now use chatops define in unrealircd.conf, we have
2213 			   * to disallow it here */
2214 		  case 's':
2215 			  if (what == MODE_DEL) {
2216 				if (parc >= 4 && sptr->user->snomask) {
2217 					set_snomask(sptr, parv[3]);
2218 					if (sptr->user->snomask == 0)
2219 						goto def;
2220 					break;
2221 				}
2222 				else {
2223 					set_snomask(sptr, NULL);
2224 					goto def;
2225 				}
2226 			  }
2227 			  if (what == MODE_ADD) {
2228 				if (parc < 4)
2229 					set_snomask(sptr, IsAnOper(sptr) ? SNO_DEFOPER : SNO_DEFUSER);
2230 				else
2231 					set_snomask(sptr, parv[3]);
2232 				goto def;
2233 			  }
2234 		  case 'o':
2235 		  case 'O':
2236 			  if(sptr->from->flags & FLAGS_QUARANTINE)
2237 			  {
2238 			    sendto_serv_butone(NULL, ":%s KILL %s :%s (Quarantined: no global oper privileges allowed)", me.name, sptr->name, me.name);
2239 			    return exit_client(cptr, sptr, &me, "Quarantined: no global oper privileges allowed");
2240 			  }
2241 			  /* A local user trying to set himself +o/+O is denied here.
2242 			   * A while later (outside this loop) it is handled as well (and +C, +N, etc too)
2243 			   * but we need to take care here too because it might cause problems
2244 			   * since otherwise all IsOper()/IsAnOper() calls cannot be trusted,
2245 			   * that's just asking for bugs! -- Syzop.
2246 			   */
2247 			  if (MyClient(sptr) && (what == MODE_ADD)) /* Someone setting himself +o? Deny it. */
2248 			    break;
2249 			  goto def;
2250 		  case 'x':
2251 			  switch (UHOST_ALLOWED)
2252 			  {
2253 				case UHALLOW_ALWAYS:
2254 					goto def;
2255 				case UHALLOW_NEVER:
2256 					if (MyClient(sptr))
2257 					{
2258 						if (!modex_err) {
2259 							sendto_one(sptr, ":%s %s %s :*** Setting %cx is disabled", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, what == MODE_ADD ? '+' : '-');
2260 							modex_err = 1;
2261 						}
2262 						break;
2263 					}
2264 					goto def;
2265 				case UHALLOW_NOCHANS:
2266 					if (MyClient(sptr) && sptr->user->joined)
2267 					{
2268 						if (!modex_err) {
2269 							sendto_one(sptr, ":%s %s %s :*** Setting %cx can not be done while you are on channels", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, what == MODE_ADD ? '+' : '-');
2270 							modex_err = 1;
2271 						}
2272 						break;
2273 					}
2274 					goto def;
2275 				case UHALLOW_REJOIN:
2276 					/* Handled later */
2277 					goto def;
2278 			  }
2279 			  break;
2280 		  default:
2281 			def:
2282 
2283 			  for (i = 0; i <= Usermode_highest; i++)
2284 			  {
2285 				  if (*m == Usermode_Table[i].flag)
2286 				  {
2287 					  if (Usermode_Table[i].allowed)
2288 						if (!Usermode_Table[i].allowed(sptr,what))
2289 							break;
2290 					  if (what == MODE_ADD)
2291 						  sptr->umodes |= Usermode_Table[i].mode;
2292 					  else
2293 						  sptr->umodes &= ~Usermode_Table[i].mode;
2294 					  break;
2295 				  }
2296 			  	  else if (i == Usermode_highest && MyConnect(sptr) && !rpterror)
2297   			  	  {
2298 				  	sendto_one(sptr,
2299 				      		err_str(ERR_UMODEUNKNOWNFLAG),
2300 				      		me.name, parv[0]);
2301 					  rpterror = 1;
2302 				  }
2303 			  }
2304 			  break;
2305 		} /* switch */
2306 	} /* for */
2307 	/*
2308 	 * stop users making themselves operators too easily
2309 	 */
2310 
2311 	if (!(setflags & UMODE_OPER) && IsOper(sptr) && !IsServer(cptr))
2312 		ClearOper(sptr);
2313 	if (!(setflags & UMODE_LOCOP) && IsLocOp(sptr) && !IsServer(cptr))
2314 		sptr->umodes &= ~UMODE_LOCOP;
2315 	/*
2316 	 *  Let only operators set HelpOp
2317 	 * Helpops get all /quote help <mess> globals -Donwulff
2318 	 */
2319 	if (MyClient(sptr) && IsHelpOp(sptr) && !OPCanHelpOp(sptr))
2320 		ClearHelpOp(sptr);
2321 	/*
2322 	 * Let only operators set FloodF, ClientF; also
2323 	 * remove those flags if they've gone -o/-O.
2324 	 *  FloodF sends notices about possible flooding -Cabal95
2325 	 *  ClientF sends notices about clients connecting or exiting
2326 	 *  Admin is for server admins
2327 	 */
2328 	if (!IsAnOper(sptr) && !IsServer(cptr))
2329 	{
2330 		sptr->umodes &= ~UMODE_WHOIS;
2331 		ClearAdmin(sptr);
2332 		ClearSAdmin(sptr);
2333 		ClearNetAdmin(sptr);
2334 		ClearHideOper(sptr);
2335 		ClearCoAdmin(sptr);
2336 		ClearHelpOp(sptr);
2337 		ClearFailops(sptr);
2338 	}
2339 
2340 	/*
2341 	 * New oper access flags - Only let them set certian usermodes on
2342 	 * themselves IF they have access to set that specific mode in their
2343 	 * O:Line.
2344 	 */
2345 	if (MyClient(sptr)) {
2346 		if (IsAnOper(sptr)) {
2347 			if (IsAdmin(sptr) && !OPIsAdmin(sptr))
2348 				ClearAdmin(sptr);
2349 			if (IsSAdmin(sptr) && !OPIsSAdmin(sptr))
2350 				ClearSAdmin(sptr);
2351 			if (IsNetAdmin(sptr) && !OPIsNetAdmin(sptr))
2352 				ClearNetAdmin(sptr);
2353 			if (IsCoAdmin(sptr) && !OPIsCoAdmin(sptr))
2354 				ClearCoAdmin(sptr);
2355 			if (MyClient(sptr) && (sptr->umodes & UMODE_SECURE)
2356 			    && !IsSecure(sptr))
2357 				sptr->umodes &= ~UMODE_SECURE;
2358 		}
2359 	/*
2360 	   This is to remooove the kix bug.. and to protect some stuffie
2361 	   -techie
2362 	 */
2363 		if (MyClient(sptr))
2364 		{
2365 			if ((sptr->umodes & UMODE_KIX) && (!OPCanUmodeq(sptr) || !IsAnOper(sptr)))
2366 				sptr->umodes &= ~UMODE_KIX;
2367 			if ((sptr->umodes & UMODE_SECURE) && !IsSecure(sptr))
2368 				sptr->umodes &= ~UMODE_SECURE;
2369 			if (!(sptr->umodes & UMODE_SECURE) && IsSecure(sptr))
2370 				sptr->umodes |= UMODE_SECURE;
2371 		}
2372 	}
2373 	/*
2374 	 * For Services Protection...
2375 	 */
2376 	if (!IsServer(cptr) && !IsULine(sptr))
2377 	{
2378 		if (IsServices(sptr))
2379 			ClearServices(sptr);
2380 	}
2381 	if ((setflags & UMODE_HIDE) && !IsHidden(sptr))
2382 		sptr->umodes &= ~UMODE_SETHOST;
2383 
2384 	if (IsHidden(sptr) && !(setflags & UMODE_HIDE))
2385 	{
2386 		if (sptr->user->virthost)
2387 		{
2388 			MyFree(sptr->user->virthost);
2389 			sptr->user->virthost = NULL;
2390 		}
2391 		sptr->user->virthost = strdup(sptr->user->cloakedhost);
2392 		if (!dontspread)
2393 			sendto_serv_butone_token_opt(cptr, OPT_VHP, sptr->name,
2394 				MSG_SETHOST, TOK_SETHOST, "%s", sptr->user->virthost);
2395 		if (UHOST_ALLOWED == UHALLOW_REJOIN)
2396 		{
2397 			DYN_LOCAL(char, did_parts, sptr->user->joined);
2398 			/* LOL, this is ugly ;) */
2399 			sptr->umodes &= ~UMODE_HIDE;
2400 			rejoin_doparts(sptr, did_parts);
2401 			sptr->umodes |= UMODE_HIDE;
2402 			rejoin_dojoinandmode(sptr, did_parts);
2403 			if (MyClient(sptr))
2404 				sptr->since += 7; /* Add fake lag */
2405 			DYN_FREE(did_parts);
2406 		}
2407 	}
2408 
2409 	if (!IsHidden(sptr) && (setflags & UMODE_HIDE))
2410 	{
2411 		if (UHOST_ALLOWED == UHALLOW_REJOIN)
2412 		{
2413 			DYN_LOCAL(char, did_parts, sptr->user->joined);
2414 			/* LOL, this is ugly ;) */
2415 			sptr->umodes |= UMODE_HIDE;
2416 			rejoin_doparts(sptr, did_parts);
2417 			sptr->umodes &= ~UMODE_HIDE;
2418 			rejoin_dojoinandmode(sptr, did_parts);
2419 			if (MyClient(sptr))
2420 				sptr->since += 7; /* Add fake lag */
2421 			DYN_FREE(did_parts);
2422 		}
2423 		if (sptr->user->virthost)
2424 		{
2425 			MyFree(sptr->user->virthost);
2426 			sptr->user->virthost = NULL;
2427 		}
2428 		/* (Re)create the cloaked virthost, because it will be used
2429 		 * for ban-checking... free+recreate here because it could have
2430 		 * been a vhost for example. -- Syzop
2431 		 */
2432 		sptr->user->virthost = strdup(sptr->user->cloakedhost);
2433 	}
2434 	/*
2435 	 * If I understand what this code is doing correctly...
2436 	 *   If the user WAS an operator and has now set themselves -o/-O
2437 	 *   then remove their access, d'oh!
2438 	 * In order to allow opers to do stuff like go +o, +h, -o and
2439 	 * remain +h, I moved this code below those checks. It should be
2440 	 * O.K. The above code just does normal access flag checks. This
2441 	 * only changes the operflag access level.  -Cabal95
2442 	 */
2443 	if ((setflags & (UMODE_OPER | UMODE_LOCOP)) && !IsAnOper(sptr) &&
2444 	    MyConnect(sptr))
2445 	{
2446 #ifndef NO_FDLIST
2447 		delfrom_fdlist(sptr->slot, &oper_fdlist);
2448 #endif
2449 		sptr->oflag = 0;
2450 		remove_oper_snomasks(sptr);
2451 		RunHook2(HOOKTYPE_LOCAL_OPER, sptr, 0);
2452 	}
2453 
2454 	if ((sptr->umodes & UMODE_BOT) && !(setflags & UMODE_BOT) && MyClient(sptr))
2455 	{
2456 		/* now +B */
2457 	  do_cmd(sptr, sptr, "BOTMOTD", 1, parv);
2458 	}
2459 
2460 	if (!(setflags & UMODE_OPER) && IsOper(sptr))
2461 		IRCstats.operators++;
2462 
2463 	/* deal with opercounts and stuff */
2464 	if ((setflags & UMODE_OPER) && !IsOper(sptr))
2465 	{
2466 		IRCstats.operators--;
2467 		VERIFY_OPERCOUNT(sptr, "umode1");
2468 	} else /* YES this 'else' must be here, otherwise we can decrease twice. fixes opercount bug. */
2469 	if (!(setflags & UMODE_HIDEOPER) && IsHideOper(sptr))
2470 	{
2471 		if (IsOper(sptr)) /* decrease, but only if GLOBAL oper */
2472 			IRCstats.operators--;
2473 		VERIFY_OPERCOUNT(sptr, "umode2");
2474 	}
2475 	/* end of dealing with opercounts */
2476 
2477 	if ((setflags & UMODE_HIDEOPER) && !IsHideOper(sptr))
2478 	{
2479 		if (IsOper(sptr)) /* increase, but only if GLOBAL oper */
2480 			IRCstats.operators++;
2481 	}
2482 	if (!(setflags & UMODE_INVISIBLE) && IsInvisible(sptr))
2483 		IRCstats.invisible++;
2484 	if ((setflags & UMODE_INVISIBLE) && !IsInvisible(sptr))
2485 		IRCstats.invisible--;
2486 
2487 	if (MyConnect(sptr) && !IsAnOper(sptr))
2488 		remove_oper_modes(sptr);
2489 
2490 	/*
2491 	 * compare new flags with old flags and send string which
2492 	 * will cause servers to update correctly.
2493 	 */
2494 	if (setflags != sptr->umodes)
2495 		RunHook3(HOOKTYPE_UMODE_CHANGE, sptr, setflags, sptr->umodes);
2496 	if (dontspread == 0)
2497 		send_umode_out(cptr, sptr, setflags);
2498 
2499 	if (MyConnect(sptr) && setsnomask != sptr->user->snomask)
2500 		sendto_one(sptr, rpl_str(RPL_SNOMASK),
2501 			me.name, parv[0], get_sno_str(sptr));
2502 
2503 	return 0;
2504 }
2505 
CMD_FUNC(m_mlock)2506 CMD_FUNC(m_mlock)
2507 {
2508 	aChannel *chptr = NULL;
2509 	TS chants;
2510 
2511 	if ((parc < 3) || BadPtr(parv[2]))
2512 		return 0;
2513 
2514 	if (*parv[1] == '!')
2515 		chants = (TS) base64dec(parv[1] + 1);
2516 	else
2517 		chants = (TS) atol(parv[1]);
2518 
2519 	/* Now, try to find the channel in question */
2520 	chptr = find_channel(parv[2], NullChn);
2521 	if (chptr == NULL)
2522 		return 0;
2523 
2524 	/* Senders' Channel TS is higher, drop it. */
2525 	if (chants > chptr->creationtime)
2526 		return 0;
2527 
2528 	if (IsServer(sptr))
2529 		set_channel_mlock(cptr, sptr, chptr, parv[3], TRUE);
2530 
2531 	return 0;
2532 }
2533