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.
33 #include "ircd_chattr.h"
34 #include "ircd_features.h"
36 #include "ircd_reply.h"
37 #include "ircd_string.h"
46 /* #include <assert.h> -- Now using assert in ircd_log.h */
50 /** Searches for and handles a 0 in a join list.
51 * @param[in] cptr Client that sent us the message.
52 * @param[in] sptr Original source of message.
53 * @param[in] chanlist List of channels to join.
54 * @return First token in \a chanlist after the final 0 entry, which
55 * may be its nul terminator (if the final entry is a 0 entry).
58 last0(struct Client *cptr, struct Client *sptr, char *chanlist)
63 for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */
64 if (p[0] == '0' && (p[1] == ',' || p[1] == '\0')) {
70 while (p[0] != ',' && p[0] != '\0') /* skip past channel name */
73 if (!p[0]) /* hit the end */
79 struct Membership *member;
81 joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL,
82 "Left all channels", 0);
84 joinbuf_join(&part, 0, 0);
86 while ((member = cli_user(sptr)->channel))
87 joinbuf_join(&part, member->channel,
88 IsZombie(member) ? CHFL_ZOMBIE :
89 IsDelayedJoin(member) ? CHFL_DELAYED :
98 /** Handle a JOIN message from a client connection.
99 * See @ref m_functions for discussion of the arguments.
100 * @param[in] cptr Client that sent us the message.
101 * @param[in] sptr Original source of message.
102 * @param[in] parc Number of arguments.
103 * @param[in] parv Argument vector.
105 int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
107 struct Channel *chptr;
109 struct JoinBuf create;
116 if (parc < 2 || *parv[1] == '\0')
117 return need_more_params(sptr, "JOIN");
119 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
120 joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime());
122 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
124 keys = parv[2]; /* remember where keys are */
126 for (name = ircd_strtok(&p, chanlist, ","); name;
127 name = ircd_strtok(&p, 0, ",")) {
130 /* If we have any more keys, take the first for this channel. */
132 && (keys = strchr(key = keys, ',')))
135 /* Empty keys are the same as no keys. */
139 if (!IsChannelName(name) || !strIsIrcCh(name))
141 /* bad channel name */
142 send_reply(sptr, ERR_NOSUCHCHANNEL, name);
146 if (cli_user(sptr)->joined >= feature_int(FEAT_MAXCHANNELSPERUSER)
147 && !HasPriv(sptr, PRIV_CHAN_LIMIT)) {
148 send_reply(sptr, ERR_TOOMANYCHANNELS, name);
149 break; /* no point processing the other channels */
152 /* BADCHANed channel */
153 if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) &&
154 GlineIsActive(gline) && !IsAnOper(sptr)) {
155 send_reply(sptr, ERR_BANNEDFROMCHAN, name);
159 if (!(chptr = FindChannel(name))) {
160 if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS))
161 || strlen(name) >= IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) {
162 send_reply(sptr, ERR_NOSUCHCHANNEL, name);
166 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
169 /* Try to add the new channel as a recent target for the user. */
170 if (check_target_limit(sptr, chptr, chptr->chname, 1)) {
172 destruct_channel(chptr);
176 joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER);
177 } else if (find_member_link(chptr, sptr)) {
178 continue; /* already on channel */
179 } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
182 int flags = CHFL_DEOPPED;
185 /* Check Apass/Upass -- since we only ever look at a single
186 * "key" per channel now, this hampers brute force attacks. */
187 if (key && !strcmp(key, chptr->mode.apass))
188 flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
189 else if (key && !strcmp(key, chptr->mode.upass))
191 else if (chptr->users == 0 && !chptr->mode.apass[0]) {
192 /* Joining a zombie channel (zannel): give ops and increment TS. */
194 chptr->creationtime++;
195 } else if (IsInvited(sptr, chptr)) {
196 /* Invites bypass these other checks. */
197 } else if (chptr->mode.mode & MODE_INVITEONLY)
198 err = ERR_INVITEONLYCHAN;
199 else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit))
200 err = ERR_CHANNELISFULL;
201 else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
202 err = ERR_NEEDREGGEDNICK;
203 else if (find_ban(sptr, chptr->banlist))
204 err = ERR_BANNEDFROMCHAN;
205 else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)))
206 err = ERR_BADCHANNELKEY;
208 /* An oper with WALK_LCHAN privilege can join a local channel
209 * he otherwise could not join by using "OVERRIDE" as the key.
210 * This will generate a HACK(4) notice, but fails if the oper
211 * could normally join the channel. */
212 if (IsLocalChannel(chptr->chname)
213 && HasPriv(sptr, PRIV_WALK_LCHAN)
214 && !(flags & CHFL_CHANOP)
215 && key && !strcmp(key, "OVERRIDE"))
219 if (strcmp(chptr->mode.key, "OVERRIDE")
220 && strcmp(chptr->mode.apass, "OVERRIDE")
221 && strcmp(chptr->mode.upass, "OVERRIDE")) {
222 send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
226 case ERR_INVITEONLYCHAN: err = 'i'; break;
227 case ERR_CHANNELISFULL: err = 'l'; break;
228 case ERR_BANNEDFROMCHAN: err = 'b'; break;
229 case ERR_BADCHANNELKEY: err = 'k'; break;
230 case ERR_NEEDREGGEDNICK: err = 'r'; break;
231 default: err = '?'; break;
233 /* send accountability notice */
235 sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
236 "(overriding +%c)", sptr, chptr, err);
240 /* Is there some reason the user may not join? */
242 send_reply(sptr, err, chptr->chname);
246 joinbuf_join(&join, chptr, flags);
247 if (flags & CHFL_CHANOP) {
248 /* Send a MODE to the other servers. If the user used the A/U pass,
249 * let his server op him, otherwise let him op himself. */
251 modebuf_init(&mbuf, chptr->mode.apass[0] ? &me : sptr, cptr, chptr, MODEBUF_DEST_SERVER);
252 modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr,
253 chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL);
254 modebuf_flush(&mbuf);
258 del_invite(sptr, chptr);
260 if (chptr->topic[0]) {
261 send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
262 send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
266 do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
269 joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */
270 joinbuf_flush(&create);
275 /** Handle a JOIN message from a server connection.
276 * See @ref m_functions for discussion of the arguments.
277 * @param[in] cptr Client that sent us the message.
278 * @param[in] sptr Original source of message.
279 * @param[in] parc Number of arguments.
280 * @param[in] parv Argument vector.
282 int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
284 struct Membership *member;
285 struct Channel *chptr;
295 return protocol_violation(cptr,
296 "%s tried to JOIN %s, duh!",
298 (parc < 2 || *parv[1] == '\0') ? "a channel" :
303 if (parc < 2 || *parv[1] == '\0')
304 return need_more_params(sptr, "JOIN");
306 if (parc > 2 && parv[2])
307 creation = atoi(parv[2]);
309 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
311 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
313 for (name = ircd_strtok(&p, chanlist, ","); name;
314 name = ircd_strtok(&p, 0, ",")) {
316 flags = CHFL_DEOPPED;
318 if (IsLocalChannel(name) || !IsChannelName(name))
320 protocol_violation(cptr, "%s tried to join %s", cli_name(sptr), name);
324 if (!(chptr = FindChannel(name)))
326 /* No channel exists, so create one */
327 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
329 protocol_violation(sptr,"couldn't get channel %s for %s",
330 name,cli_name(sptr));
333 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
335 /* when the network is 2.10.11+ then remove MAGIC_REMOTE_JOIN_TS */
336 chptr->creationtime = creation ? creation : MAGIC_REMOTE_JOIN_TS;
338 else { /* We have a valid channel? */
339 if ((member = find_member_link(chptr, sptr)))
341 /* It is impossible to get here --Run */
342 if (!IsZombie(member)) /* already on channel */
345 flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK);
346 remove_user_from_channel(sptr, chptr);
347 chptr = FindChannel(name);
350 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
351 /* Always copy the timestamp when it is older, that is the only way to
352 ensure network-wide synchronization of creation times.
353 We now also copy a creation time that only 1 second younger...
354 this is needed because the timestamp must be incremented
355 by one when someone joins an existing, but empty, channel.
356 However, this is only necessary when the channel is still
357 empty (also here) and when this channel doesn't have +A set.
359 if (creation && creation - ((!chptr->mode.apass[0] && chptr->users == 0) ? 1 : 0) <= chptr->creationtime)
360 chptr->creationtime = creation;
363 joinbuf_join(&join, chptr, flags);
366 joinbuf_flush(&join); /* flush joins... */