]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * ircd-ratbox: A slightly useful ircd. | |
3 | * m_join.c: Joins a channel. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
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 2 of the License, or | |
12 | * (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
23 | * | |
24 | * $Id: m_join.c 3173 2007-01-31 23:57:18Z jilles $ | |
25 | */ | |
26 | ||
27 | #include "stdinc.h" | |
28 | #include "tools.h" | |
29 | #include "channel.h" | |
30 | #include "client.h" | |
31 | #include "common.h" | |
32 | #include "hash.h" | |
33 | #include "irc_string.h" | |
34 | #include "ircd.h" | |
35 | #include "numeric.h" | |
36 | #include "send.h" | |
37 | #include "s_serv.h" | |
38 | #include "s_conf.h" | |
39 | #include "s_newconf.h" | |
40 | #include "msg.h" | |
41 | #include "parse.h" | |
42 | #include "modules.h" | |
43 | #include "sprintf_irc.h" | |
44 | #include "packet.h" | |
45 | ||
46 | static int m_join(struct Client *, struct Client *, int, const char **); | |
47 | static int ms_join(struct Client *, struct Client *, int, const char **); | |
48 | ||
49 | static int h_can_create_channel; | |
50 | static int h_channel_join; | |
51 | ||
52 | struct Message join_msgtab = { | |
53 | "JOIN", 0, 0, 0, MFLG_SLOW, | |
54 | {mg_unreg, {m_join, 2}, {ms_join, 2}, mg_ignore, mg_ignore, {m_join, 2}} | |
55 | }; | |
56 | ||
57 | mapi_clist_av1 join_clist[] = { &join_msgtab, NULL }; | |
58 | ||
59 | mapi_hlist_av1 join_hlist[] = { | |
60 | { "can_create_channel", &h_can_create_channel }, | |
61 | { "channel_join", &h_channel_join }, | |
62 | { NULL, NULL }, | |
63 | }; | |
64 | ||
65 | DECLARE_MODULE_AV1(join, NULL, NULL, join_clist, join_hlist, NULL, "$Revision: 3173 $"); | |
66 | ||
67 | static void do_join_0(struct Client *client_p, struct Client *source_p); | |
68 | static int check_channel_name_loc(struct Client *source_p, const char *name); | |
69 | ||
70 | static void set_final_mode(struct Mode *mode, struct Mode *oldmode); | |
71 | static void remove_our_modes(struct Channel *chptr, struct Client *source_p); | |
72 | ||
73 | static char modebuf[MODEBUFLEN]; | |
74 | static char parabuf[MODEBUFLEN]; | |
75 | static char *mbuf; | |
76 | ||
77 | /* Check what we will forward to, without sending any notices to the user | |
78 | * -- jilles | |
79 | */ | |
80 | static struct Channel * | |
81 | check_forward(struct Client *source_p, struct Channel *chptr, | |
82 | char *key) | |
83 | { | |
84 | int depth = 0, i; | |
85 | ||
86 | /* User is +Q */ | |
87 | if (IsNoForward(source_p)) | |
88 | return NULL; | |
89 | ||
90 | while (depth < 16) | |
91 | { | |
92 | chptr = find_channel(chptr->mode.forward); | |
93 | /* Can only forward to existing channels */ | |
94 | if (chptr == NULL) | |
95 | return NULL; | |
96 | /* Already on there, show original error message */ | |
97 | if (IsMember(source_p, chptr)) | |
98 | return NULL; | |
99 | /* Juped. Sending a warning notice would be unfair */ | |
100 | if (hash_find_resv(chptr->chname)) | |
101 | return NULL; | |
102 | /* Don't forward to +Q channel */ | |
103 | if (chptr->mode.mode & MODE_DISFORWARD) | |
104 | return NULL; | |
105 | i = can_join(source_p, chptr, key); | |
106 | if (i == 0) | |
107 | return chptr; | |
108 | if (i != ERR_INVITEONLYCHAN && i != ERR_NEEDREGGEDNICK && i != ERR_THROTTLE && i != ERR_CHANNELISFULL) | |
109 | return NULL; | |
110 | depth++; | |
111 | } | |
112 | ||
113 | return NULL; | |
114 | } | |
115 | ||
116 | /* | |
117 | * m_join | |
118 | * parv[0] = sender prefix | |
119 | * parv[1] = channel | |
120 | * parv[2] = channel password (key) | |
121 | */ | |
122 | static int | |
123 | m_join(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
124 | { | |
125 | static char jbuf[BUFSIZE]; | |
126 | struct Channel *chptr = NULL; | |
127 | struct ConfItem *aconf; | |
128 | char *name; | |
129 | char *key = NULL; | |
130 | int i, flags = 0; | |
131 | char *p = NULL, *p2 = NULL; | |
132 | char *chanlist; | |
133 | char *mykey; | |
134 | int successful_join_count = 0; /* Number of channels successfully joined */ | |
135 | ||
136 | jbuf[0] = '\0'; | |
137 | ||
138 | /* rebuild the list of channels theyre supposed to be joining. | |
139 | * this code has a side effect of losing keys, but.. | |
140 | */ | |
141 | chanlist = LOCAL_COPY(parv[1]); | |
142 | for(name = strtoken(&p, chanlist, ","); name; name = strtoken(&p, NULL, ",")) | |
143 | { | |
144 | /* check the length and name of channel is ok */ | |
145 | if(!check_channel_name_loc(source_p, name) || (strlen(name) > LOC_CHANNELLEN)) | |
146 | { | |
147 | sendto_one_numeric(source_p, ERR_BADCHANNAME, | |
148 | form_str(ERR_BADCHANNAME), (unsigned char *) name); | |
149 | continue; | |
150 | } | |
151 | ||
152 | /* join 0 parts all channels */ | |
153 | if(*name == '0' && !atoi(name)) | |
154 | { | |
155 | (void) strcpy(jbuf, "0"); | |
156 | continue; | |
157 | } | |
158 | ||
159 | /* check it begins with # or &, and local chans are disabled */ | |
160 | else if(!IsChannelName(name)) | |
161 | { | |
162 | sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, | |
163 | form_str(ERR_NOSUCHCHANNEL), name); | |
164 | continue; | |
165 | } | |
166 | ||
167 | /* see if its resv'd */ | |
168 | if(!IsExemptResv(source_p) && (aconf = hash_find_resv(name))) | |
169 | { | |
170 | sendto_one_numeric(source_p, ERR_BADCHANNAME, | |
171 | form_str(ERR_BADCHANNAME), name); | |
172 | ||
173 | /* dont warn for opers */ | |
174 | if(!IsExemptJupe(source_p) && !IsOper(source_p)) | |
175 | sendto_realops_snomask(SNO_SPY, L_NETWIDE, | |
176 | "User %s (%s@%s) is attempting to join locally juped channel %s (%s)", | |
177 | source_p->name, source_p->username, | |
178 | source_p->orighost, name, aconf->passwd); | |
179 | /* dont update tracking for jupe exempt users, these | |
180 | * are likely to be spamtrap leaves | |
181 | */ | |
182 | else if(IsExemptJupe(source_p)) | |
183 | aconf->port--; | |
184 | ||
185 | continue; | |
186 | } | |
187 | ||
188 | if(splitmode && !IsOper(source_p) && (*name != '&') && | |
189 | ConfigChannel.no_join_on_split) | |
190 | { | |
191 | sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), | |
192 | me.name, source_p->name, name); | |
193 | continue; | |
194 | } | |
195 | ||
196 | if(*jbuf) | |
197 | (void) strcat(jbuf, ","); | |
198 | (void) strlcat(jbuf, name, sizeof(jbuf)); | |
199 | } | |
200 | ||
201 | if(parc > 2) | |
202 | { | |
203 | mykey = LOCAL_COPY(parv[2]); | |
204 | key = strtoken(&p2, mykey, ","); | |
205 | } | |
206 | ||
207 | for(name = strtoken(&p, jbuf, ","); name; | |
208 | key = (key) ? strtoken(&p2, NULL, ",") : NULL, name = strtoken(&p, NULL, ",")) | |
209 | { | |
210 | hook_data_channel_activity hook_info; | |
211 | ||
212 | /* JOIN 0 simply parts all channels the user is in */ | |
213 | if(*name == '0' && !atoi(name)) | |
214 | { | |
215 | if(source_p->user->channel.head == NULL) | |
216 | continue; | |
217 | ||
218 | do_join_0(&me, source_p); | |
219 | continue; | |
220 | } | |
221 | ||
222 | /* look for the channel */ | |
223 | if((chptr = find_channel(name)) != NULL) | |
224 | { | |
225 | if(IsMember(source_p, chptr)) | |
226 | continue; | |
227 | ||
228 | flags = 0; | |
229 | } | |
230 | else | |
231 | { | |
232 | hook_data_client_approval moduledata; | |
233 | ||
234 | moduledata.client = source_p; | |
235 | moduledata.approved = 0; | |
236 | ||
237 | call_hook(h_can_create_channel, &moduledata); | |
238 | ||
239 | if(moduledata.approved != 0 && !IsOper(source_p)) | |
240 | { | |
241 | sendto_one(source_p, form_str(moduledata.approved), | |
242 | me.name, source_p->name, name); | |
243 | continue; | |
244 | } | |
245 | ||
246 | if(splitmode && !IsOper(source_p) && (*name != '&') && | |
247 | ConfigChannel.no_create_on_split) | |
248 | { | |
249 | sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), | |
250 | me.name, source_p->name, name); | |
251 | continue; | |
252 | } | |
253 | ||
254 | flags = CHFL_CHANOP; | |
255 | } | |
256 | ||
257 | if((dlink_list_length(&source_p->user->channel) >= | |
258 | (unsigned long) ConfigChannel.max_chans_per_user) && | |
259 | (!IsOper(source_p) || | |
260 | (dlink_list_length(&source_p->user->channel) >= | |
261 | (unsigned long) ConfigChannel.max_chans_per_user * 3))) | |
262 | { | |
263 | sendto_one(source_p, form_str(ERR_TOOMANYCHANNELS), | |
264 | me.name, source_p->name, name); | |
265 | if(successful_join_count) | |
266 | source_p->localClient->last_join_time = CurrentTime; | |
267 | return 0; | |
268 | } | |
269 | ||
270 | if(flags == 0) /* if channel doesn't exist, don't penalize */ | |
271 | successful_join_count++; | |
272 | ||
273 | if(chptr == NULL) /* If I already have a chptr, no point doing this */ | |
274 | { | |
275 | chptr = get_or_create_channel(source_p, name, NULL); | |
276 | ||
277 | if(chptr == NULL) | |
278 | { | |
279 | sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), | |
280 | me.name, source_p->name, name); | |
281 | if(successful_join_count > 0) | |
282 | successful_join_count--; | |
283 | continue; | |
284 | } | |
285 | } | |
286 | ||
287 | if(!IsOper(source_p) && !IsExemptSpambot(source_p)) | |
288 | check_spambot_warning(source_p, name); | |
289 | ||
290 | /* can_join checks for +i key, bans etc */ | |
291 | if((i = can_join(source_p, chptr, key))) | |
292 | { | |
293 | if ((i != ERR_NEEDREGGEDNICK && i != ERR_THROTTLE && i != ERR_INVITEONLYCHAN && i != ERR_CHANNELISFULL) || | |
294 | (!ConfigChannel.use_forward || (chptr = check_forward(source_p, chptr, key)) == NULL)) | |
295 | { | |
296 | sendto_one(source_p, form_str(i), me.name, source_p->name, name); | |
297 | if(successful_join_count > 0) | |
298 | successful_join_count--; | |
299 | continue; | |
300 | } | |
301 | sendto_one_numeric(source_p, ERR_LINKCHANNEL, form_str(ERR_LINKCHANNEL), name, chptr->chname); | |
302 | } | |
303 | ||
304 | /* add the user to the channel */ | |
305 | add_user_to_channel(chptr, source_p, flags); | |
306 | if (chptr->mode.join_num && | |
307 | CurrentTime - chptr->join_delta >= chptr->mode.join_time) | |
308 | { | |
309 | chptr->join_count = 0; | |
310 | chptr->join_delta = CurrentTime; | |
311 | } | |
312 | chptr->join_count++; | |
313 | ||
314 | /* we send the user their join here, because we could have to | |
315 | * send a mode out next. | |
316 | */ | |
317 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", | |
318 | source_p->name, | |
319 | source_p->username, source_p->host, chptr->chname); | |
320 | ||
321 | /* its a new channel, set +nt and burst. */ | |
322 | if(flags & CHFL_CHANOP) | |
323 | { | |
324 | chptr->channelts = CurrentTime; | |
325 | chptr->mode.mode |= MODE_TOPICLIMIT; | |
326 | chptr->mode.mode |= MODE_NOPRIVMSGS; | |
327 | ||
328 | sendto_channel_local(ONLY_CHANOPS, chptr, ":%s MODE %s +nt", | |
329 | me.name, chptr->chname); | |
330 | ||
331 | if(*chptr->chname == '#') | |
332 | { | |
333 | sendto_server(client_p, chptr, CAP_TS6, NOCAPS, | |
334 | ":%s SJOIN %ld %s +nt :@%s", | |
335 | me.id, (long) chptr->channelts, | |
336 | chptr->chname, source_p->id); | |
337 | sendto_server(client_p, chptr, NOCAPS, CAP_TS6, | |
338 | ":%s SJOIN %ld %s +nt :@%s", | |
339 | me.name, (long) chptr->channelts, | |
340 | chptr->chname, source_p->name); | |
341 | } | |
342 | } | |
343 | else | |
344 | { | |
345 | const char *modes = channel_modes(chptr, &me); | |
346 | ||
347 | sendto_server(client_p, chptr, CAP_TS6, NOCAPS, | |
348 | ":%s JOIN %ld %s %s", | |
349 | use_id(source_p), (long) chptr->channelts, | |
350 | chptr->chname, modes); | |
351 | ||
352 | sendto_server(client_p, chptr, NOCAPS, CAP_TS6, | |
353 | ":%s SJOIN %ld %s %s :%s", | |
354 | me.name, (long) chptr->channelts, | |
355 | chptr->chname, modes, source_p->name); | |
356 | } | |
357 | ||
358 | del_invite(chptr, source_p); | |
359 | ||
360 | if(chptr->topic != NULL) | |
361 | { | |
362 | sendto_one(source_p, form_str(RPL_TOPIC), me.name, | |
363 | source_p->name, chptr->chname, chptr->topic); | |
364 | ||
365 | sendto_one(source_p, form_str(RPL_TOPICWHOTIME), | |
366 | me.name, source_p->name, chptr->chname, | |
367 | chptr->topic_info, chptr->topic_time); | |
368 | } | |
369 | ||
370 | channel_member_names(chptr, source_p, 1); | |
371 | ||
372 | if(successful_join_count) | |
373 | source_p->localClient->last_join_time = CurrentTime; | |
374 | ||
375 | hook_info.client = source_p; | |
376 | hook_info.chptr = chptr; | |
377 | hook_info.key = key; | |
378 | call_hook(h_channel_join, &hook_info); | |
379 | } | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | /* | |
385 | * ms_join | |
386 | * | |
387 | * inputs - | |
388 | * output - none | |
389 | * side effects - handles remote JOIN's sent by servers. In TSora | |
390 | * remote clients are joined using SJOIN, hence a | |
391 | * JOIN sent by a server on behalf of a client is an error. | |
392 | * here, the initial code is in to take an extra parameter | |
393 | * and use it for the TimeStamp on a new channel. | |
394 | */ | |
395 | static int | |
396 | ms_join(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
397 | { | |
398 | struct Channel *chptr; | |
399 | static struct Mode mode, *oldmode; | |
400 | const char *s; | |
401 | const char *modes; | |
402 | time_t oldts; | |
403 | time_t newts; | |
404 | int isnew; | |
405 | int args = 0; | |
406 | int keep_our_modes = YES; | |
407 | int keep_new_modes = YES; | |
408 | dlink_node *ptr, *next_ptr; | |
409 | ||
410 | /* special case for join 0 */ | |
411 | if((parv[1][0] == '0') && (parv[1][1] == '\0') && parc == 2) | |
412 | { | |
413 | do_join_0(client_p, source_p); | |
414 | return 0; | |
415 | } | |
416 | ||
417 | if(parc < 4) | |
418 | return 0; | |
419 | ||
420 | if(!IsChannelName(parv[2]) || !check_channel_name(parv[2])) | |
421 | return 0; | |
422 | ||
423 | /* joins for local channels cant happen. */ | |
424 | if(parv[2][0] == '&') | |
425 | return 0; | |
426 | ||
427 | mbuf = modebuf; | |
428 | mode.key[0] = mode.forward[0] = '\0'; | |
429 | mode.mode = mode.limit = mode.join_num = mode.join_time = 0; | |
430 | ||
431 | s = parv[3]; | |
432 | while(*s) | |
433 | { | |
434 | switch (*(s++)) | |
435 | { | |
436 | case 'i': | |
437 | mode.mode |= MODE_INVITEONLY; | |
438 | break; | |
439 | case 'n': | |
440 | mode.mode |= MODE_NOPRIVMSGS; | |
441 | break; | |
442 | case 'p': | |
443 | mode.mode |= MODE_PRIVATE; | |
444 | break; | |
445 | case 's': | |
446 | mode.mode |= MODE_SECRET; | |
447 | break; | |
448 | case 'm': | |
449 | mode.mode |= MODE_MODERATED; | |
450 | break; | |
451 | case 't': | |
452 | mode.mode |= MODE_TOPICLIMIT; | |
453 | break; | |
454 | case 'r': | |
455 | mode.mode |= MODE_REGONLY; | |
456 | break; | |
457 | case 'L': | |
458 | mode.mode |= MODE_EXLIMIT; | |
459 | break; | |
460 | case 'P': | |
461 | mode.mode |= MODE_PERMANENT; | |
462 | break; | |
463 | case 'c': | |
464 | mode.mode |= MODE_NOCOLOR; | |
465 | break; | |
466 | case 'g': | |
467 | mode.mode |= MODE_FREEINVITE; | |
468 | break; | |
469 | case 'z': | |
470 | mode.mode |= MODE_OPMODERATE; | |
471 | break; | |
472 | case 'F': | |
473 | mode.mode |= MODE_FREETARGET; | |
474 | break; | |
475 | case 'Q': | |
476 | mode.mode |= MODE_DISFORWARD; | |
477 | break; | |
478 | case 'f': | |
479 | if(parc < 5 + args) | |
480 | return 0; | |
481 | strlcpy(mode.forward, parv[4 + args], sizeof(mode.forward)); | |
482 | args++; | |
483 | break; | |
484 | case 'j': | |
485 | /* sent a +j without an arg. */ | |
486 | if(parc < 5 + args) | |
487 | return 0; | |
488 | sscanf(parv[4 + args], "%d:%d", &mode.join_num, &mode.join_time); | |
489 | args++; | |
490 | break; | |
491 | case 'k': | |
492 | /* sent a +k without a key, eek. */ | |
493 | if(parc < 5 + args) | |
494 | return 0; | |
495 | strlcpy(mode.key, parv[4 + args], sizeof(mode.key)); | |
496 | args++; | |
497 | break; | |
498 | case 'l': | |
499 | /* sent a +l without a limit. */ | |
500 | if(parc < 5 + args) | |
501 | return 0; | |
502 | mode.limit = atoi(parv[4 + args]); | |
503 | args++; | |
504 | break; | |
505 | } | |
506 | } | |
507 | ||
508 | if((chptr = get_or_create_channel(source_p, parv[2], &isnew)) == NULL) | |
509 | return 0; | |
510 | ||
511 | newts = atol(parv[1]); | |
512 | oldts = chptr->channelts; | |
513 | oldmode = &chptr->mode; | |
514 | ||
515 | #ifdef IGNORE_BOGUS_TS | |
516 | if(newts < 800000000) | |
517 | { | |
518 | sendto_realops_snomask(SNO_DEBUG, L_ALL, | |
519 | "*** Bogus TS %ld on %s ignored from %s", | |
520 | (long) newts, chptr->chname, client_p->name); | |
521 | newts = (oldts == 0) ? oldts : 800000000; | |
522 | } | |
523 | #else | |
524 | /* making a channel TS0 */ | |
525 | if(!isnew && !newts && oldts) | |
526 | { | |
527 | sendto_channel_local(ALL_MEMBERS, chptr, | |
528 | ":%s NOTICE %s :*** Notice -- TS for %s changed from %ld to 0", | |
529 | me.name, chptr->chname, chptr->chname, (long) oldts); | |
530 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
531 | "Server %s changing TS on %s from %ld to 0", | |
532 | source_p->name, chptr->chname, (long) oldts); | |
533 | } | |
534 | #endif | |
535 | ||
536 | if(isnew) | |
537 | chptr->channelts = newts; | |
538 | else if(newts == 0 || oldts == 0) | |
539 | chptr->channelts = 0; | |
540 | else if(newts == oldts) | |
541 | ; | |
542 | else if(newts < oldts) | |
543 | { | |
544 | keep_our_modes = NO; | |
545 | chptr->channelts = newts; | |
546 | } | |
547 | else | |
548 | keep_new_modes = NO; | |
549 | ||
550 | if(!keep_new_modes) | |
551 | mode = *oldmode; | |
552 | else if(keep_our_modes) | |
553 | { | |
554 | mode.mode |= oldmode->mode; | |
555 | if(oldmode->limit > mode.limit) | |
556 | mode.limit = oldmode->limit; | |
557 | if(strcmp(mode.key, oldmode->key) < 0) | |
558 | strcpy(mode.key, oldmode->key); | |
559 | if(oldmode->join_num > mode.join_num || | |
560 | (oldmode->join_num == mode.join_num && | |
561 | oldmode->join_time > mode.join_time)) | |
562 | { | |
563 | mode.join_num = oldmode->join_num; | |
564 | mode.join_time = oldmode->join_time; | |
565 | } | |
566 | if(irccmp(mode.forward, oldmode->forward) < 0) | |
567 | strcpy(mode.forward, oldmode->forward); | |
568 | } | |
569 | else | |
570 | { | |
571 | /* If setting -j, clear join throttle state -- jilles */ | |
572 | if (!mode.join_num) | |
573 | chptr->join_count = chptr->join_delta = 0; | |
574 | } | |
575 | ||
576 | set_final_mode(&mode, oldmode); | |
577 | chptr->mode = mode; | |
578 | ||
579 | /* Lost the TS, other side wins, so remove modes on this side */ | |
580 | if(!keep_our_modes) | |
581 | { | |
582 | remove_our_modes(chptr, source_p); | |
583 | DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->invites.head) | |
584 | { | |
585 | del_invite(chptr, ptr->data); | |
586 | } | |
587 | sendto_channel_local(ALL_MEMBERS, chptr, | |
588 | ":%s NOTICE %s :*** Notice -- TS for %s changed from %ld to %ld", | |
589 | me.name, chptr->chname, chptr->chname, | |
590 | (long) oldts, (long) newts); | |
591 | } | |
592 | ||
593 | if(*modebuf != '\0') | |
594 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s MODE %s %s %s", | |
595 | source_p->user->server, chptr->chname, modebuf, parabuf); | |
596 | ||
597 | *modebuf = *parabuf = '\0'; | |
598 | ||
599 | if(!IsMember(source_p, chptr)) | |
600 | { | |
601 | add_user_to_channel(chptr, source_p, CHFL_PEON); | |
602 | if (chptr->mode.join_num && | |
603 | CurrentTime - chptr->join_delta >= chptr->mode.join_time) | |
604 | { | |
605 | chptr->join_count = 0; | |
606 | chptr->join_delta = CurrentTime; | |
607 | } | |
608 | chptr->join_count++; | |
609 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", | |
610 | source_p->name, source_p->username, | |
611 | source_p->host, chptr->chname); | |
612 | } | |
613 | ||
614 | modes = channel_modes(chptr, client_p); | |
615 | sendto_server(client_p, chptr, CAP_TS6, NOCAPS, | |
616 | ":%s JOIN %ld %s %s", | |
617 | source_p->id, (long) chptr->channelts, chptr->chname, modes); | |
618 | sendto_server(client_p, chptr, NOCAPS, CAP_TS6, | |
619 | ":%s SJOIN %ld %s %s :%s", | |
620 | source_p->user->server, (long) chptr->channelts, | |
621 | chptr->chname, modes, source_p->name); | |
622 | return 0; | |
623 | } | |
624 | ||
625 | /* | |
626 | * do_join_0 | |
627 | * | |
628 | * inputs - pointer to client doing join 0 | |
629 | * output - NONE | |
630 | * side effects - Use has decided to join 0. This is legacy | |
631 | * from the days when channels were numbers not names. *sigh* | |
632 | * There is a bunch of evilness necessary here due to | |
633 | * anti spambot code. | |
634 | */ | |
635 | static void | |
636 | do_join_0(struct Client *client_p, struct Client *source_p) | |
637 | { | |
638 | struct membership *msptr; | |
639 | struct Channel *chptr = NULL; | |
640 | dlink_node *ptr; | |
641 | ||
642 | /* Finish the flood grace period... */ | |
643 | if(MyClient(source_p) && !IsFloodDone(source_p)) | |
644 | flood_endgrace(source_p); | |
645 | ||
646 | ||
647 | sendto_server(client_p, NULL, NOCAPS, NOCAPS, ":%s JOIN 0", source_p->name); | |
648 | ||
649 | if(source_p->user->channel.head && MyConnect(source_p) && | |
650 | !IsOper(source_p) && !IsExemptSpambot(source_p)) | |
651 | check_spambot_warning(source_p, NULL); | |
652 | ||
653 | while((ptr = source_p->user->channel.head)) | |
654 | { | |
655 | msptr = ptr->data; | |
656 | chptr = msptr->chptr; | |
657 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s", | |
658 | source_p->name, | |
659 | source_p->username, source_p->host, chptr->chname); | |
660 | remove_user_from_channel(msptr); | |
661 | } | |
662 | } | |
663 | ||
664 | static int | |
665 | check_channel_name_loc(struct Client *source_p, const char *name) | |
666 | { | |
667 | s_assert(name != NULL); | |
668 | if(EmptyString(name)) | |
669 | return 0; | |
670 | ||
671 | if(ConfigFileEntry.disable_fake_channels && !IsOper(source_p)) | |
672 | { | |
673 | for(; *name; ++name) | |
674 | { | |
675 | if(!IsChanChar(*name) || IsFakeChanChar(*name)) | |
676 | return 0; | |
677 | } | |
678 | } | |
679 | else | |
680 | { | |
681 | for(; *name; ++name) | |
682 | { | |
683 | if(!IsChanChar(*name)) | |
684 | return 0; | |
685 | } | |
686 | } | |
687 | ||
688 | return 1; | |
689 | } | |
690 | ||
691 | struct mode_letter | |
692 | { | |
693 | int mode; | |
694 | char letter; | |
695 | }; | |
696 | ||
697 | static struct mode_letter flags[] = { | |
698 | {MODE_NOPRIVMSGS, 'n'}, | |
699 | {MODE_TOPICLIMIT, 't'}, | |
700 | {MODE_SECRET, 's'}, | |
701 | {MODE_MODERATED, 'm'}, | |
702 | {MODE_INVITEONLY, 'i'}, | |
703 | {MODE_PRIVATE, 'p'}, | |
704 | {MODE_REGONLY, 'r'}, | |
705 | {MODE_EXLIMIT, 'L'}, | |
706 | {MODE_PERMANENT, 'P'}, | |
707 | {MODE_NOCOLOR, 'c'}, | |
708 | {MODE_FREEINVITE, 'g'}, | |
709 | {MODE_OPMODERATE, 'z'}, | |
710 | {MODE_FREETARGET, 'F'}, | |
711 | {MODE_DISFORWARD, 'Q'}, | |
712 | {0, 0} | |
713 | }; | |
714 | ||
715 | static void | |
716 | set_final_mode(struct Mode *mode, struct Mode *oldmode) | |
717 | { | |
718 | int dir = MODE_QUERY; | |
719 | char *pbuf = parabuf; | |
720 | int len; | |
721 | int i; | |
722 | ||
723 | /* ok, first get a list of modes we need to add */ | |
724 | for(i = 0; flags[i].letter; i++) | |
725 | { | |
726 | if((mode->mode & flags[i].mode) && !(oldmode->mode & flags[i].mode)) | |
727 | { | |
728 | if(dir != MODE_ADD) | |
729 | { | |
730 | *mbuf++ = '+'; | |
731 | dir = MODE_ADD; | |
732 | } | |
733 | *mbuf++ = flags[i].letter; | |
734 | } | |
735 | } | |
736 | ||
737 | /* now the ones we need to remove. */ | |
738 | for(i = 0; flags[i].letter; i++) | |
739 | { | |
740 | if((oldmode->mode & flags[i].mode) && !(mode->mode & flags[i].mode)) | |
741 | { | |
742 | if(dir != MODE_DEL) | |
743 | { | |
744 | *mbuf++ = '-'; | |
745 | dir = MODE_DEL; | |
746 | } | |
747 | *mbuf++ = flags[i].letter; | |
748 | } | |
749 | } | |
750 | ||
751 | if(oldmode->limit && !mode->limit) | |
752 | { | |
753 | if(dir != MODE_DEL) | |
754 | { | |
755 | *mbuf++ = '-'; | |
756 | dir = MODE_DEL; | |
757 | } | |
758 | *mbuf++ = 'l'; | |
759 | } | |
760 | if(oldmode->key[0] && !mode->key[0]) | |
761 | { | |
762 | if(dir != MODE_DEL) | |
763 | { | |
764 | *mbuf++ = '-'; | |
765 | dir = MODE_DEL; | |
766 | } | |
767 | *mbuf++ = 'k'; | |
768 | len = ircsprintf(pbuf, "%s ", oldmode->key); | |
769 | pbuf += len; | |
770 | } | |
771 | if(oldmode->join_num && !mode->join_num) | |
772 | { | |
773 | if(dir != MODE_DEL) | |
774 | { | |
775 | *mbuf++ = '-'; | |
776 | dir = MODE_DEL; | |
777 | } | |
778 | *mbuf++ = 'j'; | |
779 | } | |
780 | if(oldmode->forward[0] && !mode->forward[0]) | |
781 | { | |
782 | if(dir != MODE_DEL) | |
783 | { | |
784 | *mbuf++ = '-'; | |
785 | dir = MODE_DEL; | |
786 | } | |
787 | *mbuf++ = 'f'; | |
788 | } | |
789 | if(mode->limit && oldmode->limit != mode->limit) | |
790 | { | |
791 | if(dir != MODE_ADD) | |
792 | { | |
793 | *mbuf++ = '+'; | |
794 | dir = MODE_ADD; | |
795 | } | |
796 | *mbuf++ = 'l'; | |
797 | len = ircsprintf(pbuf, "%d ", mode->limit); | |
798 | pbuf += len; | |
799 | } | |
800 | if(mode->key[0] && strcmp(oldmode->key, mode->key)) | |
801 | { | |
802 | if(dir != MODE_ADD) | |
803 | { | |
804 | *mbuf++ = '+'; | |
805 | dir = MODE_ADD; | |
806 | } | |
807 | *mbuf++ = 'k'; | |
808 | len = ircsprintf(pbuf, "%s ", mode->key); | |
809 | pbuf += len; | |
810 | } | |
811 | if(mode->join_num && (oldmode->join_num != mode->join_num || oldmode->join_time != mode->join_time)) | |
812 | { | |
813 | if(dir != MODE_ADD) | |
814 | { | |
815 | *mbuf++ = '+'; | |
816 | dir = MODE_ADD; | |
817 | } | |
818 | *mbuf++ = 'j'; | |
819 | len = ircsprintf(pbuf, "%d:%d ", mode->join_num, mode->join_time); | |
820 | pbuf += len; | |
821 | } | |
822 | if(mode->forward[0] && strcmp(oldmode->forward, mode->forward) && ConfigChannel.use_forward) | |
823 | { | |
824 | if(dir != MODE_ADD) | |
825 | { | |
826 | *mbuf++ = '+'; | |
827 | dir = MODE_ADD; | |
828 | } | |
829 | *mbuf++ = 'f'; | |
830 | len = ircsprintf(pbuf, "%s ", mode->forward); | |
831 | pbuf += len; | |
832 | } | |
833 | *mbuf = '\0'; | |
834 | } | |
835 | ||
836 | /* | |
837 | * remove_our_modes | |
838 | * | |
839 | * inputs - | |
840 | * output - | |
841 | * side effects - | |
842 | */ | |
843 | static void | |
844 | remove_our_modes(struct Channel *chptr, struct Client *source_p) | |
845 | { | |
846 | struct membership *msptr; | |
847 | dlink_node *ptr; | |
848 | char lmodebuf[MODEBUFLEN]; | |
849 | char *lpara[MAXMODEPARAMS]; | |
850 | int count = 0; | |
851 | int i; | |
852 | ||
853 | mbuf = lmodebuf; | |
854 | *mbuf++ = '-'; | |
855 | ||
856 | for(i = 0; i < MAXMODEPARAMS; i++) | |
857 | lpara[i] = NULL; | |
858 | ||
859 | DLINK_FOREACH(ptr, chptr->members.head) | |
860 | { | |
861 | msptr = ptr->data; | |
862 | ||
863 | if(is_chanop(msptr)) | |
864 | { | |
865 | msptr->flags &= ~CHFL_CHANOP; | |
866 | lpara[count++] = msptr->client_p->name; | |
867 | *mbuf++ = 'o'; | |
868 | ||
869 | /* +ov, might not fit so check. */ | |
870 | if(is_voiced(msptr)) | |
871 | { | |
872 | if(count >= MAXMODEPARAMS) | |
873 | { | |
874 | *mbuf = '\0'; | |
875 | sendto_channel_local(ALL_MEMBERS, chptr, | |
876 | ":%s MODE %s %s %s %s %s %s", | |
877 | me.name, chptr->chname, | |
878 | lmodebuf, lpara[0], lpara[1], | |
879 | lpara[2], lpara[3]); | |
880 | ||
881 | /* preserve the initial '-' */ | |
882 | mbuf = lmodebuf; | |
883 | *mbuf++ = '-'; | |
884 | count = 0; | |
885 | ||
886 | for(i = 0; i < MAXMODEPARAMS; i++) | |
887 | lpara[i] = NULL; | |
888 | } | |
889 | ||
890 | msptr->flags &= ~CHFL_VOICE; | |
891 | lpara[count++] = msptr->client_p->name; | |
892 | *mbuf++ = 'v'; | |
893 | } | |
894 | } | |
895 | else if(is_voiced(msptr)) | |
896 | { | |
897 | msptr->flags &= ~CHFL_VOICE; | |
898 | lpara[count++] = msptr->client_p->name; | |
899 | *mbuf++ = 'v'; | |
900 | } | |
901 | else | |
902 | continue; | |
903 | ||
904 | if(count >= MAXMODEPARAMS) | |
905 | { | |
906 | *mbuf = '\0'; | |
907 | sendto_channel_local(ALL_MEMBERS, chptr, | |
908 | ":%s MODE %s %s %s %s %s %s", | |
909 | me.name, chptr->chname, lmodebuf, | |
910 | lpara[0], lpara[1], lpara[2], lpara[3]); | |
911 | mbuf = lmodebuf; | |
912 | *mbuf++ = '-'; | |
913 | count = 0; | |
914 | ||
915 | for(i = 0; i < MAXMODEPARAMS; i++) | |
916 | lpara[i] = NULL; | |
917 | } | |
918 | } | |
919 | ||
920 | if(count != 0) | |
921 | { | |
922 | *mbuf = '\0'; | |
923 | sendto_channel_local(ALL_MEMBERS, chptr, | |
924 | ":%s MODE %s %s %s %s %s %s", | |
925 | me.name, chptr->chname, lmodebuf, | |
926 | EmptyString(lpara[0]) ? "" : lpara[0], | |
927 | EmptyString(lpara[1]) ? "" : lpara[1], | |
928 | EmptyString(lpara[2]) ? "" : lpara[2], | |
929 | EmptyString(lpara[3]) ? "" : lpara[3]); | |
930 | ||
931 | } | |
932 | } |