2 * IRC - Internet Relay Chat, ircd/m_join.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * $Id: m_join.c 1906 2009-02-09 03:39:42Z entrope $
34 #include "ircd_chattr.h"
35 #include "ircd_features.h"
37 #include "ircd_reply.h"
38 #include "ircd_string.h"
49 /* #include <assert.h> -- Now using assert in ircd_log.h */
53 /** Searches for and handles a 0 in a join list.
54 * @param[in] cptr Client that sent us the message.
55 * @param[in] sptr Original source of message.
56 * @param[in] chanlist List of channels to join.
57 * @return First token in \a chanlist after the final 0 entry, which
58 * may be its nul terminator (if the final entry is a 0 entry).
61 last0(struct Client *cptr, struct Client *sptr, char *chanlist)
66 for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */
67 if (p[0] == '0' && (p[1] == ',' || p[1] == '\0')) {
73 while (p[0] != ',' && p[0] != '\0') /* skip past channel name */
76 if (!p[0]) /* hit the end */
82 struct Membership *member;
84 joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL,
85 "Left all channels", 0);
87 joinbuf_join(&part, 0, 0);
89 while ((member = cli_user(sptr)->channel))
90 joinbuf_join(&part, member->channel,
91 IsZombie(member) ? CHFL_ZOMBIE :
92 IsDelayedJoin(member) ? CHFL_DELAYED :
101 /** Handle a JOIN message from a client connection.
102 * See @ref m_functions for discussion of the arguments.
103 * @param[in] cptr Client that sent us the message.
104 * @param[in] sptr Original source of message.
105 * @param[in] parc Number of arguments.
106 * @param[in] parv Argument vector.
108 int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
110 struct Channel *chptr,*chptrb;
112 struct JoinBuf create;
114 struct Membership *member;
119 unsigned int maxchans;
120 const struct User* user = cli_user(sptr);
122 if (parc < 2 || *parv[1] == '\0')
123 return need_more_params(sptr, "JOIN");
125 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
126 joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime());
128 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
130 keys = parv[2]; /* remember where keys are */
132 for (name = ircd_strtok(&p, chanlist, ","); name;
133 name = ircd_strtok(&p, 0, ",")) {
136 /* If we have any more keys, take the first for this channel. */
138 && (keys = strchr(key = keys, ',')))
141 /* Empty keys are the same as no keys. */
145 if (!IsChannelName(name) || !strIsIrcCh(name))
147 /* bad channel name */
148 send_reply(sptr, ERR_NOSUCHCHANNEL, name);
152 maxchans = cli_confs(sptr)->value.aconf ? ConfMaxChannels(cli_confs(sptr)->value.aconf) : feature_int(FEAT_MAXCHANNELSPERUSER);
153 if(sptr->maxchans > 0)
154 maxchans = sptr->maxchans;
155 if (cli_user(sptr)->joined >= maxchans
156 && !HasPriv(sptr, PRIV_CHAN_LIMIT)) {
157 send_reply(sptr, ERR_TOOMANYCHANNELS, name);
158 break; /* no point processing the other channels */
163 /* BADCHANed channel */
164 if ((gline = gline_find(name, GLINE_BADCHAN)) &&
165 GlineIsActive(gline) && !IsAnOper(sptr)) {
166 send_reply(sptr, ERR_BADCHANNAME, name, GlineReason(gline));
170 if (!(chptr = FindChannel(name))) {
171 if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS))
172 || strlen(name) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) {
173 send_reply(sptr, ERR_NOSUCHCHANNEL, name);
177 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
180 /* Try to add the new channel as a recent target for the user. */
181 if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
183 destruct_channel(chptr);
187 joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER);
188 } else if (member = find_member_link(chptr, sptr)) {
189 if(IsDelayedJoin(member) && IsInvisibleJoin(member)) {
190 ClearInvisibleJoin(member);
191 RevealDelayedJoinIfNeeded(sptr, chptr);
194 } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
197 flags = CHFL_DEOPPED;
201 /* Check Apass/Upass -- since we only ever look at a single
202 * "key" per channel now, this hampers brute force attacks. */
203 if (key && !strcmp(key, chptr->mode.apass))
204 flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
205 else if (key && !strcmp(key, chptr->mode.upass))
207 else if (chptr->users == 0 && !chptr->mode.apass[0] && !(chptr->mode.mode & MODE_PERSIST)) {
208 /* Joining a zombie channel (zannel): give ops and increment TS. */
210 chptr->creationtime++;
211 } else if (IsInvited(sptr, chptr) || (IsXtraOp(sptr) && key && strcmp(key, "OVERRIDE") == 0)) {
212 /* Invites and key=OVERRIDE bypass these other checks. */
213 } else if (chptr->mode.mode & MODE_INVITEONLY)
214 err = ERR_INVITEONLYCHAN;
215 else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit))
216 err = ERR_CHANNELISFULL;
217 else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
218 err = ERR_NEEDREGGEDNICK;
219 else if (find_ban(sptr, chptr->banlist))
220 err = ERR_BANNEDFROMCHAN;
221 else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)))
222 err = ERR_BADCHANNELKEY;
226 /* An oper with WALK_LCHAN privilege can join a local channel
227 * he otherwise could not join by using "OVERRIDE" as the key.
228 * This will generate a HACK(4) notice, but fails if the oper
229 * could normally join the channel. */
230 if (HasPriv(sptr, PRIV_WALK_LCHAN) && HasFlag(sptr, FLAG_SECURITY_SERV) && key && !strcmp(key, "INVISIBLE")) {
231 sendto_opmask_butone(0, SNO_HACK4, "INVISIBLE JOIN: %C JOIN %H", sptr, chptr);
232 flags = CHFL_INVISIBLE;
237 if ((HasPriv(sptr, PRIV_WALK_LCHAN) && HasFlag(sptr, FLAG_SECURITY_SERV))
238 && !(flags & CHFL_CHANOP)
239 && key && !strcmp(key, "OVERRIDE"))
243 if (strcmp(chptr->mode.key, "OVERRIDE")
244 && strcmp(chptr->mode.apass, "OVERRIDE")
245 && strcmp(chptr->mode.upass, "OVERRIDE")) {
246 send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
250 case ERR_INVITEONLYCHAN: err = 'i'; break;
251 case ERR_CHANNELISFULL: err = 'l'; break;
252 case ERR_BANNEDFROMCHAN: err = 'b'; break;
253 case ERR_BADCHANNELKEY: err = 'k'; break;
254 case ERR_NEEDREGGEDNICK: err = 'r'; break;
255 default: err = '?'; break;
257 /* send accountability notice */
259 sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
260 "(overriding +%c)", sptr, chptr, err);
265 if(!err && !override && !IsInvited(sptr, chptr) && chptr->mode.access && chptr->mode.access > 0 && chptr->mode.access < 500 && feature_bool(FEAT_CHMODE_A_ENABLE)) {
266 //We have to check the users channel access...
267 struct Client *acptr;
268 if(feature_str(FEAT_CHMODE_A_TARGET) && (acptr = FindUser(feature_str(FEAT_CHMODE_A_TARGET))) && IsNetServ(acptr) && IsService(cli_user(acptr)->server)) {
269 sendcmdto_one(&me, CMD_RELAY, acptr, "%C JA %C %s %i %i", acptr, sptr, chptr->chname, chptr->mode.access, flags);
270 continue; // We can't do anything more here... We have to wait for the response...
272 if(feature_str(FEAT_CHMODE_A_TARGET))
273 send_reply(sptr, ERR_SERVICESDOWN, feature_str(FEAT_CHMODE_A_TARGET));
274 err = ERR_JOINACCESS;
278 /* Is there some reason the user may not join? */
279 if(err && chptr->mode.altchan && IsChannelName(chptr->mode.altchan) && strIsIrcCh(chptr->mode.altchan)) {
280 char *altchan = chptr->mode.altchan;
282 if (!(chptrb = FindChannel(altchan))) {
283 if (((altchan[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(altchan) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) {
284 //we don't send an error message here - that would be very strange for the user, because they normaly don't know that mode +F is set
285 } else if ((chptrb = get_channel(sptr, altchan, CGT_CREATE))) {
286 joinbuf_join(&create, chptrb, CHFL_CHANOP | CHFL_CHANNEL_MANAGER);
287 do_names(sptr, chptrb, NAMES_ALL|NAMES_EON);
290 if(find_member_link(chptrb, sptr))
291 continue; //we have already joined this channel
292 //first of all check if we may even join this channel
294 if (chptrb->users == 0 && !chptrb->mode.apass[0] && !(chptrb->mode.mode & MODE_PERSIST)) {
295 /* Joining a zombie channel (zannel): give ops and increment TS. */
297 chptrb->creationtime++;
298 } else if (IsInvited(sptr, chptrb)) {
299 /* Invites and key=OVERRIDE bypass these other checks. */
300 } else if (chptrb->mode.mode & MODE_INVITEONLY)
301 err2 = ERR_INVITEONLYCHAN;
302 else if (chptrb->mode.limit && (chptrb->users >= chptrb->mode.limit))
303 err2 = ERR_CHANNELISFULL;
304 else if ((chptrb->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
305 err2 = ERR_NEEDREGGEDNICK;
306 else if (find_ban(sptr, chptrb->banlist))
307 err2 = ERR_BANNEDFROMCHAN;
308 else if (*chptrb->mode.key && (!key || strcmp(key, chptrb->mode.key)))
309 err2 = ERR_BADCHANNELKEY;
311 joinbuf_join(&join, chptrb, flags);
312 del_invite(sptr, chptrb);
313 if (chptrb->topic[0]) {
314 send_reply(sptr, RPL_TOPIC, chptrb->chname, chptrb->topic);
315 send_reply(sptr, RPL_TOPICWHOTIME, chptrb->chname, chptrb->topic_nick, chptrb->topic_time);
317 do_names(sptr, chptrb, NAMES_ALL|NAMES_EON|(((chptrb->mode.mode & MODE_AUDITORIUM) && !(flags & CHFL_CHANOP)) ? NAMES_OPS : 0)); /* send /names list */
324 case ERR_NEEDREGGEDNICK:
328 feature_str(FEAT_URLREG));
331 send_reply(sptr, err, chptr->chname);
337 joinbuf_join(&join, chptr, flags);
338 if (flags & CHFL_CHANOP) {
340 /* Always let the server op him: this is needed on a net with older servers
341 because they 'destruct' channels immediately when they become empty without
342 sending out a DESTRUCT message. As a result, they would always bounce a mode
343 (as HACK(2)) when the user ops himself.
344 (There is also no particularly good reason to have the user op himself.)
346 modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER);
347 modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr,
348 chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL);
349 modebuf_flush(&mbuf);
353 del_invite(sptr, chptr);
355 if (chptr->topic[0]) {
356 send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
357 send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
361 do_names(sptr, chptr, NAMES_ALL|NAMES_EON|(((chptr->mode.mode & MODE_AUDITORIUM) && !(flags & CHFL_CHANOP)) ? NAMES_OPS : 0)); /* send /names list */
364 joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */
365 joinbuf_flush(&create);
370 /** Handle a JOIN message from a server connection.
371 * See @ref m_functions for discussion of the arguments.
372 * @param[in] cptr Client that sent us the message.
373 * @param[in] sptr Original source of message.
374 * @param[in] parc Number of arguments.
375 * @param[in] parv Argument vector.
377 int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
379 struct Membership *member;
380 struct Channel *chptr;
383 unsigned int invisible;
391 return protocol_violation(cptr,
392 "%s tried to JOIN %s, duh!",
394 (parc < 2 || *parv[1] == '\0') ? "a channel" :
399 if (parc < 2 || *parv[1] == '\0')
400 return need_more_params(sptr, "JOIN");
402 if (parc > 2 && parv[2])
403 creation = atoi(parv[2]);
404 if (parc > 3 && parv[3])
405 invisible = atoi(parv[3]);
407 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
409 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
411 for (name = ircd_strtok(&p, chanlist, ","); name;
412 name = ircd_strtok(&p, 0, ",")) {
414 if (invisible == 1) {
415 flags = CHFL_DEOPPED | CHFL_INVISIBLE;
417 flags = CHFL_DEOPPED;
420 if (IsLocalChannel(name) || !IsChannelName(name))
422 protocol_violation(cptr, "%s tried to join %s", cli_name(sptr), name);
426 if (!(chptr = FindChannel(name)))
428 /* No channel exists, so create one */
429 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
431 protocol_violation(sptr,"couldn't get channel %s for %s",
432 name,cli_name(sptr));
435 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
437 chptr->creationtime = creation;
438 const struct User* user = cli_user(sptr);
439 if (IsAccount(sptr)) {
440 //chptr->chanowner = user->account;
443 else { /* We have a valid channel? */
444 if ((member = find_member_link(chptr, sptr)))
446 /* It is impossible to get here --Run */
447 if (!IsZombie(member)) /* already on channel */
449 if (invisible == 1) {
450 flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK | CHFL_INVISIBLE);
452 flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK);
454 remove_user_from_channel(sptr, chptr);
455 chptr = FindChannel(name);
458 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
460 /* Always copy the timestamp when it is older, that is the only way to
461 ensure network-wide synchronization of creation times.
462 We now also copy a creation time that only 1 second younger...
463 this is needed because the timestamp must be incremented
464 by one when someone joins an existing, but empty, channel.
465 However, this is only necessary when the channel is still
466 empty (also here) and when this channel doesn't have +A set.
468 To prevent this from allowing net-rides on the channel, we
469 clear all modes from the channel.
471 (Scenario for a net ride: c1 - s1 - s2 - c2, with c1 the only
472 user in the channel; c1 parts and rejoins, gaining ops.
473 Before s2 sees c1's part, c2 joins the channel and parts
474 immediately. s1 sees c1 part, c1 create, c2 join, c2 part;
475 c2's join resets the timestamp. s2 sees c2 join, c2 part, c1
476 part, c1 create; but since s2 sees the channel as a zannel or
477 non-existent, it does not bounce the create with the newer
480 if (creation && (creation < chptr->creationtime ||
481 (!chptr->mode.apass[0] && !(chptr->mode.mode & MODE_PERSIST) && chptr->users == 0))) {
482 struct Membership *member;
485 chptr->creationtime = creation;
486 /* Wipe out the current modes on the channel. */
487 modebuf_init(&mbuf, sptr, cptr, chptr, MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK3);
489 modebuf_mode(&mbuf, MODE_DEL | chptr->mode.mode);
490 chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS;
492 if (chptr->mode.limit) {
493 modebuf_mode_uint(&mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
494 chptr->mode.limit = 0;
497 if (chptr->mode.key[0]) {
498 modebuf_mode_string(&mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
499 chptr->mode.key[0] = '\0';
502 if (chptr->mode.upass[0]) {
503 modebuf_mode_string(&mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
504 chptr->mode.upass[0] = '\0';
507 if (chptr->mode.apass[0]) {
508 modebuf_mode_string(&mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
509 chptr->mode.apass[0] = '\0';
512 for (member = chptr->members; member; member = member->next_member)
514 if (IsChanOp(member)) {
515 modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, OpLevel(member));
516 member->status &= ~CHFL_CHANOP;
518 if (HasVoice(member)) {
519 modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, member->user, OpLevel(member));
520 member->status &= ~CHFL_VOICE;
523 modebuf_flush(&mbuf);
527 joinbuf_join(&join, chptr, flags);
530 joinbuf_flush(&join); /* flush joins... */