]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
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 AC |
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 AC |
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 JT |
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 AC |
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 AC |
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 JT |
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 AC |
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 | { | |
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 AC |
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 AC |
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 AC |
398 | time_t oldts; |
399 | time_t newts; | |
400 | int isnew; | |
212380e3 AC |
401 | int keep_our_modes = YES; |
402 | int keep_new_modes = YES; | |
403 | dlink_node *ptr, *next_ptr; | |
404 | ||
f4a80ce6 JT |
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 AC |
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 AC |
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 AC |
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 AC |
467 | /* Lost the TS, other side wins, so remove modes on this side */ |
468 | if(!keep_our_modes) | |
469 | { | |
bee3b671 JT |
470 | set_final_mode(&mode, &chptr->mode); |
471 | chptr->mode = mode; | |
212380e3 AC |
472 | remove_our_modes(chptr, source_p); |
473 | DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->invites.head) | |
474 | { | |
475 | del_invite(chptr, ptr->data); | |
476 | } | |
bee3b671 JT |
477 | /* If setting -j, clear join throttle state -- jilles */ |
478 | chptr->join_count = chptr->join_delta = 0; | |
212380e3 AC |
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 JT |
483 | /* Update capitalization in channel name, this makes the |
484 | * capitalization timestamped like modes are -- jilles */ | |
485 | strcpy(chptr->chname, parv[2]); | |
bee3b671 JT |
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 AC |
492 | } |
493 | ||
212380e3 AC |
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 JT |
510 | ":%s JOIN %ld %s +", |
511 | source_p->id, (long) chptr->channelts, chptr->chname); | |
212380e3 AC |
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 JT |
515 | chptr->chname, keep_new_modes ? "+" : "0", |
516 | source_p->name); | |
212380e3 AC |
517 | return 0; |
518 | } | |
519 | ||
f4a80ce6 JT |
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; | |
535 | dlink_node *ptr; | |
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 AC |
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 | ||
587 | struct mode_letter | |
588 | { | |
589 | int mode; | |
590 | char letter; | |
591 | }; | |
592 | ||
593 | static struct mode_letter flags[] = { | |
594 | {MODE_NOPRIVMSGS, 'n'}, | |
595 | {MODE_TOPICLIMIT, 't'}, | |
596 | {MODE_SECRET, 's'}, | |
597 | {MODE_MODERATED, 'm'}, | |
598 | {MODE_INVITEONLY, 'i'}, | |
599 | {MODE_PRIVATE, 'p'}, | |
600 | {MODE_REGONLY, 'r'}, | |
601 | {MODE_EXLIMIT, 'L'}, | |
602 | {MODE_PERMANENT, 'P'}, | |
603 | {MODE_NOCOLOR, 'c'}, | |
604 | {MODE_FREEINVITE, 'g'}, | |
605 | {MODE_OPMODERATE, 'z'}, | |
606 | {MODE_FREETARGET, 'F'}, | |
607 | {MODE_DISFORWARD, 'Q'}, | |
608 | {0, 0} | |
609 | }; | |
610 | ||
611 | static void | |
612 | set_final_mode(struct Mode *mode, struct Mode *oldmode) | |
613 | { | |
614 | int dir = MODE_QUERY; | |
615 | char *pbuf = parabuf; | |
616 | int len; | |
617 | int i; | |
618 | ||
619 | /* ok, first get a list of modes we need to add */ | |
620 | for(i = 0; flags[i].letter; i++) | |
621 | { | |
622 | if((mode->mode & flags[i].mode) && !(oldmode->mode & flags[i].mode)) | |
623 | { | |
624 | if(dir != MODE_ADD) | |
625 | { | |
626 | *mbuf++ = '+'; | |
627 | dir = MODE_ADD; | |
628 | } | |
629 | *mbuf++ = flags[i].letter; | |
630 | } | |
631 | } | |
632 | ||
633 | /* now the ones we need to remove. */ | |
634 | for(i = 0; flags[i].letter; i++) | |
635 | { | |
636 | if((oldmode->mode & flags[i].mode) && !(mode->mode & flags[i].mode)) | |
637 | { | |
638 | if(dir != MODE_DEL) | |
639 | { | |
640 | *mbuf++ = '-'; | |
641 | dir = MODE_DEL; | |
642 | } | |
643 | *mbuf++ = flags[i].letter; | |
644 | } | |
645 | } | |
646 | ||
647 | if(oldmode->limit && !mode->limit) | |
648 | { | |
649 | if(dir != MODE_DEL) | |
650 | { | |
651 | *mbuf++ = '-'; | |
652 | dir = MODE_DEL; | |
653 | } | |
654 | *mbuf++ = 'l'; | |
655 | } | |
656 | if(oldmode->key[0] && !mode->key[0]) | |
657 | { | |
658 | if(dir != MODE_DEL) | |
659 | { | |
660 | *mbuf++ = '-'; | |
661 | dir = MODE_DEL; | |
662 | } | |
663 | *mbuf++ = 'k'; | |
664 | len = ircsprintf(pbuf, "%s ", oldmode->key); | |
665 | pbuf += len; | |
666 | } | |
667 | if(oldmode->join_num && !mode->join_num) | |
668 | { | |
669 | if(dir != MODE_DEL) | |
670 | { | |
671 | *mbuf++ = '-'; | |
672 | dir = MODE_DEL; | |
673 | } | |
674 | *mbuf++ = 'j'; | |
675 | } | |
676 | if(oldmode->forward[0] && !mode->forward[0]) | |
677 | { | |
678 | if(dir != MODE_DEL) | |
679 | { | |
680 | *mbuf++ = '-'; | |
681 | dir = MODE_DEL; | |
682 | } | |
683 | *mbuf++ = 'f'; | |
684 | } | |
685 | if(mode->limit && oldmode->limit != mode->limit) | |
686 | { | |
687 | if(dir != MODE_ADD) | |
688 | { | |
689 | *mbuf++ = '+'; | |
690 | dir = MODE_ADD; | |
691 | } | |
692 | *mbuf++ = 'l'; | |
693 | len = ircsprintf(pbuf, "%d ", mode->limit); | |
694 | pbuf += len; | |
695 | } | |
696 | if(mode->key[0] && strcmp(oldmode->key, mode->key)) | |
697 | { | |
698 | if(dir != MODE_ADD) | |
699 | { | |
700 | *mbuf++ = '+'; | |
701 | dir = MODE_ADD; | |
702 | } | |
703 | *mbuf++ = 'k'; | |
704 | len = ircsprintf(pbuf, "%s ", mode->key); | |
705 | pbuf += len; | |
706 | } | |
707 | if(mode->join_num && (oldmode->join_num != mode->join_num || oldmode->join_time != mode->join_time)) | |
708 | { | |
709 | if(dir != MODE_ADD) | |
710 | { | |
711 | *mbuf++ = '+'; | |
712 | dir = MODE_ADD; | |
713 | } | |
714 | *mbuf++ = 'j'; | |
715 | len = ircsprintf(pbuf, "%d:%d ", mode->join_num, mode->join_time); | |
716 | pbuf += len; | |
717 | } | |
718 | if(mode->forward[0] && strcmp(oldmode->forward, mode->forward) && ConfigChannel.use_forward) | |
719 | { | |
720 | if(dir != MODE_ADD) | |
721 | { | |
722 | *mbuf++ = '+'; | |
723 | dir = MODE_ADD; | |
724 | } | |
725 | *mbuf++ = 'f'; | |
726 | len = ircsprintf(pbuf, "%s ", mode->forward); | |
727 | pbuf += len; | |
728 | } | |
729 | *mbuf = '\0'; | |
730 | } | |
731 | ||
732 | /* | |
733 | * remove_our_modes | |
734 | * | |
735 | * inputs - | |
736 | * output - | |
737 | * side effects - | |
738 | */ | |
739 | static void | |
740 | remove_our_modes(struct Channel *chptr, struct Client *source_p) | |
741 | { | |
742 | struct membership *msptr; | |
743 | dlink_node *ptr; | |
744 | char lmodebuf[MODEBUFLEN]; | |
745 | char *lpara[MAXMODEPARAMS]; | |
746 | int count = 0; | |
747 | int i; | |
748 | ||
749 | mbuf = lmodebuf; | |
750 | *mbuf++ = '-'; | |
751 | ||
752 | for(i = 0; i < MAXMODEPARAMS; i++) | |
753 | lpara[i] = NULL; | |
754 | ||
755 | DLINK_FOREACH(ptr, chptr->members.head) | |
756 | { | |
757 | msptr = ptr->data; | |
758 | ||
759 | if(is_chanop(msptr)) | |
760 | { | |
761 | msptr->flags &= ~CHFL_CHANOP; | |
762 | lpara[count++] = msptr->client_p->name; | |
763 | *mbuf++ = 'o'; | |
764 | ||
765 | /* +ov, might not fit so check. */ | |
766 | if(is_voiced(msptr)) | |
767 | { | |
768 | if(count >= MAXMODEPARAMS) | |
769 | { | |
770 | *mbuf = '\0'; | |
771 | sendto_channel_local(ALL_MEMBERS, chptr, | |
772 | ":%s MODE %s %s %s %s %s %s", | |
773 | me.name, chptr->chname, | |
774 | lmodebuf, lpara[0], lpara[1], | |
775 | lpara[2], lpara[3]); | |
776 | ||
777 | /* preserve the initial '-' */ | |
778 | mbuf = lmodebuf; | |
779 | *mbuf++ = '-'; | |
780 | count = 0; | |
781 | ||
782 | for(i = 0; i < MAXMODEPARAMS; i++) | |
783 | lpara[i] = NULL; | |
784 | } | |
785 | ||
786 | msptr->flags &= ~CHFL_VOICE; | |
787 | lpara[count++] = msptr->client_p->name; | |
788 | *mbuf++ = 'v'; | |
789 | } | |
790 | } | |
791 | else if(is_voiced(msptr)) | |
792 | { | |
793 | msptr->flags &= ~CHFL_VOICE; | |
794 | lpara[count++] = msptr->client_p->name; | |
795 | *mbuf++ = 'v'; | |
796 | } | |
797 | else | |
798 | continue; | |
799 | ||
800 | if(count >= MAXMODEPARAMS) | |
801 | { | |
802 | *mbuf = '\0'; | |
803 | sendto_channel_local(ALL_MEMBERS, chptr, | |
804 | ":%s MODE %s %s %s %s %s %s", | |
805 | me.name, chptr->chname, lmodebuf, | |
806 | lpara[0], lpara[1], lpara[2], lpara[3]); | |
807 | mbuf = lmodebuf; | |
808 | *mbuf++ = '-'; | |
809 | count = 0; | |
810 | ||
811 | for(i = 0; i < MAXMODEPARAMS; i++) | |
812 | lpara[i] = NULL; | |
813 | } | |
814 | } | |
815 | ||
816 | if(count != 0) | |
817 | { | |
818 | *mbuf = '\0'; | |
819 | sendto_channel_local(ALL_MEMBERS, chptr, | |
820 | ":%s MODE %s %s %s %s %s %s", | |
821 | me.name, chptr->chname, lmodebuf, | |
822 | EmptyString(lpara[0]) ? "" : lpara[0], | |
823 | EmptyString(lpara[1]) ? "" : lpara[1], | |
824 | EmptyString(lpara[2]) ? "" : lpara[2], | |
825 | EmptyString(lpara[3]) ? "" : lpara[3]); | |
826 | ||
827 | } | |
828 | } |