]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/m_join.c
fixed autochanmodes code so it works for channel modes +CN
[irc/quakenet/snircd.git] / ircd / m_join.c
CommitLineData
189935b1 1/*
2 * IRC - Internet Relay Chat, ircd/m_join.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
5 *
6 * See file AUTHORS in IRC package for additional names of
7 * the programmers.
8 *
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)
12 * any later version.
13 *
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.
18 *
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.
22 *
23 * $Id: m_join.c,v 1.34.2.1 2005/10/06 23:58:09 entrope Exp $
24 */
25
26#include "config.h"
27
28#include "channel.h"
29#include "client.h"
30#include "gline.h"
31#include "hash.h"
32#include "ircd.h"
33#include "ircd_chattr.h"
34#include "ircd_features.h"
35#include "ircd_log.h"
36#include "ircd_reply.h"
37#include "ircd_string.h"
38#include "msg.h"
39#include "numeric.h"
40#include "numnicks.h"
41#include "s_debug.h"
42#include "s_user.h"
43#include "send.h"
44#include "sys.h"
45
46/* #include <assert.h> -- Now using assert in ircd_log.h */
47#include <stdlib.h>
48#include <string.h>
49
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).
56 */
57static char *
58last0(struct Client *cptr, struct Client *sptr, char *chanlist)
59{
60 char *p;
61 int join0 = 0;
62
63 for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */
64 if (p[0] == '0' && (p[1] == ',' || p[1] == '\0')) {
65 if (p[1] == ',')
66 p++;
67 chanlist = p + 1;
68 join0 = 1;
69 } else {
70 while (p[0] != ',' && p[0] != '\0') /* skip past channel name */
71 p++;
72
73 if (!p[0]) /* hit the end */
74 break;
75 }
76
77 if (join0) {
78 struct JoinBuf part;
79 struct Membership *member;
80
81 joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL,
82 "Left all channels", 0);
83
84 joinbuf_join(&part, 0, 0);
85
86 while ((member = cli_user(sptr)->channel))
87 joinbuf_join(&part, member->channel,
88 IsZombie(member) ? CHFL_ZOMBIE :
89 IsDelayedJoin(member) ? CHFL_DELAYED :
90 0);
91
92 joinbuf_flush(&part);
93 }
94
95 return chanlist;
96}
97
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.
104 */
105int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
106{
107 struct Channel *chptr;
108 struct JoinBuf join;
109 struct JoinBuf create;
110 struct Gline *gline;
111 char *p = 0;
112 char *chanlist;
113 char *name;
114 char *keys;
115
116 if (parc < 2 || *parv[1] == '\0')
117 return need_more_params(sptr, "JOIN");
118
119 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
120 joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime());
121
122 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
123
124 keys = parv[2]; /* remember where keys are */
125
126 for (name = ircd_strtok(&p, chanlist, ","); name;
127 name = ircd_strtok(&p, 0, ",")) {
128 char *key = 0;
129
130 /* If we have any more keys, take the first for this channel. */
131 if (!BadPtr(keys)
132 && (keys = strchr(key = keys, ',')))
133 *keys++ = '\0';
134
135 /* Empty keys are the same as no keys. */
136 if (key && !key[0])
137 key = 0;
138
139 if (!IsChannelName(name) || !strIsIrcCh(name))
140 {
141 /* bad channel name */
142 send_reply(sptr, ERR_NOSUCHCHANNEL, name);
143 continue;
144 }
145
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 */
150 }
151
152 /* BADCHANed channel */
8ebc3d85 153 if ((gline = gline_find(name, GLINE_BADCHAN)) &&
189935b1 154 GlineIsActive(gline) && !IsAnOper(sptr)) {
8ebc3d85 155 send_reply(sptr, ERR_BADCHANNAME, name, gline->gl_reason);
189935b1 156 continue;
157 }
158
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);
163 continue;
164 }
165
166 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
167 continue;
168
169 /* Try to add the new channel as a recent target for the user. */
170 if (check_target_limit(sptr, chptr, chptr->chname, 1)) {
171 chptr->members = 0;
172 destruct_channel(chptr);
173 continue;
174 }
175
176 joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER);
f952a879 177 if (feature_bool(FEAT_AUTOCHANMODES) && feature_str(FEAT_AUTOCHANMODES_LIST) && strlen(feature_str(FEAT_AUTOCHANMODES_LIST)) > 0)
178 SetAutoChanModes(chptr);
189935b1 179 } else if (find_member_link(chptr, sptr)) {
180 continue; /* already on channel */
181 } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
182 continue;
183 } else {
184 int flags = CHFL_DEOPPED;
185 int err = 0;
186
187 /* Check target change limits. */
188
189 /* Check Apass/Upass -- since we only ever look at a single
190 * "key" per channel now, this hampers brute force attacks. */
191 if (key && !strcmp(key, chptr->mode.apass))
192 flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
193 else if (key && !strcmp(key, chptr->mode.upass))
194 flags = CHFL_CHANOP;
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;
207
943fcf70 208 /*
209 * ASUKA_X:
210 * Allow XtraOpers to join all channels.
211 * --Bigfoot
212 */
213 if (IsXtraOp(sptr))
214 err = 0;
215
189935b1 216 /* An oper with WALK_LCHAN privilege can join a local channel
217 * he otherwise could not join by using "OVERRIDE" as the key.
218 * This will generate a HACK(4) notice, but fails if the oper
219 * could normally join the channel. */
220 if (IsLocalChannel(chptr->chname)
221 && HasPriv(sptr, PRIV_WALK_LCHAN)
222 && !(flags & CHFL_CHANOP)
223 && key && !strcmp(key, "OVERRIDE")
224 && strcmp(chptr->mode.key, "OVERRIDE"))
225 {
226 switch (err) {
227 case 0:
228 send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
229 continue;
230 case ERR_INVITEONLYCHAN: err = 'i'; break;
231 case ERR_CHANNELISFULL: err = 'l'; break;
232 case ERR_BANNEDFROMCHAN: err = 'b'; break;
233 case ERR_BADCHANNELKEY: err = 'k'; break;
234 case ERR_NEEDREGGEDNICK: err = 'r'; break;
235 default: err = '?'; break;
236 }
237 /* send accountability notice */
238 sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
239 "(overriding +%c)", sptr, chptr, err);
240 err = 0;
241 }
242
243 /* Is there some reason the user may not join? */
244 if (err) {
245 send_reply(sptr, err, chptr->chname);
246 continue;
247 }
248
249 joinbuf_join(&join, chptr, flags);
250 }
251
252 del_invite(sptr, chptr);
253
254 if (chptr->topic[0]) {
255 send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
256 send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
257 chptr->topic_time);
258 }
259
260 do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
261 }
262
263 joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */
264 joinbuf_flush(&create);
265
266 return 0;
267}
268
269/** Handle a JOIN message from a server connection.
270 * See @ref m_functions for discussion of the arguments.
271 * @param[in] cptr Client that sent us the message.
272 * @param[in] sptr Original source of message.
273 * @param[in] parc Number of arguments.
274 * @param[in] parv Argument vector.
275 */
276int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
277{
278 struct Membership *member;
279 struct Channel *chptr;
280 struct JoinBuf join;
281 unsigned int flags;
282 time_t creation = 0;
283 char *p = 0;
284 char *chanlist;
285 char *name;
286
287 if (IsServer(sptr))
288 {
289 return protocol_violation(cptr,
290 "%s tried to JOIN %s, duh!",
291 cli_name(sptr),
292 (parc < 2 || *parv[1] == '\0') ? "a channel" :
293 parv[1]
294 );
295 }
296
297 if (parc < 2 || *parv[1] == '\0')
298 return need_more_params(sptr, "JOIN");
299
300 if (parc > 2 && parv[2])
301 creation = atoi(parv[2]);
302
303 joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
304
305 chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
306
307 for (name = ircd_strtok(&p, chanlist, ","); name;
308 name = ircd_strtok(&p, 0, ",")) {
309
310 if (name[0] == '0' && name[1] == ':')
311 {
312 flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
313 name += 2;
314 }
315 else if (name[0] == '1' && name[1] == ':')
316 {
317 flags = CHFL_CHANOP;
318 name += 2;
319 }
320 else
321 flags = CHFL_DEOPPED;
322
323 if (IsLocalChannel(name) || !IsChannelName(name))
324 {
325 protocol_violation(cptr, "%s tried to join %s", cli_name(sptr), name);
326 continue;
327 }
328
329 if (!(chptr = FindChannel(name)))
330 {
331 /* No channel exists, so create one */
332 if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
333 {
334 protocol_violation(sptr,"couldn't get channel %s for %s",
335 name,cli_name(sptr));
336 continue;
337 }
338 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
339
340 /* when the network is 2.10.11+ then remove MAGIC_REMOTE_JOIN_TS */
341 chptr->creationtime = creation ? creation : MAGIC_REMOTE_JOIN_TS;
342 }
343 else { /* We have a valid channel? */
344 if ((member = find_member_link(chptr, sptr)))
345 {
346 /* It is impossible to get here --Run */
347 if (!IsZombie(member)) /* already on channel */
348 continue;
349
350 flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK);
351 remove_user_from_channel(sptr, chptr);
352 chptr = FindChannel(name);
353 }
354 else
355 flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
356 /* Always copy the timestamp when it is older, that is the only way to
357 ensure network-wide synchronization of creation times. */
358 if (creation && creation < chptr->creationtime)
359 chptr->creationtime = creation;
360 }
361
362 joinbuf_join(&join, chptr, flags);
363 }
364
365 joinbuf_flush(&join); /* flush joins... */
366
367 return 0;
368}