1 /*
2  *   IRC - Internet Relay Chat, src/modules/m_join.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 <time.h>
31 #include <sys/stat.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #ifdef _WIN32
36 #include <io.h>
37 #endif
38 #include <fcntl.h>
39 #include "h.h"
40 #ifdef STRIPBADWORDS
41 #include "badwords.h"
42 #endif
43 #ifdef _WIN32
44 #include "version.h"
45 #endif
46 
47 /* Forward declarations */
48 DLLFUNC CMD_FUNC(m_join);
49 DLLFUNC void _join_channel(aChannel *chptr, aClient *cptr, aClient *sptr, int flags);
50 DLLFUNC CMD_FUNC(_do_join);
51 DLLFUNC int _can_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *key, char *link, char *parv[]);
52 static int extended_operoverride(aClient *sptr, aChannel *chptr, char *key, int mval, char mchar);
53 #define MAXBOUNCE   5 /** Most sensible */
54 #ifdef JOINTHROTTLE
55 static int isjthrottled(aClient *cptr, aChannel *chptr);
56 static void cmodej_increase_usercounter(aClient *cptr, aChannel *chptr);
57 #endif
58 
59 /* Externs */
60 extern MODVAR int spamf_ugly_vchanoverride;
61 extern int find_invex(aChannel *chptr, aClient *sptr);
62 
63 /* Local vars */
64 static int bouncedtimes = 0;
65 
66 #define MSG_JOIN 	"JOIN"
67 #define TOK_JOIN 	"C"
68 
69 ModuleHeader MOD_HEADER(m_join)
70   = {
71 	"m_join",
72 	"$Id$",
73 	"command /join",
74 	"3.2-b8-1",
75 	NULL
76     };
77 
MOD_TEST(m_join)78 DLLFUNC int MOD_TEST(m_join)(ModuleInfo *modinfo)
79 {
80 	MARK_AS_OFFICIAL_MODULE(modinfo);
81 	EfunctionAddVoid(modinfo->handle, EFUNC_JOIN_CHANNEL, _join_channel);
82 	EfunctionAdd(modinfo->handle, EFUNC_DO_JOIN, _do_join);
83 	EfunctionAdd(modinfo->handle, EFUNC_CAN_JOIN, _can_join);
84 	return MOD_SUCCESS;
85 }
86 
MOD_INIT(m_join)87 DLLFUNC int MOD_INIT(m_join)(ModuleInfo *modinfo)
88 {
89 	CommandAdd(modinfo->handle, MSG_JOIN, TOK_JOIN, m_join, MAXPARA, M_USER);
90 	MARK_AS_OFFICIAL_MODULE(modinfo);
91 	return MOD_SUCCESS;
92 }
93 
MOD_LOAD(m_join)94 DLLFUNC int MOD_LOAD(m_join)(int module_load)
95 {
96 	return MOD_SUCCESS;
97 }
98 
MOD_UNLOAD(m_join)99 DLLFUNC int MOD_UNLOAD(m_join)(int module_unload)
100 {
101 	return MOD_SUCCESS;
102 }
103 
104 /* This function adds as an extra (weird) operoverride.
105  * Currently it's only used if you try to operoverride for a +z channel,
106  * if you then do '/join #chan override' it will put the channel -z and allow you directly in.
107  * This is to avoid attackers from using 'race conditions' to prevent you from joining.
108  * PARAMETERS: sptr = the client, chptr = the channel, mval = mode value (eg MODE_ONLYSECURE),
109  *             mchar = mode char (eg 'z')
110  * RETURNS: 1 if operoverride, 0 if not.
111  */
extended_operoverride(aClient * sptr,aChannel * chptr,char * key,int mval,char mchar)112 int extended_operoverride(aClient *sptr, aChannel *chptr, char *key, int mval, char mchar)
113 {
114 unsigned char invited = 0;
115 Link *lp;
116 
117 	if (!IsAnOper(sptr) || !OPCanOverride(sptr))
118 		return 0;
119 
120 	for (lp = sptr->user->invited; lp; lp = lp->next)
121 		if (lp->value.chptr == chptr)
122 		{
123 			invited = 1;
124 			break;
125 		}
126 	if (invited)
127 	{
128 		if (key && !strcasecmp(key, "override"))
129 		{
130 			sendto_channelprefix_butone(NULL, &me, chptr, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER,
131 				":%s NOTICE @%s :setting channel -%c due to OperOverride request from %s",
132 				me.name, chptr->chname, mchar, sptr->name);
133 			sendto_serv_butone(&me, ":%s MODE %s -%c 0", me.name, chptr->chname, mchar);
134 			sendto_channel_butserv(chptr, &me, ":%s MODE %s -%c", me.name, chptr->chname, mchar);
135 			chptr->mode.mode &= ~mval;
136 			return 1;
137 		}
138 	}
139 	return 0;
140 }
141 
142 
143 /* Now let _invited_ people join thru bans, +i and +l.
144  * Checking if an invite exist could be done only if a block exists,
145  * but I'm not too fancy of the complicated structure that'd cause,
146  * when optimization will hopefully take care of it. Most of the time
147  * a user won't have invites on him anyway. -Donwulff
148  */
149 
_can_join(aClient * cptr,aClient * sptr,aChannel * chptr,char * key,char * link,char * parv[])150 DLLFUNC int _can_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *key, char *link, char *parv[])
151 {
152 Link *lp;
153 Ban *banned;
154 
155 	if ((chptr->mode.mode & MODE_ONLYSECURE) && !(sptr->umodes & UMODE_SECURE))
156 	{
157 		if (IsAnOper(sptr))
158 		{
159 			/* Yeah yeah.. duplicate code..
160 			 * Anyway: if the channel is +z we still allow an ircop to bypass it
161 			 * if they are invited.
162 			 */
163 			for (lp = sptr->user->invited; lp; lp = lp->next)
164 				if (lp->value.chptr == chptr)
165 					return 0;
166 		}
167 		return (ERR_SECUREONLYCHAN);
168 	}
169 
170 	if ((chptr->mode.mode & MODE_OPERONLY) && !IsAnOper(sptr))
171 		return (ERR_OPERONLY);
172 
173 	if ((chptr->mode.mode & MODE_ADMONLY) && !IsSkoAdmin(sptr))
174 		return (ERR_ADMONLY);
175 
176 	/* Admin, Coadmin, Netadmin, and SAdmin can still walk +b in +O */
177 	banned = is_banned(sptr, chptr, BANCHK_JOIN);
178 	if (banned && (chptr->mode.mode & MODE_OPERONLY) &&
179 	    IsAnOper(sptr) && !IsSkoAdmin(sptr) && !IsCoAdmin(sptr))
180 		return (ERR_BANNEDFROMCHAN);
181 
182 	/* Only NetAdmin/SAdmin can walk +b in +A */
183 	if (banned && (chptr->mode.mode & MODE_ADMONLY) &&
184 	    IsAnOper(sptr) && !IsNetAdmin(sptr) && !IsSAdmin(sptr))
185 		return (ERR_BANNEDFROMCHAN);
186 
187 	for (lp = sptr->user->invited; lp; lp = lp->next)
188 		if (lp->value.chptr == chptr)
189 			return 0;
190 
191         if ((chptr->mode.limit && chptr->users >= chptr->mode.limit))
192         {
193                 if (chptr->mode.link)
194                 {
195                         if (*chptr->mode.link != '\0')
196                         {
197                                 /* We are linked. */
198                                 sendto_one(sptr,
199                                     err_str(ERR_LINKCHANNEL), me.name,
200                                     sptr->name, chptr->chname,
201                                     chptr->mode.link);
202                                 parv[0] = sptr->name;
203                                 parv[1] = (chptr->mode.link);
204                                 do_join(cptr, sptr, 2, parv);
205                                 return -1;
206                         }
207                 }
208                 /* We check this later return (ERR_CHANNELISFULL); */
209         }
210 
211         if ((chptr->mode.mode & MODE_RGSTRONLY) && !IsLoggedIn(sptr))
212                 return (ERR_NEEDREGGEDNICK);
213 
214         if (*chptr->mode.key && (BadPtr(key) || strcmp(chptr->mode.key, key)))
215                 return (ERR_BADCHANNELKEY);
216 
217         if ((chptr->mode.mode & MODE_INVITEONLY) && !find_invex(chptr, sptr))
218                 return (ERR_INVITEONLYCHAN);
219 
220         if ((chptr->mode.limit && chptr->users >= chptr->mode.limit))
221                 return (ERR_CHANNELISFULL);
222 
223         if (banned)
224                 return (ERR_BANNEDFROMCHAN);
225 
226 #ifndef NO_OPEROVERRIDE
227 #ifdef OPEROVERRIDE_VERIFY
228         if (IsOper(sptr) && (chptr->mode.mode & MODE_SECRET ||
229             chptr->mode.mode & MODE_PRIVATE) && !is_autojoin_chan(chptr->chname))
230                 return (ERR_OPERSPVERIFY);
231 #endif
232 #endif
233 
234 #ifdef JOINTHROTTLE
235 		if (!IsAnOper(cptr) &&
236 		    (chptr->mode.extmode & EXTMODE_JOINTHROTTLE) && isjthrottled(cptr, chptr))
237 			return ERR_TOOMANYJOINS;
238 #endif
239 
240         return 0;
241 }
242 
243 #ifdef JOINTHROTTLE
isjthrottled(aClient * cptr,aChannel * chptr)244 static int isjthrottled(aClient *cptr, aChannel *chptr)
245 {
246 CmodeParam *m;
247 aJFlood *e;
248 int num=0, t=0;
249 
250 	if (!MyClient(cptr))
251 		return 0;
252 
253 	for (m = chptr->mode.extmodeparam; m; m=m->next)
254 		if (m->flag == 'j')
255 		{
256 			num = ((aModejEntry *)m)->num;
257 			t = ((aModejEntry *)m)->t;
258 			break;
259 		}
260 
261 	if (!num || !t)
262 		return 0;
263 
264 	/* Grab user<->chan entry.. */
265 	for (e = cptr->user->jflood; e; e=e->next_u)
266 		if (e->chptr == chptr)
267 			break;
268 
269 	if (!e)
270 		return 0; /* Not present, so cannot be throttled */
271 
272 	/* Ok... now the actual check:
273 	 * if ([timer valid] && [one more join would exceed num])
274 	 */
275 	if (((TStime() - e->firstjoin) < t) && (e->numjoins == num))
276 		return 1; /* Throttled */
277 
278 	return 0;
279 }
280 
cmodej_increase_usercounter(aClient * cptr,aChannel * chptr)281 static void cmodej_increase_usercounter(aClient *cptr, aChannel *chptr)
282 {
283 CmodeParam *m;
284 aJFlood *e;
285 int num=0, t=0;
286 
287 	if (!MyClient(cptr))
288 		return;
289 
290 	for (m = chptr->mode.extmodeparam; m; m=m->next)
291 		if (m->flag == 'j')
292 		{
293 			num = ((aModejEntry *)m)->num;
294 			t = ((aModejEntry *)m)->t;
295 			break;
296 		}
297 
298 	if (!num || !t)
299 		return;
300 
301 	/* Grab user<->chan entry.. */
302 	for (e = cptr->user->jflood; e; e=e->next_u)
303 		if (e->chptr == chptr)
304 			break;
305 
306 	if (!e)
307 	{
308 		/* Allocate one */
309 		e = cmodej_addentry(cptr, chptr);
310 		e->firstjoin = TStime();
311 		e->numjoins = 1;
312 	} else
313 	if ((TStime() - e->firstjoin) < t) /* still valid? */
314 	{
315 		e->numjoins++;
316 	} else {
317 		/* reset :p */
318 		e->firstjoin = TStime();
319 		e->numjoins = 1;
320 	}
321 }
322 
323 #endif
324 
325 /*
326 ** m_join
327 **	parv[0] = sender prefix
328 **	parv[1] = channel
329 **	parv[2] = channel password (key)
330 */
CMD_FUNC(m_join)331 DLLFUNC CMD_FUNC(m_join)
332 {
333 int r;
334 
335 	if (bouncedtimes)
336 		sendto_realops("m_join: bouncedtimes=%d??? [please report at http://bugs.unrealircd.org/]", bouncedtimes);
337 	bouncedtimes = 0;
338 	if (IsServer(sptr))
339 		return 0;
340 	r = do_join(cptr, sptr, parc, parv);
341 	bouncedtimes = 0;
342 	return r;
343 }
344 
345 /* Routine that actually makes a user join the channel
346  * this does no actual checking (banned, etc.) it just adds the user
347  */
_join_channel(aChannel * chptr,aClient * cptr,aClient * sptr,int flags)348 DLLFUNC void _join_channel(aChannel *chptr, aClient *cptr, aClient *sptr, int flags)
349 {
350 	char *parv[] = { 0, 0 };
351 	/*
352 	   **  Complete user entry to the new channel (if any)
353 	 */
354 	add_user_to_channel(chptr, sptr, flags);
355 	/*
356 	   ** notify all other users on the new channel
357 	 */
358 	if (chptr->mode.mode & MODE_AUDITORIUM)
359 	{
360 		if (MyClient(sptr))
361 			sendto_one(sptr, ":%s!%s@%s JOIN :%s",
362 			    sptr->name, sptr->user->username,
363 			    GetHost(sptr), chptr->chname);
364 		sendto_chanops_butone(NULL, chptr, ":%s!%s@%s JOIN :%s",
365 		    sptr->name, sptr->user->username,
366 		    GetHost(sptr), chptr->chname);
367 	}
368 	else
369 		sendto_channel_butserv(chptr, sptr,
370 		    ":%s JOIN :%s", sptr->name, chptr->chname);
371 
372 	sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, sptr->name, MSG_JOIN,
373 		    TOK_JOIN, "%s", chptr->chname);
374 
375 #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
376 	if ((MyClient(sptr) && !(flags & CHFL_CHANOP)) || !MyClient(sptr))
377 		sendto_serv_butone_token_opt(cptr, OPT_SJ3, sptr->name, MSG_JOIN,
378 		    TOK_JOIN, "%s", chptr->chname);
379 	if (flags && !(flags & CHFL_DEOPPED))
380 	{
381 #endif
382 		/* I _know_ that the "@%s " look a bit wierd
383 		   with the space and all .. but its to get around
384 		   a SJOIN bug --stskeeps */
385 		sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_SJB64,
386 			me.name, MSG_SJOIN, TOK_SJOIN,
387 			"%B %s :%s%s ", (long)chptr->creationtime,
388 			chptr->chname, chfl_to_sjoin_symbol(flags), sptr->name);
389 		sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_NOT_SJB64,
390 			me.name, MSG_SJOIN, TOK_SJOIN,
391 			"%li %s :%s%s ", chptr->creationtime,
392 			chptr->chname, chfl_to_sjoin_symbol(flags), sptr->name);
393 #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
394 	}
395 #endif
396 
397 	if (MyClient(sptr))
398 	{
399 		/*
400 		   ** Make a (temporal) creationtime, if someone joins
401 		   ** during a net.reconnect : between remote join and
402 		   ** the mode with TS. --Run
403 		 */
404 		if (chptr->creationtime == 0)
405 		{
406 			chptr->creationtime = TStime();
407 			sendto_serv_butone_token(cptr, me.name,
408 			    MSG_MODE, TOK_MODE, "%s + %lu",
409 			    chptr->chname, chptr->creationtime);
410 		}
411 		del_invite(sptr, chptr);
412 		if (flags && !(flags & CHFL_DEOPPED))
413 		{
414 #ifndef PREFIX_AQ
415 			if ((flags & CHFL_CHANOWNER) || (flags & CHFL_CHANPROT))
416 			{
417 				/* +ao / +qo for when PREFIX_AQ is off */
418 				sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3,
419 				    me.name,
420 				    MSG_MODE, TOK_MODE, "%s +o%c %s %s %lu",
421 				    chptr->chname, chfl_to_chanmode(flags), sptr->name, sptr->name,
422 				    chptr->creationtime);
423 			} else {
424 #endif
425 				/* +v/+h/+o (and +a/+q if PREFIX_AQ is on) */
426 				sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3,
427 				    me.name,
428 				    MSG_MODE, TOK_MODE, "%s +%c %s %lu",
429 				    chptr->chname, chfl_to_chanmode(flags), sptr->name,
430 				    chptr->creationtime);
431 #ifndef PREFIX_AQ
432 			}
433 #endif
434 		}
435 		if (chptr->topic)
436 		{
437 			sendto_one(sptr, rpl_str(RPL_TOPIC),
438 			    me.name, sptr->name, chptr->chname, chptr->topic);
439 			sendto_one(sptr,
440 			    rpl_str(RPL_TOPICWHOTIME), me.name,
441 			    sptr->name, chptr->chname, chptr->topic_nick,
442 			    chptr->topic_time);
443 		}
444 		if (chptr->users == 1 && (MODES_ON_JOIN
445 #ifdef EXTCMODE
446 		    || iConf.modes_on_join.extmodes)
447 #endif
448 		)
449 		{
450 #ifdef EXTCMODE
451 			int i;
452 			chptr->mode.extmode =  iConf.modes_on_join.extmodes;
453 			/* Param fun */
454 			for (i = 0; i <= Channelmode_highest; i++)
455 			{
456 				if (!Channelmode_Table[i].flag || !Channelmode_Table[i].paracount)
457 					continue;
458 				if (chptr->mode.extmode & Channelmode_Table[i].mode)
459 				{
460 					CmodeParam *p;
461 					p = Channelmode_Table[i].put_param(NULL, iConf.modes_on_join.extparams[i]);
462 					AddListItem(p, chptr->mode.extmodeparam);
463 				}
464 			}
465 #endif
466 			chptr->mode.mode = MODES_ON_JOIN;
467 #ifdef NEWCHFLOODPROT
468 			if (iConf.modes_on_join.floodprot.per)
469 			{
470 				chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt));
471 				memcpy(chptr->mode.floodprot, &iConf.modes_on_join.floodprot, sizeof(ChanFloodProt));
472 			}
473 #else
474 			chptr->mode.kmode = iConf.modes_on_join.kmode;
475 			chptr->mode.per = iConf.modes_on_join.per;
476 			chptr->mode.msgs = iConf.modes_on_join.msgs;
477 #endif
478 			*modebuf = *parabuf = 0;
479 			channel_modes(sptr, modebuf, parabuf, chptr);
480 			/* This should probably be in the SJOIN stuff */
481 			sendto_serv_butone_token(&me, me.name, MSG_MODE, TOK_MODE,
482 				"%s %s %s %lu", chptr->chname, modebuf, parabuf,
483 				chptr->creationtime);
484 			sendto_one(sptr, ":%s MODE %s %s %s", me.name, chptr->chname, modebuf, parabuf);
485 		}
486 		parv[0] = sptr->name;
487 		parv[1] = chptr->chname;
488 		do_cmd(cptr, sptr, "NAMES", 2, parv);
489 		RunHook4(HOOKTYPE_LOCAL_JOIN, cptr, sptr,chptr,parv);
490 	} else {
491 		RunHook4(HOOKTYPE_REMOTE_JOIN, cptr, sptr, chptr, parv); /* (rarely used) */
492 	}
493 
494 #ifdef NEWCHFLOODPROT
495 	/* I'll explain this only once:
496 	 * 1. if channel is +f
497 	 * 2. local client OR synced server
498 	 * 3. then, increase floodcounter
499 	 * 4. if we reached the limit AND only if source was a local client.. do the action (+i).
500 	 * Nr 4 is done because otherwise you would have a noticeflood with 'joinflood detected'
501 	 * from all servers.
502 	 */
503 	if (chptr->mode.floodprot && (MyClient(sptr) || sptr->srvptr->serv->flags.synced) &&
504 	    !IsULine(sptr) && do_chanflood(chptr->mode.floodprot, FLD_JOIN) && MyClient(sptr))
505 	{
506 		do_chanflood_action(chptr, FLD_JOIN, "join");
507 	}
508 #endif
509 }
510 
511 /** User request to join a channel.
512  * This routine can be called from both m_join or via do_join->can_join->do_join
513  * if the channel is 'linked' (chmode +L). We use a counter 'bouncedtimes' which
514  * is set to 0 in m_join, increased every time we enter this loop and decreased
515  * anytime we leave the loop. So be carefull ;p.
516  */
CMD_FUNC(_do_join)517 DLLFUNC CMD_FUNC(_do_join)
518 {
519 	char jbuf[BUFSIZE];
520 	Membership *lp;
521 	aChannel *chptr;
522 	char *name, *key = NULL, *link = NULL;
523 	int  i, flags = 0;
524 	char *p = NULL, *p2 = NULL;
525 
526 #define RET(x) { bouncedtimes--; return x; }
527 
528 	if (parc < 2 || *parv[1] == '\0')
529 	{
530 		sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
531 		    me.name, parv[0], "JOIN");
532 		return 0;
533 	}
534 	bouncedtimes++;
535 	/* don't use 'return x;' but 'RET(x)' from here ;p */
536 
537 	if (bouncedtimes > MAXBOUNCE)
538 	{
539 		/* bounced too many times */
540 		sendto_one(sptr,
541 		    ":%s %s %s :*** Couldn't join %s ! - Link setting was too bouncy",
542 		    me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, parv[1]);
543 		RET(0)
544 	}
545 
546 	*jbuf = '\0';
547 	/*
548 	   ** Rebuild list of channels joined to be the actual result of the
549 	   ** JOIN.  Note that "JOIN 0" is the destructive problem.
550 	 */
551 	for (i = 0, name = strtoken(&p, parv[1], ","); name;
552 	    name = strtoken(&p, NULL, ","))
553 	{
554 		/* pathological case only on longest channel name.
555 		   ** If not dealt with here, causes desynced channel ops
556 		   ** since ChannelExists() doesn't see the same channel
557 		   ** as one being joined. cute bug. Oct 11 1997, Dianora/comstud
558 		   ** Copied from Dianora's "hybrid 5" ircd.
559 		 */
560 
561 		if (strlen(name) > CHANNELLEN)	/* same thing is done in get_channel() */
562 			name[CHANNELLEN] = '\0';
563 
564 		if (MyConnect(sptr))
565 			clean_channelname(name);
566 		if (check_channelmask(sptr, cptr, name) == -1)
567 			continue;
568 		if (*name == '0' && !atoi(name))
569 		{
570 			(void)strcpy(jbuf, "0");
571 			i = 1;
572 			continue;
573 		}
574 		else if (!IsChannelName(name))
575 		{
576 			if (MyClient(sptr))
577 				sendto_one(sptr,
578 				    err_str(ERR_NOSUCHCHANNEL), me.name,
579 				    parv[0], name);
580 			continue;
581 		}
582 		if (*jbuf)
583 			(void)strlcat(jbuf, ",", sizeof jbuf);
584 		(void)strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1);
585 		i += strlen(name) + 1;
586 	}
587 	/* This strcpy should be safe since jbuf contains the "filtered"
588 	 * result of parv[1] which should never be larger than the source.
589 	 */
590 	(void)strcpy(parv[1], jbuf);
591 
592 	p = NULL;
593 	if (parv[2])
594 		key = strtoken(&p2, parv[2], ",");
595 	parv[2] = NULL;		/* for m_names call later, parv[parc] must == NULL */
596 	for (name = strtoken(&p, jbuf, ","); name;
597 	    key = (key) ? strtoken(&p2, NULL, ",") : NULL,
598 	    name = strtoken(&p, NULL, ","))
599 	{
600 		/*
601 		   ** JOIN 0 sends out a part for all channels a user
602 		   ** has joined.
603 		 */
604 		if (*name == '0' && !atoi(name))
605 		{
606 			while ((lp = sptr->user->channel))
607 			{
608 				chptr = lp->chptr;
609 				sendto_channel_butserv(chptr, sptr,
610 				    PARTFMT2, parv[0], chptr->chname,
611 				    "Left all channels");
612 				if (MyConnect(sptr))
613 					RunHook4(HOOKTYPE_LOCAL_PART, cptr, sptr, chptr, "Left all channels");
614 				remove_user_from_channel(sptr, chptr);
615 			}
616 			sendto_serv_butone_token(cptr, parv[0],
617 			    MSG_JOIN, TOK_JOIN, "0");
618 			continue;
619 		}
620 
621 		if (MyConnect(sptr))
622 		{
623 			/*
624 			   ** local client is first to enter previously nonexistant
625 			   ** channel so make them (rightfully) the Channel
626 			   ** Operator.
627 			 */
628 			/* Where did this come from? Potvin ? --Stskeeps
629 			   flags = (ChannelExists(name)) ? CHFL_DEOPPED :
630 			   CHFL_CHANOWNER;
631 
632 			 */
633 
634 			flags =
635 			    (ChannelExists(name)) ? CHFL_DEOPPED : LEVEL_ON_JOIN;
636 
637 			if (!IsAnOper(sptr))	/* opers can join unlimited chans */
638 				if (sptr->user->joined >= MAXCHANNELSPERUSER)
639 				{
640 					sendto_one(sptr,
641 					    err_str
642 					    (ERR_TOOMANYCHANNELS),
643 					    me.name, parv[0], name);
644 					RET(0)
645 				}
646 /* RESTRICTCHAN */
647 			if (conf_deny_channel)
648 			{
649 				if (!IsOper(sptr) && !IsULine(sptr))
650 				{
651 					ConfigItem_deny_channel *d;
652 					if ((d = Find_channel_allowed(cptr, name)))
653 					{
654 						if (d->warn)
655 						{
656 							sendto_snomask(SNO_EYES, "*** %s tried to join forbidden channel %s",
657 								get_client_name(sptr, 1), name);
658 						}
659 						if (d->reason)
660 							sendto_one(sptr,
661 							":%s %s %s :*** Can not join %s: %s",
662 							me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, name, d->reason);
663 						if (d->redirect)
664 						{
665 							sendto_one(sptr,
666 							":%s %s %s :*** Redirecting you to %s",
667 							me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, d->redirect);
668 							parv[0] = sptr->name;
669 							parv[1] = d->redirect;
670 							do_join(cptr, sptr, 2, parv);
671 						}
672 						if (d->class) {
673 							sendto_one(sptr,
674 							":%s %s %s :*** Can not join %s: Your class is not allowed",
675 							me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, name);
676 						}
677 						continue;
678 					}
679 				}
680 			}
681 			/* ugly set::spamfilter::virus-help-channel-deny hack.. */
682 			if (SPAMFILTER_VIRUSCHANDENY && SPAMFILTER_VIRUSCHAN &&
683 			    !strcasecmp(name, SPAMFILTER_VIRUSCHAN) &&
684 			    !IsAnOper(sptr) && !spamf_ugly_vchanoverride)
685 			{
686 				int invited = 0;
687 				Link *lp;
688 				aChannel *chptr = find_channel(name, NULL);
689 
690 				if (chptr)
691 				{
692 					for (lp = sptr->user->invited; lp; lp = lp->next)
693 						if (lp->value.chptr == chptr)
694 							invited = 1;
695 				}
696 				if (!invited)
697 				{
698 					sendnotice(sptr, "*** Cannot join '%s' because it's the virus-help-channel which is "
699 					                 "reserved for infected users only", name);
700 					continue;
701 				}
702 			}
703 		}
704 
705 		chptr = get_channel(sptr, name, CREATE);
706 		if (chptr && (lp = find_membership_link(sptr->user->channel, chptr)))
707 			continue;
708 
709 		if (!chptr)
710 			continue;
711 
712 		i = HOOK_CONTINUE;
713 		if (!MyConnect(sptr))
714 			flags = CHFL_DEOPPED;
715 		else
716 		{
717 			Hook *h;
718 			for (h = Hooks[HOOKTYPE_PRE_LOCAL_JOIN]; h; h = h->next)
719 			{
720 				i = (*(h->func.intfunc))(sptr,chptr,parv);
721 				if (i == HOOK_DENY || i == HOOK_ALLOW)
722 					break;
723 			}
724 			/* Denied, get out now! */
725 			if (i == HOOK_DENY)
726 			{
727 				/* Rejected... if we just created a new chan we should destroy it too. -- Syzop */
728 				if (!chptr->users)
729 					sub1_from_channel(chptr);
730 				continue;
731 			}
732 			/* If they are allowed, don't check can_join */
733 			if (i != HOOK_ALLOW &&
734 			   (i = can_join(cptr, sptr, chptr, key, link, parv)))
735 			{
736 				if (i != -1)
737 					sendto_one(sptr, err_str(i),
738 					    me.name, parv[0], name);
739 				continue;
740 			}
741 #ifdef JOINTHROTTLE
742 			cmodej_increase_usercounter(cptr, chptr);
743 #endif
744 		}
745 
746 		join_channel(chptr, cptr, sptr, flags);
747 	}
748 	RET(0)
749 #undef RET
750 }
751