Make empty -A channels into zombies ("zannels") to avoid +A hijacks.
[ircu2.10.12-pk.git] / ircd / m_join.c
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$
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  */
57 static char *
58 last0(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  */
105 int 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 */
153     if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) &&
154         GlineIsActive(gline) && !IsAnOper(sptr)) {
155       send_reply(sptr, ERR_BANNEDFROMCHAN, name);
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);
177     } else if (find_member_link(chptr, sptr)) {
178       continue; /* already on channel */
179     } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
180       continue;
181     } else {
182       int flags = CHFL_DEOPPED;
183       int err = 0;
184
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))
190         flags = CHFL_CHANOP;
191       else if (chptr->users == 0 && !chptr->mode.apass[0]) {
192         /* Joining a zombie channel (zannel): give ops and increment TS. */
193         flags = CHFL_CHANOP;
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;
207
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"))
216       {
217         switch (err) {
218         case 0:
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);
223             continue;
224           }
225           break;
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;
232         }
233         /* send accountability notice */
234         if (err)
235           sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
236                                "(overriding +%c)", sptr, chptr, err);
237         err = 0;
238       }
239
240       /* Is there some reason the user may not join? */
241       if (err) {
242         send_reply(sptr, err, chptr->chname);
243         continue;
244       }
245
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. */
250         struct ModeBuf mbuf;
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);
255       }
256     }
257
258     del_invite(sptr, chptr);
259
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,
263                  chptr->topic_time);
264     }
265
266     do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
267   }
268
269   joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */
270   joinbuf_flush(&create);
271
272   return 0;
273 }
274
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.
281  */
282 int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
283 {
284   struct Membership *member;
285   struct Channel *chptr;
286   struct JoinBuf join;
287   unsigned int flags;
288   time_t creation = 0;
289   char *p = 0;
290   char *chanlist;
291   char *name;
292
293   if (IsServer(sptr))
294   {
295     return protocol_violation(cptr,
296                               "%s tried to JOIN %s, duh!",
297                               cli_name(sptr),
298                               (parc < 2 || *parv[1] == '\0') ? "a channel" :
299                                                                parv[1]
300                               );
301   }
302
303   if (parc < 2 || *parv[1] == '\0')
304     return need_more_params(sptr, "JOIN");
305
306   if (parc > 2 && parv[2])
307     creation = atoi(parv[2]);
308
309   joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0);
310
311   chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */
312
313   for (name = ircd_strtok(&p, chanlist, ","); name;
314        name = ircd_strtok(&p, 0, ",")) {
315
316     flags = CHFL_DEOPPED;
317
318     if (IsLocalChannel(name) || !IsChannelName(name))
319     {
320       protocol_violation(cptr, "%s tried to join %s", cli_name(sptr), name);
321       continue;
322     }
323
324     if (!(chptr = FindChannel(name)))
325     {
326       /* No channel exists, so create one */
327       if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
328       {
329         protocol_violation(sptr,"couldn't get channel %s for %s",
330                            name,cli_name(sptr));
331         continue;
332       }
333       flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
334
335       /* when the network is 2.10.11+ then remove MAGIC_REMOTE_JOIN_TS */
336       chptr->creationtime = creation ? creation : MAGIC_REMOTE_JOIN_TS;
337     }
338     else { /* We have a valid channel? */
339       if ((member = find_member_link(chptr, sptr)))
340       {
341         /* It is impossible to get here --Run */
342         if (!IsZombie(member)) /* already on channel */
343           continue;
344
345         flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK);
346         remove_user_from_channel(sptr, chptr);
347         chptr = FindChannel(name);
348       }
349       else
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.
358       */
359       if (creation && creation - ((!chptr->mode.apass[0] && chptr->users == 0) ? 1 : 0) <= chptr->creationtime)
360         chptr->creationtime = creation;
361     }
362
363     joinbuf_join(&join, chptr, flags);
364   }
365
366   joinbuf_flush(&join); /* flush joins... */
367
368   return 0;
369 }