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