]>
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" | |
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 | ||
f4a80ce6 | 65 | DECLARE_MODULE_AV1(join, NULL, NULL, join_clist, join_hlist, NULL, "$Revision: 3494 $"); |
212380e3 | 66 | |
f4a80ce6 | 67 | static void do_join_0(struct Client *client_p, struct Client *source_p); |
212380e3 | 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 | ||
f4a80ce6 | 152 | /* join 0 parts all channels */ |
153 | if(*name == '0' && (name[1] == ',' || name[1] == '\0') && name == chanlist) | |
154 | { | |
155 | (void) strcpy(jbuf, "0"); | |
156 | continue; | |
157 | } | |
158 | ||
212380e3 | 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, | |
63aecfb9 | 178 | source_p->orighost, name, aconf->passwd); |
212380e3 | 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 | ||
f4a80ce6 | 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 | ||
212380e3 | 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 | ||
544cde90 | 239 | if(moduledata.approved != 0) |
212380e3 | 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 | ||
08d11e34 | 257 | if((rb_dlink_list_length(&source_p->user->channel) >= |
212380e3 | 258 | (unsigned long) ConfigChannel.max_chans_per_user) && |
259 | (!IsOper(source_p) || | |
08d11e34 | 260 | (rb_dlink_list_length(&source_p->user->channel) >= |
212380e3 | 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 | { | |
212380e3 | 345 | sendto_server(client_p, chptr, CAP_TS6, NOCAPS, |
bee3b671 | 346 | ":%s JOIN %ld %s +", |
212380e3 | 347 | use_id(source_p), (long) chptr->channelts, |
bee3b671 | 348 | chptr->chname); |
212380e3 | 349 | |
350 | sendto_server(client_p, chptr, NOCAPS, CAP_TS6, | |
bee3b671 | 351 | ":%s SJOIN %ld %s + :%s", |
212380e3 | 352 | me.name, (long) chptr->channelts, |
bee3b671 | 353 | chptr->chname, source_p->name); |
212380e3 | 354 | } |
355 | ||
356 | del_invite(chptr, source_p); | |
357 | ||
358 | if(chptr->topic != NULL) | |
359 | { | |
360 | sendto_one(source_p, form_str(RPL_TOPIC), me.name, | |
361 | source_p->name, chptr->chname, chptr->topic); | |
362 | ||
363 | sendto_one(source_p, form_str(RPL_TOPICWHOTIME), | |
364 | me.name, source_p->name, chptr->chname, | |
365 | chptr->topic_info, chptr->topic_time); | |
366 | } | |
367 | ||
368 | channel_member_names(chptr, source_p, 1); | |
369 | ||
370 | if(successful_join_count) | |
371 | source_p->localClient->last_join_time = CurrentTime; | |
372 | ||
373 | hook_info.client = source_p; | |
374 | hook_info.chptr = chptr; | |
375 | hook_info.key = key; | |
376 | call_hook(h_channel_join, &hook_info); | |
377 | } | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | /* | |
383 | * ms_join | |
384 | * | |
385 | * inputs - | |
386 | * output - none | |
387 | * side effects - handles remote JOIN's sent by servers. In TSora | |
388 | * remote clients are joined using SJOIN, hence a | |
389 | * JOIN sent by a server on behalf of a client is an error. | |
390 | * here, the initial code is in to take an extra parameter | |
391 | * and use it for the TimeStamp on a new channel. | |
392 | */ | |
393 | static int | |
394 | ms_join(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
395 | { | |
396 | struct Channel *chptr; | |
bee3b671 | 397 | static struct Mode mode; |
212380e3 | 398 | time_t oldts; |
399 | time_t newts; | |
400 | int isnew; | |
212380e3 | 401 | int keep_our_modes = YES; |
402 | int keep_new_modes = YES; | |
08d11e34 | 403 | rb_dlink_node *ptr, *next_ptr; |
212380e3 | 404 | |
f4a80ce6 | 405 | /* special case for join 0 */ |
406 | if((parv[1][0] == '0') && (parv[1][1] == '\0') && parc == 2) | |
407 | { | |
408 | do_join_0(client_p, source_p); | |
409 | return 0; | |
410 | } | |
411 | ||
212380e3 | 412 | if(parc < 4) |
413 | return 0; | |
414 | ||
415 | if(!IsChannelName(parv[2]) || !check_channel_name(parv[2])) | |
416 | return 0; | |
417 | ||
418 | /* joins for local channels cant happen. */ | |
419 | if(parv[2][0] == '&') | |
420 | return 0; | |
421 | ||
422 | mbuf = modebuf; | |
423 | mode.key[0] = mode.forward[0] = '\0'; | |
424 | mode.mode = mode.limit = mode.join_num = mode.join_time = 0; | |
425 | ||
212380e3 | 426 | if((chptr = get_or_create_channel(source_p, parv[2], &isnew)) == NULL) |
427 | return 0; | |
428 | ||
429 | newts = atol(parv[1]); | |
430 | oldts = chptr->channelts; | |
212380e3 | 431 | |
432 | #ifdef IGNORE_BOGUS_TS | |
433 | if(newts < 800000000) | |
434 | { | |
435 | sendto_realops_snomask(SNO_DEBUG, L_ALL, | |
436 | "*** Bogus TS %ld on %s ignored from %s", | |
437 | (long) newts, chptr->chname, client_p->name); | |
438 | newts = (oldts == 0) ? oldts : 800000000; | |
439 | } | |
440 | #else | |
441 | /* making a channel TS0 */ | |
442 | if(!isnew && !newts && oldts) | |
443 | { | |
444 | sendto_channel_local(ALL_MEMBERS, chptr, | |
445 | ":%s NOTICE %s :*** Notice -- TS for %s changed from %ld to 0", | |
446 | me.name, chptr->chname, chptr->chname, (long) oldts); | |
447 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
448 | "Server %s changing TS on %s from %ld to 0", | |
449 | source_p->name, chptr->chname, (long) oldts); | |
450 | } | |
451 | #endif | |
452 | ||
453 | if(isnew) | |
454 | chptr->channelts = newts; | |
455 | else if(newts == 0 || oldts == 0) | |
456 | chptr->channelts = 0; | |
457 | else if(newts == oldts) | |
458 | ; | |
459 | else if(newts < oldts) | |
460 | { | |
461 | keep_our_modes = NO; | |
462 | chptr->channelts = newts; | |
463 | } | |
464 | else | |
465 | keep_new_modes = NO; | |
466 | ||
212380e3 | 467 | /* Lost the TS, other side wins, so remove modes on this side */ |
468 | if(!keep_our_modes) | |
469 | { | |
bee3b671 | 470 | set_final_mode(&mode, &chptr->mode); |
471 | chptr->mode = mode; | |
212380e3 | 472 | remove_our_modes(chptr, source_p); |
08d11e34 | 473 | RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->invites.head) |
212380e3 | 474 | { |
475 | del_invite(chptr, ptr->data); | |
476 | } | |
bee3b671 | 477 | /* If setting -j, clear join throttle state -- jilles */ |
478 | chptr->join_count = chptr->join_delta = 0; | |
212380e3 | 479 | sendto_channel_local(ALL_MEMBERS, chptr, |
480 | ":%s NOTICE %s :*** Notice -- TS for %s changed from %ld to %ld", | |
481 | me.name, chptr->chname, chptr->chname, | |
482 | (long) oldts, (long) newts); | |
1117fbd3 | 483 | /* Update capitalization in channel name, this makes the |
484 | * capitalization timestamped like modes are -- jilles */ | |
485 | strcpy(chptr->chname, parv[2]); | |
bee3b671 | 486 | if(*modebuf != '\0') |
487 | sendto_channel_local(ALL_MEMBERS, chptr, | |
488 | ":%s MODE %s %s %s", | |
489 | source_p->servptr->name, | |
490 | chptr->chname, modebuf, parabuf); | |
491 | *modebuf = *parabuf = '\0'; | |
212380e3 | 492 | } |
493 | ||
212380e3 | 494 | if(!IsMember(source_p, chptr)) |
495 | { | |
496 | add_user_to_channel(chptr, source_p, CHFL_PEON); | |
497 | if (chptr->mode.join_num && | |
498 | CurrentTime - chptr->join_delta >= chptr->mode.join_time) | |
499 | { | |
500 | chptr->join_count = 0; | |
501 | chptr->join_delta = CurrentTime; | |
502 | } | |
503 | chptr->join_count++; | |
504 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s", | |
505 | source_p->name, source_p->username, | |
506 | source_p->host, chptr->chname); | |
507 | } | |
508 | ||
212380e3 | 509 | sendto_server(client_p, chptr, CAP_TS6, NOCAPS, |
bee3b671 | 510 | ":%s JOIN %ld %s +", |
511 | source_p->id, (long) chptr->channelts, chptr->chname); | |
212380e3 | 512 | sendto_server(client_p, chptr, NOCAPS, CAP_TS6, |
513 | ":%s SJOIN %ld %s %s :%s", | |
c88cdb00 | 514 | source_p->servptr->name, (long) chptr->channelts, |
bee3b671 | 515 | chptr->chname, keep_new_modes ? "+" : "0", |
516 | source_p->name); | |
212380e3 | 517 | return 0; |
518 | } | |
519 | ||
f4a80ce6 | 520 | /* |
521 | * do_join_0 | |
522 | * | |
523 | * inputs - pointer to client doing join 0 | |
524 | * output - NONE | |
525 | * side effects - Use has decided to join 0. This is legacy | |
526 | * from the days when channels were numbers not names. *sigh* | |
527 | * There is a bunch of evilness necessary here due to | |
528 | * anti spambot code. | |
529 | */ | |
530 | static void | |
531 | do_join_0(struct Client *client_p, struct Client *source_p) | |
532 | { | |
533 | struct membership *msptr; | |
534 | struct Channel *chptr = NULL; | |
08d11e34 | 535 | rb_dlink_node *ptr; |
f4a80ce6 | 536 | |
537 | /* Finish the flood grace period... */ | |
538 | if(MyClient(source_p) && !IsFloodDone(source_p)) | |
539 | flood_endgrace(source_p); | |
540 | ||
541 | ||
542 | sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s JOIN 0", use_id(source_p)); | |
543 | sendto_server(client_p, NULL, NOCAPS, CAP_TS6, ":%s JOIN 0", source_p->name); | |
544 | ||
545 | if(source_p->user->channel.head && MyConnect(source_p) && | |
546 | !IsOper(source_p) && !IsExemptSpambot(source_p)) | |
547 | check_spambot_warning(source_p, NULL); | |
548 | ||
549 | while((ptr = source_p->user->channel.head)) | |
550 | { | |
551 | msptr = ptr->data; | |
552 | chptr = msptr->chptr; | |
553 | sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s", | |
554 | source_p->name, | |
555 | source_p->username, source_p->host, chptr->chname); | |
556 | remove_user_from_channel(msptr); | |
557 | } | |
558 | } | |
559 | ||
212380e3 | 560 | static int |
561 | check_channel_name_loc(struct Client *source_p, const char *name) | |
562 | { | |
563 | s_assert(name != NULL); | |
564 | if(EmptyString(name)) | |
565 | return 0; | |
566 | ||
567 | if(ConfigFileEntry.disable_fake_channels && !IsOper(source_p)) | |
568 | { | |
569 | for(; *name; ++name) | |
570 | { | |
571 | if(!IsChanChar(*name) || IsFakeChanChar(*name)) | |
572 | return 0; | |
573 | } | |
574 | } | |
575 | else | |
576 | { | |
577 | for(; *name; ++name) | |
578 | { | |
579 | if(!IsChanChar(*name)) | |
580 | return 0; | |
581 | } | |
582 | } | |
583 | ||
584 | return 1; | |
585 | } | |
586 | ||
212380e3 | 587 | static void |
588 | set_final_mode(struct Mode *mode, struct Mode *oldmode) | |
589 | { | |
590 | int dir = MODE_QUERY; | |
591 | char *pbuf = parabuf; | |
592 | int len; | |
593 | int i; | |
594 | ||
595 | /* ok, first get a list of modes we need to add */ | |
2a719c44 | 596 | for(i = 0; chmode_flags[i].letter; i++) |
212380e3 | 597 | { |
2a719c44 | 598 | if((mode->mode & chmode_flags[i].mode) && !(oldmode->mode & chmode_flags[i].mode)) |
212380e3 | 599 | { |
600 | if(dir != MODE_ADD) | |
601 | { | |
602 | *mbuf++ = '+'; | |
603 | dir = MODE_ADD; | |
604 | } | |
2a719c44 | 605 | *mbuf++ = chmode_flags[i].letter; |
212380e3 | 606 | } |
607 | } | |
608 | ||
609 | /* now the ones we need to remove. */ | |
2a719c44 | 610 | for(i = 0; chmode_flags[i].letter; i++) |
212380e3 | 611 | { |
2a719c44 | 612 | if((oldmode->mode & chmode_flags[i].mode) && !(mode->mode & chmode_flags[i].mode)) |
212380e3 | 613 | { |
614 | if(dir != MODE_DEL) | |
615 | { | |
616 | *mbuf++ = '-'; | |
617 | dir = MODE_DEL; | |
618 | } | |
2a719c44 | 619 | *mbuf++ = chmode_flags[i].letter; |
212380e3 | 620 | } |
621 | } | |
622 | ||
623 | if(oldmode->limit && !mode->limit) | |
624 | { | |
625 | if(dir != MODE_DEL) | |
626 | { | |
627 | *mbuf++ = '-'; | |
628 | dir = MODE_DEL; | |
629 | } | |
630 | *mbuf++ = 'l'; | |
631 | } | |
632 | if(oldmode->key[0] && !mode->key[0]) | |
633 | { | |
634 | if(dir != MODE_DEL) | |
635 | { | |
636 | *mbuf++ = '-'; | |
637 | dir = MODE_DEL; | |
638 | } | |
639 | *mbuf++ = 'k'; | |
581fa5c4 | 640 | len = rb_sprintf(pbuf, "%s ", oldmode->key); |
212380e3 | 641 | pbuf += len; |
642 | } | |
643 | if(oldmode->join_num && !mode->join_num) | |
644 | { | |
645 | if(dir != MODE_DEL) | |
646 | { | |
647 | *mbuf++ = '-'; | |
648 | dir = MODE_DEL; | |
649 | } | |
650 | *mbuf++ = 'j'; | |
651 | } | |
652 | if(oldmode->forward[0] && !mode->forward[0]) | |
653 | { | |
654 | if(dir != MODE_DEL) | |
655 | { | |
656 | *mbuf++ = '-'; | |
657 | dir = MODE_DEL; | |
658 | } | |
659 | *mbuf++ = 'f'; | |
660 | } | |
661 | if(mode->limit && oldmode->limit != mode->limit) | |
662 | { | |
663 | if(dir != MODE_ADD) | |
664 | { | |
665 | *mbuf++ = '+'; | |
666 | dir = MODE_ADD; | |
667 | } | |
668 | *mbuf++ = 'l'; | |
581fa5c4 | 669 | len = rb_sprintf(pbuf, "%d ", mode->limit); |
212380e3 | 670 | pbuf += len; |
671 | } | |
672 | if(mode->key[0] && strcmp(oldmode->key, mode->key)) | |
673 | { | |
674 | if(dir != MODE_ADD) | |
675 | { | |
676 | *mbuf++ = '+'; | |
677 | dir = MODE_ADD; | |
678 | } | |
679 | *mbuf++ = 'k'; | |
581fa5c4 | 680 | len = rb_sprintf(pbuf, "%s ", mode->key); |
212380e3 | 681 | pbuf += len; |
682 | } | |
683 | if(mode->join_num && (oldmode->join_num != mode->join_num || oldmode->join_time != mode->join_time)) | |
684 | { | |
685 | if(dir != MODE_ADD) | |
686 | { | |
687 | *mbuf++ = '+'; | |
688 | dir = MODE_ADD; | |
689 | } | |
690 | *mbuf++ = 'j'; | |
581fa5c4 | 691 | len = rb_sprintf(pbuf, "%d:%d ", mode->join_num, mode->join_time); |
212380e3 | 692 | pbuf += len; |
693 | } | |
694 | if(mode->forward[0] && strcmp(oldmode->forward, mode->forward) && ConfigChannel.use_forward) | |
695 | { | |
696 | if(dir != MODE_ADD) | |
697 | { | |
698 | *mbuf++ = '+'; | |
699 | dir = MODE_ADD; | |
700 | } | |
701 | *mbuf++ = 'f'; | |
581fa5c4 | 702 | len = rb_sprintf(pbuf, "%s ", mode->forward); |
212380e3 | 703 | pbuf += len; |
704 | } | |
705 | *mbuf = '\0'; | |
706 | } | |
707 | ||
708 | /* | |
709 | * remove_our_modes | |
710 | * | |
711 | * inputs - | |
712 | * output - | |
713 | * side effects - | |
714 | */ | |
715 | static void | |
716 | remove_our_modes(struct Channel *chptr, struct Client *source_p) | |
717 | { | |
718 | struct membership *msptr; | |
08d11e34 | 719 | rb_dlink_node *ptr; |
212380e3 | 720 | char lmodebuf[MODEBUFLEN]; |
721 | char *lpara[MAXMODEPARAMS]; | |
722 | int count = 0; | |
723 | int i; | |
724 | ||
725 | mbuf = lmodebuf; | |
726 | *mbuf++ = '-'; | |
727 | ||
728 | for(i = 0; i < MAXMODEPARAMS; i++) | |
729 | lpara[i] = NULL; | |
730 | ||
08d11e34 | 731 | RB_DLINK_FOREACH(ptr, chptr->members.head) |
212380e3 | 732 | { |
733 | msptr = ptr->data; | |
734 | ||
735 | if(is_chanop(msptr)) | |
736 | { | |
737 | msptr->flags &= ~CHFL_CHANOP; | |
738 | lpara[count++] = msptr->client_p->name; | |
739 | *mbuf++ = 'o'; | |
740 | ||
741 | /* +ov, might not fit so check. */ | |
742 | if(is_voiced(msptr)) | |
743 | { | |
744 | if(count >= MAXMODEPARAMS) | |
745 | { | |
746 | *mbuf = '\0'; | |
747 | sendto_channel_local(ALL_MEMBERS, chptr, | |
748 | ":%s MODE %s %s %s %s %s %s", | |
749 | me.name, chptr->chname, | |
750 | lmodebuf, lpara[0], lpara[1], | |
751 | lpara[2], lpara[3]); | |
752 | ||
753 | /* preserve the initial '-' */ | |
754 | mbuf = lmodebuf; | |
755 | *mbuf++ = '-'; | |
756 | count = 0; | |
757 | ||
758 | for(i = 0; i < MAXMODEPARAMS; i++) | |
759 | lpara[i] = NULL; | |
760 | } | |
761 | ||
762 | msptr->flags &= ~CHFL_VOICE; | |
763 | lpara[count++] = msptr->client_p->name; | |
764 | *mbuf++ = 'v'; | |
765 | } | |
766 | } | |
767 | else if(is_voiced(msptr)) | |
768 | { | |
769 | msptr->flags &= ~CHFL_VOICE; | |
770 | lpara[count++] = msptr->client_p->name; | |
771 | *mbuf++ = 'v'; | |
772 | } | |
773 | else | |
774 | continue; | |
775 | ||
776 | if(count >= MAXMODEPARAMS) | |
777 | { | |
778 | *mbuf = '\0'; | |
779 | sendto_channel_local(ALL_MEMBERS, chptr, | |
780 | ":%s MODE %s %s %s %s %s %s", | |
781 | me.name, chptr->chname, lmodebuf, | |
782 | lpara[0], lpara[1], lpara[2], lpara[3]); | |
783 | mbuf = lmodebuf; | |
784 | *mbuf++ = '-'; | |
785 | count = 0; | |
786 | ||
787 | for(i = 0; i < MAXMODEPARAMS; i++) | |
788 | lpara[i] = NULL; | |
789 | } | |
790 | } | |
791 | ||
792 | if(count != 0) | |
793 | { | |
794 | *mbuf = '\0'; | |
795 | sendto_channel_local(ALL_MEMBERS, chptr, | |
796 | ":%s MODE %s %s %s %s %s %s", | |
797 | me.name, chptr->chname, lmodebuf, | |
798 | EmptyString(lpara[0]) ? "" : lpara[0], | |
799 | EmptyString(lpara[1]) ? "" : lpara[1], | |
800 | EmptyString(lpara[2]) ? "" : lpara[2], | |
801 | EmptyString(lpara[3]) ? "" : lpara[3]); | |
802 | ||
803 | } | |
804 | } |