]>
Commit | Line | Data |
---|---|---|
3bd189cb JR |
1 | /************************************************************************ |
2 | * IRC - Internet Relay Chat, ircd/channel.c | |
3 | * Copyright (C) 1990 Jarkko Oikarinen and | |
4 | * University of Oulu, Co Center | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 1, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
21 | /* -- Jto -- 09 Jul 1990 | |
22 | * Bug fix | |
23 | */ | |
24 | ||
25 | /* -- Jto -- 03 Jun 1990 | |
26 | * Moved m_channel() and related functions from s_msg.c to here | |
27 | * Many changes to start changing into string channels... | |
28 | */ | |
29 | ||
30 | /* -- Jto -- 24 May 1990 | |
31 | * Moved is_full() from list.c | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static const volatile char rcsid[] = "@(#)$Id: channel.c,v 1.279 2010/08/12 16:23:14 bif Exp $"; | |
36 | #endif | |
37 | ||
38 | #include "os.h" | |
39 | #include "s_defines.h" | |
40 | #define CHANNEL_C | |
41 | #include "s_externs.h" | |
42 | #undef CHANNEL_C | |
43 | ||
44 | static char asterix[2]="*"; | |
45 | ||
46 | #define BanLen(x) ((strlen(x->nick)+strlen(x->user)+strlen(x->host))) | |
47 | #define BanMatch(x,y) ((!match(x->nick,y->nick)&&!match(x->user,y->user)&&!match(x->host,y->host))) | |
48 | #define BanExact(x,y) ((!mycmp(x->nick,y->nick)&&!mycmp(x->user,y->user)&&!mycmp(x->host,y->host))) | |
49 | ||
50 | aChannel *channel = NullChn; | |
51 | ||
52 | static void add_invite (aClient *, aClient *, aChannel *); | |
53 | static int can_join (aClient *, aChannel *, char *); | |
54 | void channel_modes (aClient *, char *, char *, aChannel *); | |
55 | static int check_channelmask (aClient *, aClient *, char *); | |
56 | ||
57 | #ifdef JAPANESE | |
58 | static int jp_chname (char *); | |
59 | #endif | |
60 | ||
61 | static aChannel *get_channel (aClient *, char *, int); | |
62 | static int set_mode (aClient *, aClient *, aChannel *, int *, | |
63 | int, char **); | |
64 | static void free_channel (aChannel *); | |
65 | ||
66 | static int add_modeid (int, aClient *, aChannel *, aListItem *); | |
67 | static int del_modeid (int, aChannel *, aListItem *); | |
68 | static Link *match_modeid (int, aClient *, aChannel *); | |
69 | static void names_channel (aClient *,aClient *,char *,aChannel *,int); | |
70 | static void free_bei (aListItem *bei); | |
71 | static aListItem *make_bei (char *nick, char *user, char *host); | |
72 | ||
73 | ||
74 | static char *PartFmt = ":%s PART %s :%s"; | |
75 | ||
76 | /* | |
77 | * some buffers for rebuilding channel/nick lists with ,'s | |
78 | */ | |
79 | static char buf[BUFSIZE]; | |
80 | static char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN], uparabuf[MODEBUFLEN]; | |
81 | ||
82 | /* | |
83 | * return the length (>=0) of a chain of links. | |
84 | */ | |
85 | static int list_length(invLink *lp) | |
86 | { | |
87 | Reg int count = 0; | |
88 | ||
89 | for (; lp; lp = lp->next) | |
90 | count++; | |
91 | return count; | |
92 | } | |
93 | ||
94 | /* | |
95 | ** find_chasing | |
96 | ** Find the client structure for a nick name (user) using history | |
97 | ** mechanism if necessary. If the client is not found, an error | |
98 | ** message (NO SUCH NICK) is generated. If the client was found | |
99 | ** through the history, chasing will be 1 and otherwise 0. | |
100 | */ | |
101 | static aClient *find_chasing(aClient *sptr, char *user, int *chasing) | |
102 | { | |
103 | Reg aClient *who = find_client(user, (aClient *)NULL); | |
104 | ||
105 | if (chasing) | |
106 | *chasing = 0; | |
107 | if (who) | |
108 | return who; | |
109 | if (!(who = get_history(user, (long)KILLCHASETIMELIMIT))) | |
110 | { | |
111 | sendto_one(sptr, replies[ERR_NOSUCHNICK], ME, BadTo(sptr->name), user); | |
112 | return NULL; | |
113 | } | |
114 | if (chasing) | |
115 | *chasing = 1; | |
116 | return who; | |
117 | } | |
118 | ||
119 | /* | |
120 | * Fixes a string so that the first white space found becomes an end of | |
121 | * string marker (`\0`). Returns the 'fixed' string or static "*" if the string | |
122 | * was NULL length or a NULL pointer. | |
123 | */ | |
124 | static char *check_string(char *s) | |
125 | { | |
126 | char *str = s; | |
127 | ||
128 | if (BadPtr(s)) | |
129 | return asterix; | |
130 | ||
131 | for ( ;*s; s++) | |
132 | if (isspace(*s)) | |
133 | { | |
134 | *s = '\0'; | |
135 | break; | |
136 | } | |
137 | ||
138 | return (BadPtr(str)) ? asterix : str; | |
139 | } | |
140 | ||
141 | static void free_bei(aListItem *bei) | |
142 | { | |
143 | if (bei->nick != asterix) | |
144 | { | |
145 | MyFree(bei->nick); | |
146 | } | |
147 | if (bei->user != asterix) | |
148 | { | |
149 | MyFree(bei->user); | |
150 | } | |
151 | if (bei->host != asterix) | |
152 | { | |
153 | MyFree(bei->host); | |
154 | } | |
155 | MyFree(bei); | |
156 | } | |
157 | ||
158 | /* Prepare and fill ListItem struct. Note: check_string takes care of | |
159 | ** cleaning parts, including possible use of static asterix. */ | |
160 | static aListItem *make_bei(char *nick, char *user, char *host) | |
161 | { | |
162 | aListItem *tmp; | |
163 | int len; | |
164 | ||
165 | tmp = (struct ListItem *)MyMalloc(sizeof(aListItem)); | |
166 | ||
167 | nick = check_string(nick); | |
168 | if (nick == asterix) | |
169 | { | |
170 | tmp->nick = asterix; | |
171 | } | |
172 | else | |
173 | { | |
174 | len = MIN(strlen(nick), NICKLEN) + 1; | |
175 | tmp->nick = (char *) MyMalloc(len); | |
176 | strncpyzt(tmp->nick, nick, len); | |
177 | } | |
178 | user = check_string(user); | |
179 | if (user == asterix) | |
180 | { | |
181 | tmp->user = asterix; | |
182 | } | |
183 | else | |
184 | { | |
185 | len = MIN(strlen(user), USERLEN) + 1; | |
186 | tmp->user=(char *) MyMalloc(len); | |
187 | strncpyzt(tmp->user, user, len); | |
188 | } | |
189 | host = check_string(host); | |
190 | if (host == asterix) | |
191 | { | |
192 | tmp->host = asterix; | |
193 | } | |
194 | else | |
195 | { | |
196 | len = MIN(strlen(host), HOSTLEN) + 1; | |
197 | tmp->host=(char *) MyMalloc(len); | |
198 | strncpyzt(tmp->host, host, len); | |
199 | } | |
200 | ||
201 | return tmp; | |
202 | } | |
203 | ||
204 | /* | |
205 | * Ban functions to work with mode +b/+e/+I | |
206 | */ | |
207 | /* add_modeid - add an id to the list of modes "type" for chptr | |
208 | * (belongs to cptr) | |
209 | */ | |
210 | ||
211 | static int add_modeid(int type, aClient *cptr, aChannel *chptr, | |
212 | aListItem *modeid) | |
213 | { | |
214 | Reg Link *mode; | |
215 | Reg int cnt = 0, len = 0; | |
216 | ||
217 | if (MyClient(cptr)) | |
218 | { | |
219 | (void) collapse(modeid->nick); | |
220 | (void) collapse(modeid->user); | |
221 | (void) collapse(modeid->host); | |
222 | } | |
223 | ||
224 | for (mode = chptr->mlist; mode; mode = mode->next) | |
225 | { | |
226 | len += BanLen(mode->value.alist); | |
227 | if (MyClient(cptr)) | |
228 | { | |
229 | if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS)) | |
230 | { | |
231 | sendto_one(cptr, replies[ERR_BANLISTFULL], | |
232 | ME, BadTo(cptr->name), | |
233 | chptr->chname, modeid->nick, | |
234 | modeid->user, modeid->host); | |
235 | return -1; | |
236 | } | |
237 | if (type == mode->flags && | |
238 | BanExact(mode->value.alist, modeid)) | |
239 | { | |
240 | int rpl; | |
241 | ||
242 | if (type == CHFL_BAN) | |
243 | rpl = RPL_BANLIST; | |
244 | else if (type == CHFL_EXCEPTION) | |
245 | rpl = RPL_EXCEPTLIST; | |
246 | else if (type == CHFL_REOPLIST) | |
247 | rpl = RPL_REOPLIST; | |
248 | else | |
249 | rpl = RPL_INVITELIST; | |
250 | ||
251 | sendto_one(cptr, replies[rpl], ME, BadTo(cptr->name), | |
252 | chptr->chname, mode->value.alist->nick, | |
253 | mode->value.alist->user, | |
254 | mode->value.alist->host); | |
255 | return -1; | |
256 | } | |
257 | } | |
258 | else if (type == mode->flags && BanExact(mode->value.alist, modeid)) | |
259 | { | |
260 | return -1; | |
261 | } | |
262 | ||
263 | } | |
264 | mode = make_link(); | |
265 | istat.is_bans++; | |
266 | bzero((char *)mode, sizeof(Link)); | |
267 | mode->flags = type; | |
268 | mode->next = chptr->mlist; | |
269 | mode->value.alist = modeid; | |
270 | istat.is_banmem += BanLen(modeid); | |
271 | chptr->mlist = mode; | |
272 | return 0; | |
273 | } | |
274 | ||
275 | /* | |
276 | * del_modeid - delete an id belonging to chptr | |
277 | * if modeid is null, delete all ids belonging to chptr. (seems to be unused) | |
278 | */ | |
279 | static int del_modeid(int type, aChannel *chptr, aListItem *modeid) | |
280 | { | |
281 | Reg Link **mode; | |
282 | Reg Link *tmp; | |
283 | ||
284 | /* mode == NULL rewritten inside loop */ | |
285 | for (mode = &(chptr->mlist); *mode; mode = &((*mode)->next)) | |
286 | { | |
287 | if (type == (*mode)->flags && | |
288 | (modeid == NULL ? 1 : BanExact(modeid, (*mode)->value.alist))) | |
289 | { | |
290 | tmp = *mode; | |
291 | *mode = tmp->next; | |
292 | istat.is_banmem -= BanLen(tmp->value.alist); | |
293 | istat.is_bans--; | |
294 | free_bei(tmp->value.alist); | |
295 | free_link(tmp); | |
296 | break; | |
297 | } | |
298 | } | |
299 | return 0; | |
300 | } | |
301 | ||
302 | /* | |
303 | * match_modeid - returns a pointer to the mode structure if matching else NULL | |
304 | */ | |
305 | static Link *match_modeid(int type, aClient *cptr, aChannel *chptr) | |
306 | { | |
307 | Reg Link *tmp; | |
308 | ||
309 | if (!IsPerson(cptr)) | |
310 | return NULL; | |
311 | ||
312 | for (tmp = chptr->mlist; tmp; tmp = tmp->next) | |
313 | { | |
314 | if (tmp->flags == type) | |
315 | { | |
316 | if (match(tmp->value.alist->nick, cptr->name) != 0) | |
317 | { | |
318 | /* seems like no match on nick, but... */ | |
319 | if (isdigit(tmp->value.alist->nick[0])) | |
320 | { | |
321 | /* ...perhaps it is UID-ban? */ | |
322 | if (match(tmp->value.alist->nick, | |
323 | cptr->user->uid) != 0) | |
324 | { | |
325 | /* no match on UID */ | |
326 | continue; | |
327 | } | |
328 | /* We have match on UID! | |
329 | * Go now for the user part */ | |
330 | } | |
331 | else | |
332 | { | |
333 | /* no match on nick part */ | |
334 | continue; | |
335 | } | |
336 | } | |
337 | if (match(tmp->value.alist->user, cptr->user->username) != 0) | |
338 | { | |
339 | /* no match on user part */ | |
340 | continue; | |
341 | } | |
342 | /* At this point n!u of a client matches that of a beI. | |
343 | * Proceeding to more intensive checks of hostname, | |
344 | * IP, CIDR | |
345 | */ | |
346 | if (match(tmp->value.alist->host, cptr->user->host) == 0) | |
347 | { | |
348 | /* match */ | |
349 | break; | |
350 | } | |
351 | /* if our client, let's check IP and CIDR */ | |
352 | /* perhaps we could relax it and check remotes too? */ | |
353 | if (MyConnect(cptr)) | |
354 | { | |
355 | Link *acf = cptr->confs; | |
356 | ||
357 | /* scroll acf to I:line */ | |
358 | if (IsAnOper(cptr)) | |
359 | { | |
360 | acf = acf->next; | |
361 | /* above is faster but will fail if we introduce | |
362 | ** something that will attach another conf for | |
363 | ** client -- the following will have to be used: | |
364 | for (; acf; acf = acf->next) | |
365 | if (acf->value.aconf->status & CONF_CLIENT) | |
366 | break; | |
367 | */ | |
368 | } | |
369 | ||
370 | if (IsConfNoResolveMatch(acf->value.aconf)) | |
371 | { | |
372 | /* user->host contains IP and was just | |
373 | * checked; try sockhost, it may have | |
374 | * hostname. | |
375 | */ | |
376 | if (match(tmp->value.alist->host, | |
377 | cptr->sockhost) == 0) | |
378 | { | |
379 | /* match */ | |
380 | break; | |
381 | } | |
382 | } | |
383 | else | |
384 | /* Yay, it's 2.11, we have string ip! */ | |
385 | if (match(tmp->value.alist->host, cptr->user->sip) == 0) | |
386 | { | |
387 | /* match */ | |
388 | break; | |
389 | } | |
390 | /* so now we check CIDR */ | |
391 | if (strchr(tmp->value.alist->host, '/') && | |
392 | match_ipmask(tmp->value.alist->host, cptr, 0) == 0) | |
393 | { | |
394 | break; | |
395 | } | |
396 | } | |
397 | } | |
398 | } | |
399 | return (tmp); | |
400 | } | |
401 | ||
402 | /* | |
403 | * adds a user to a channel by adding another link to the channels member | |
404 | * chain. | |
405 | */ | |
406 | static void add_user_to_channel(aChannel *chptr, aClient *who, int flags) | |
407 | { | |
408 | Reg Link *ptr; | |
409 | Reg int sz = sizeof(aChannel) + strlen(chptr->chname); | |
410 | ||
411 | if (who->user) | |
412 | { | |
413 | ptr = make_link(); | |
414 | ptr->flags = flags; | |
415 | ptr->value.cptr = who; | |
416 | ptr->next = chptr->members; | |
417 | chptr->members = ptr; | |
418 | istat.is_chanusers++; | |
419 | if (chptr->users++ == 0) | |
420 | { | |
421 | istat.is_chan++; | |
422 | istat.is_chanmem += sz; | |
423 | } | |
424 | if (chptr->users == 1 && chptr->history) | |
425 | { | |
426 | /* Locked channel */ | |
427 | istat.is_hchan--; | |
428 | istat.is_hchanmem -= sz; | |
429 | /* | |
430 | ** The modes had been kept, but now someone is joining, | |
431 | ** they should be reset to avoid desynchs | |
432 | ** (you wouldn't want to join a +i channel, either) | |
433 | ** | |
434 | ** This can be wrong in some cases such as a netjoin | |
435 | ** which will not complete, or on a mixed net (with | |
436 | ** servers that don't do channel delay) - kalt | |
437 | */ | |
438 | if (*chptr->chname != '!') | |
439 | bzero((char *)&chptr->mode, sizeof(Mode)); | |
440 | } | |
441 | ||
442 | #ifdef USE_SERVICES | |
443 | if (chptr->users == 1) | |
444 | check_services_butone(SERVICE_WANT_CHANNEL| | |
445 | SERVICE_WANT_VCHANNEL, | |
446 | NULL, &me, "CHANNEL %s %d", | |
447 | chptr->chname, chptr->users); | |
448 | else | |
449 | check_services_butone(SERVICE_WANT_VCHANNEL, | |
450 | NULL, &me, "CHANNEL %s %d", | |
451 | chptr->chname, chptr->users); | |
452 | #endif | |
453 | ptr = make_link(); | |
454 | ptr->flags = flags; | |
455 | ptr->value.chptr = chptr; | |
456 | ptr->next = who->user->channel; | |
457 | who->user->channel = ptr; | |
458 | if (!IsQuiet(chptr)) | |
459 | { | |
460 | who->user->joined++; | |
461 | istat.is_userc++; | |
462 | } | |
463 | ||
464 | if (!(ptr = find_user_link(chptr->clist, who->from))) | |
465 | { | |
466 | ptr = make_link(); | |
467 | ptr->value.cptr = who->from; | |
468 | ptr->next = chptr->clist; | |
469 | chptr->clist = ptr; | |
470 | } | |
471 | ptr->flags++; | |
472 | } | |
473 | } | |
474 | ||
475 | void remove_user_from_channel(aClient *sptr, aChannel *chptr) | |
476 | { | |
477 | Reg Link **curr; | |
478 | Reg Link *tmp, *tmp2; | |
479 | ||
480 | for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next) | |
481 | if (tmp->value.cptr == sptr) | |
482 | { | |
483 | /* | |
484 | * if a chanop leaves (no matter how), record | |
485 | * the time to be able to later massreop if | |
486 | * necessary. | |
487 | */ | |
488 | if (tmp->flags & CHFL_CHANOP) | |
489 | { | |
490 | chptr->reop = timeofday + LDELAYCHASETIMELIMIT + | |
491 | myrand() % 300; | |
492 | } | |
493 | ||
494 | *curr = tmp->next; | |
495 | free_link(tmp); | |
496 | break; | |
497 | } | |
498 | for (curr = &sptr->user->channel; (tmp = *curr); curr = &tmp->next) | |
499 | if (tmp->value.chptr == chptr) | |
500 | { | |
501 | *curr = tmp->next; | |
502 | free_link(tmp); | |
503 | break; | |
504 | } | |
505 | if (sptr->from) | |
506 | tmp2 = find_user_link(chptr->clist, sptr->from); | |
507 | else | |
508 | tmp2 = find_user_link(chptr->clist, sptr); | |
509 | if (tmp2 && !--tmp2->flags) | |
510 | for (curr = &chptr->clist; (tmp = *curr); curr = &tmp->next) | |
511 | if (tmp2 == tmp) | |
512 | { | |
513 | *curr = tmp->next; | |
514 | free_link(tmp); | |
515 | break; | |
516 | } | |
517 | if (!IsQuiet(chptr)) | |
518 | { | |
519 | sptr->user->joined--; | |
520 | istat.is_userc--; | |
521 | } | |
522 | #ifdef USE_SERVICES | |
523 | if (chptr->users == 1) | |
524 | check_services_butone(SERVICE_WANT_CHANNEL| | |
525 | SERVICE_WANT_VCHANNEL, NULL, &me, | |
526 | "CHANNEL %s %d", chptr->chname, | |
527 | chptr->users-1); | |
528 | else | |
529 | check_services_butone(SERVICE_WANT_VCHANNEL, NULL, &me, | |
530 | "CHANNEL %s %d", chptr->chname, | |
531 | chptr->users-1); | |
532 | #endif | |
533 | if (--chptr->users <= 0) | |
534 | { | |
535 | u_int sz = sizeof(aChannel) + strlen(chptr->chname); | |
536 | ||
537 | istat.is_chan--; | |
538 | istat.is_chanmem -= sz; | |
539 | istat.is_hchan++; | |
540 | istat.is_hchanmem += sz; | |
541 | free_channel(chptr); | |
542 | } | |
543 | istat.is_chanusers--; | |
544 | } | |
545 | ||
546 | static void change_chan_flag(Link *lp, aChannel *chptr) | |
547 | { | |
548 | Reg Link *tmp; | |
549 | aClient *cptr = lp->value.cptr; | |
550 | ||
551 | /* | |
552 | * Set the channel members flags... | |
553 | */ | |
554 | tmp = find_user_link(chptr->members, cptr); | |
555 | if (lp->flags & MODE_ADD) | |
556 | tmp->flags |= lp->flags & MODE_FLAGS; | |
557 | else | |
558 | { | |
559 | tmp->flags &= ~lp->flags & MODE_FLAGS; | |
560 | if (lp->flags & CHFL_CHANOP) | |
561 | tmp->flags &= ~CHFL_UNIQOP; | |
562 | } | |
563 | /* | |
564 | * and make sure client membership mirrors channel | |
565 | */ | |
566 | tmp = find_user_link(cptr->user->channel, (aClient *)chptr); | |
567 | if (lp->flags & MODE_ADD) | |
568 | tmp->flags |= lp->flags & MODE_FLAGS; | |
569 | else | |
570 | { | |
571 | tmp->flags &= ~lp->flags & MODE_FLAGS; | |
572 | if (lp->flags & CHFL_CHANOP) | |
573 | tmp->flags &= ~CHFL_UNIQOP; | |
574 | } | |
575 | } | |
576 | ||
577 | int is_chan_op(aClient *cptr, aChannel *chptr) | |
578 | { | |
579 | Reg Link *lp; | |
580 | int chanop = 0; | |
581 | ||
582 | if (MyConnect(cptr) && IsPerson(cptr) && IsRestricted(cptr) && | |
583 | *chptr->chname != '&') | |
584 | return 0; | |
585 | if (chptr) | |
586 | if ((lp = find_user_link(chptr->members, cptr))) | |
587 | chanop = (lp->flags & (CHFL_CHANOP|CHFL_UNIQOP)); | |
588 | if (chanop) | |
589 | chptr->reop = 0; | |
590 | return chanop; | |
591 | } | |
592 | ||
593 | int has_voice(aClient *cptr, aChannel *chptr) | |
594 | { | |
595 | Reg Link *lp; | |
596 | ||
597 | if (chptr) | |
598 | if ((lp = find_user_link(chptr->members, cptr))) | |
599 | return (lp->flags & CHFL_VOICE); | |
600 | ||
601 | return 0; | |
602 | } | |
603 | ||
604 | int can_send(aClient *cptr, aChannel *chptr) | |
605 | { | |
606 | Reg Link *lp; | |
607 | Reg int member; | |
608 | ||
609 | member = IsMember(cptr, chptr); | |
610 | lp = find_user_link(chptr->members, cptr); | |
611 | ||
612 | if (chptr->mode.mode & MODE_MODERATED && | |
613 | (!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE)))) | |
614 | return (MODE_MODERATED); | |
615 | ||
616 | if (chptr->mode.mode & MODE_NOPRIVMSGS && !member) | |
617 | return (MODE_NOPRIVMSGS); | |
618 | ||
619 | /* checking +be is not reliable for remote clients for case | |
620 | ** when exception is in cidr format. working around that is | |
621 | ** horrible. basically inet_pton for each UNICK and keeping | |
622 | ** it in client struct, plus dealing somehow with non-inet6 | |
623 | ** servers getting inet6 clients ips from remote servers... | |
624 | ** in short it seems better to allow remote clients to just | |
625 | ** talk, trusting a bit remote servers, than to reject good | |
626 | ** messages. --B. */ | |
627 | if (!MyConnect(cptr)) | |
628 | return 0; | |
629 | ||
630 | if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE))) && | |
631 | !match_modeid(CHFL_EXCEPTION, cptr, chptr) && | |
632 | match_modeid(CHFL_BAN, cptr, chptr)) | |
633 | return (MODE_BAN); | |
634 | ||
635 | return 0; | |
636 | } | |
637 | ||
638 | #ifdef JAPANESE | |
639 | char *get_channelmask(char *chname) | |
640 | { | |
641 | char *mask; | |
642 | ||
643 | mask = rindex(chname, ':'); | |
644 | if (!mask || index(mask, '\033')) | |
645 | { | |
646 | /* If '\033' is in the mask, well, it's not a real mask, | |
647 | ** but a JIS encoded channel name. --Beeth */ | |
648 | return NULL; | |
649 | } | |
650 | return mask; | |
651 | } | |
652 | ||
653 | /* This tries to find out if given chname is JIS encoded: | |
654 | ** a) ":" followed somewhere by '\033' | |
655 | ** b) comma in chname (impossible if not for JIS) | |
656 | ** c) one of {, }, ~, \ between JIS marks. | |
657 | ** | |
658 | ** Returns 1 if seems JIS encoded, 0 otherwise. | |
659 | */ | |
660 | int jp_chname(char *chname) | |
661 | { | |
662 | char *mask, *cn; | |
663 | int flag = 0; | |
664 | ||
665 | if (!chname || !*chname) | |
666 | return 0; | |
667 | mask = rindex(chname, ':'); | |
668 | if (mask && index(mask, '\033')) | |
669 | return 1; | |
670 | if (index(chname, ',')) | |
671 | return 1; | |
672 | ||
673 | cn = chname; | |
674 | while (*cn) | |
675 | { | |
676 | if (cn[0] == '\033' | |
677 | && (cn[1] == '$' || cn[1] == '(') | |
678 | && cn[2] == 'B') | |
679 | { | |
680 | flag = (cn[1] == '$') ? 1 : 0; | |
681 | cn += 2; | |
682 | } | |
683 | else if (flag == 1 && | |
684 | (*cn == '{' || *cn == '}' || *cn == '~' || *cn == '\\')) | |
685 | { | |
686 | return 1; | |
687 | } | |
688 | cn++; | |
689 | } | |
690 | return 0; | |
691 | } | |
692 | ||
693 | #define IsJPFlag(x) (((x)->flags & FLAGS_JP)) | |
694 | #define IsJPChan(x, y) ( ((x) && IsJPFlag((x))) || jp_chname((y)) ) | |
695 | ||
696 | /* | |
697 | ** This checks for valid combination of channel name and server, | |
698 | ** so Japanese channels are not sent to non-Japanese servers. | |
699 | ** | |
700 | ** If cptr is NULL, then function is reduced to checking if channel name | |
701 | ** is (likely to be) Japanese (if it is not, it can be sent anywhere). | |
702 | ** | |
703 | ** Otherwise cptr should be a JP flagged server or not a server at all. | |
704 | ** | |
705 | ** Returns 1 if it is safe to use given combination of params or 0 if not. | |
706 | ** | |
707 | ** Note: this should be split in two functions for clarity. | |
708 | */ | |
709 | int jp_valid(aClient *cptr, aChannel *chptr, char *chname) | |
710 | { | |
711 | return ( !IsJPChan(chptr, chname) || | |
712 | (cptr && (!IsServer(cptr) || IsJPFlag(cptr))) ); | |
713 | } | |
714 | #endif | |
715 | ||
716 | aChannel *find_channel(char *chname, aChannel *chptr) | |
717 | { | |
718 | aChannel *achptr = chptr; | |
719 | ||
720 | if (chname && *chname) | |
721 | achptr = hash_find_channel(chname, chptr); | |
722 | return achptr; | |
723 | } | |
724 | ||
725 | void setup_server_channels(aClient *mp) | |
726 | { | |
727 | aChannel *chptr; | |
728 | int smode; | |
729 | ||
730 | smode = MODE_MODERATED|MODE_TOPICLIMIT|MODE_NOPRIVMSGS|MODE_ANONYMOUS| | |
731 | MODE_QUIET; | |
732 | ||
733 | chptr = get_channel(mp, "&ERRORS", CREATE); | |
734 | strcpy(chptr->topic, "SERVER MESSAGES: server errors"); | |
735 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
736 | chptr->mode.mode = smode; | |
737 | chptr = get_channel(mp, "&NOTICES", CREATE); | |
738 | strcpy(chptr->topic, "SERVER MESSAGES: warnings and notices"); | |
739 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
740 | chptr->mode.mode = smode; | |
741 | chptr = get_channel(mp, "&KILLS", CREATE); | |
742 | strcpy(chptr->topic, "SERVER MESSAGES: operator and server kills"); | |
743 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
744 | chptr->mode.mode = smode; | |
745 | chptr = get_channel(mp, "&CHANNEL", CREATE); | |
746 | strcpy(chptr->topic, "SERVER MESSAGES: fake modes"); | |
747 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
748 | chptr->mode.mode = smode; | |
749 | chptr = get_channel(mp, "&NUMERICS", CREATE); | |
750 | strcpy(chptr->topic, "SERVER MESSAGES: numerics received"); | |
751 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
752 | chptr->mode.mode = smode; | |
753 | chptr = get_channel(mp, "&SERVERS", CREATE); | |
754 | strcpy(chptr->topic, "SERVER MESSAGES: servers joining and leaving"); | |
755 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
756 | chptr->mode.mode = smode; | |
757 | chptr = get_channel(mp, "&HASH", CREATE); | |
758 | strcpy(chptr->topic, "SERVER MESSAGES: hash tables growth"); | |
759 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
760 | chptr->mode.mode = smode; | |
761 | chptr = get_channel(mp, "&LOCAL", CREATE); | |
762 | strcpy(chptr->topic, "SERVER MESSAGES: notices about local connections"); | |
763 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
764 | chptr->mode.mode = smode; | |
765 | chptr = get_channel(mp, "&SERVICES", CREATE); | |
766 | strcpy(chptr->topic, "SERVER MESSAGES: services joining and leaving"); | |
767 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
768 | chptr->mode.mode = smode; | |
769 | #if defined(USE_IAUTH) | |
770 | chptr = get_channel(mp, "&AUTH", CREATE); | |
771 | strcpy(chptr->topic, | |
772 | "SERVER MESSAGES: messages from the authentication slave"); | |
773 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
774 | chptr->mode.mode = smode; | |
775 | #endif | |
776 | chptr = get_channel(mp, "&SAVE", CREATE); | |
777 | strcpy(chptr->topic, | |
778 | "SERVER MESSAGES: save messages"); | |
779 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
780 | chptr->mode.mode = smode; | |
781 | chptr = get_channel(mp, "&DEBUG", CREATE); | |
782 | strcpy(chptr->topic, "SERVER MESSAGES: debug messages [you shouldn't be here! ;)]"); | |
783 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
784 | chptr->mode.mode = smode|MODE_SECRET; | |
785 | chptr = get_channel(mp, "&WALLOPS", CREATE); | |
786 | strcpy(chptr->topic, "SERVER MESSAGES: wallops received"); | |
787 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
788 | chptr->mode.mode = smode; | |
789 | #ifdef CLIENTS_CHANNEL | |
790 | chptr = get_channel(mp, "&CLIENTS", CREATE); | |
791 | strcpy(chptr->topic, "SERVER MESSAGES: clients activity"); | |
792 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
793 | chptr->mode.mode = smode|MODE_SECRET|MODE_INVITEONLY; | |
794 | #endif | |
795 | chptr = get_channel(mp, "&OPER", CREATE); | |
796 | strcpy(chptr->topic, "SERVER MESSAGES: for your trusted eyes only"); | |
797 | add_user_to_channel(chptr, mp, CHFL_CHANOP); | |
798 | chptr->mode.mode = smode|MODE_SECRET|MODE_INVITEONLY; | |
799 | ||
800 | setup_svchans(); | |
801 | } | |
802 | ||
803 | /* | |
804 | * write the "simple" list of channel modes for channel chptr onto buffer mbuf | |
805 | * with the parameters in pbuf. | |
806 | */ | |
807 | void channel_modes(aClient *cptr, char *mbuf, char *pbuf, aChannel *chptr) | |
808 | { | |
809 | *mbuf++ = '+'; | |
810 | if (chptr->mode.mode & MODE_SECRET) | |
811 | *mbuf++ = 's'; | |
812 | else if (chptr->mode.mode & MODE_PRIVATE) | |
813 | *mbuf++ = 'p'; | |
814 | if (chptr->mode.mode & MODE_MODERATED) | |
815 | *mbuf++ = 'm'; | |
816 | if (chptr->mode.mode & MODE_TOPICLIMIT) | |
817 | *mbuf++ = 't'; | |
818 | if (chptr->mode.mode & MODE_INVITEONLY) | |
819 | *mbuf++ = 'i'; | |
820 | if (chptr->mode.mode & MODE_NOPRIVMSGS) | |
821 | *mbuf++ = 'n'; | |
822 | if (chptr->mode.mode & MODE_ANONYMOUS) | |
823 | *mbuf++ = 'a'; | |
824 | if (chptr->mode.mode & MODE_QUIET) | |
825 | *mbuf++ = 'q'; | |
826 | if (chptr->mode.mode & MODE_REOP) | |
827 | *mbuf++ = 'r'; | |
828 | if (chptr->mode.limit) | |
829 | { | |
830 | *mbuf++ = 'l'; | |
831 | if (IsMember(cptr, chptr) || IsServer(cptr)) | |
832 | sprintf(pbuf, "%d ", chptr->mode.limit); | |
833 | } | |
834 | if (*chptr->mode.key) | |
835 | { | |
836 | *mbuf++ = 'k'; | |
837 | if (IsMember(cptr, chptr) || IsServer(cptr)) | |
838 | (void)strcat(pbuf, chptr->mode.key); | |
839 | } | |
840 | *mbuf++ = '\0'; | |
841 | return; | |
842 | } | |
843 | ||
844 | static void send_mode_list(aClient *cptr, char *chname, Link *top, | |
845 | int mask, char flag) | |
846 | { | |
847 | Reg Link *lp; | |
848 | Reg char *cp, *name; | |
849 | int count = 0, send = 0; | |
850 | char tmpbei[NICKLEN+1+USERLEN+1+HOSTLEN+1]; | |
851 | ||
852 | cp = modebuf + strlen(modebuf); | |
853 | if (*parabuf) | |
854 | { | |
855 | /* | |
856 | ** we have some modes in parabuf, | |
857 | ** so check how many of them. | |
858 | ** however, don't count initial '+' | |
859 | */ | |
860 | count = strlen(modebuf) - 1; | |
861 | } | |
862 | for (lp = top; lp; lp = lp->next) | |
863 | { | |
864 | if (!(lp->flags & mask)) | |
865 | continue; | |
866 | if (mask == CHFL_BAN || mask == CHFL_EXCEPTION || | |
867 | mask == CHFL_INVITE || mask == CHFL_REOPLIST) | |
868 | { | |
869 | /* XXX: rewrite latter to simply use alist, DO NOT copy it --B. */ | |
870 | sprintf(tmpbei, "%s!%s@%s", lp->value.alist->nick, | |
871 | lp->value.alist->user, lp->value.alist->host); | |
872 | name = tmpbei; | |
873 | } | |
874 | else | |
875 | name = lp->value.cptr->name; | |
876 | if (strlen(parabuf) + strlen(name) + 10 < (size_t) MODEBUFLEN) | |
877 | { | |
878 | if (*parabuf) | |
879 | { | |
880 | (void)strcat(parabuf, " "); | |
881 | } | |
882 | (void)strcat(parabuf, name); | |
883 | count++; | |
884 | *cp++ = flag; | |
885 | *cp = '\0'; | |
886 | } | |
887 | else | |
888 | { | |
889 | if (*parabuf) | |
890 | { | |
891 | send = 1; | |
892 | } | |
893 | } | |
894 | if (count == MAXMODEPARAMS) | |
895 | { | |
896 | send = 1; | |
897 | } | |
898 | if (send) | |
899 | { | |
900 | /* | |
901 | ** send out MODEs, it's either MAXMODEPARAMS of them | |
902 | ** or long enough that they filled up parabuf | |
903 | */ | |
904 | sendto_one(cptr, ":%s MODE %s %s %s", | |
905 | IsServer(cptr) ? me.serv->sid : ME, | |
906 | chname, modebuf, parabuf); | |
907 | send = 0; | |
908 | *parabuf = '\0'; | |
909 | cp = modebuf; | |
910 | *cp++ = '+'; | |
911 | if (count != MAXMODEPARAMS) | |
912 | { | |
913 | /* | |
914 | ** we weren't able to fit another 'name' | |
915 | ** into parabuf, so we have to send it | |
916 | ** in another turn, appending it now to | |
917 | ** empty parabuf and setting count to 1 | |
918 | */ | |
919 | (void)strcpy(parabuf, name); | |
920 | *cp++ = flag; | |
921 | count = 1; | |
922 | } | |
923 | else | |
924 | { | |
925 | count = 0; | |
926 | } | |
927 | *cp = '\0'; | |
928 | } | |
929 | } | |
930 | } | |
931 | ||
932 | /* | |
933 | * send "cptr" a full list of the modes for channel chptr. | |
934 | */ | |
935 | void send_channel_modes(aClient *cptr, aChannel *chptr) | |
936 | { | |
937 | char *me2 = me.serv->sid; | |
938 | ||
939 | if (check_channelmask(&me, cptr, chptr->chname)) | |
940 | return; | |
941 | #ifdef JAPANESE | |
942 | /* We did not send channel members, we don't send channel | |
943 | ** modes to servers that are not prepared to handle JIS encoding. */ | |
944 | if (!jp_valid(cptr, chptr, NULL)) | |
945 | return; | |
946 | #endif | |
947 | ||
948 | *modebuf = *parabuf = '\0'; | |
949 | channel_modes(cptr, modebuf, parabuf, chptr); | |
950 | ||
951 | if (modebuf[1] || *parabuf) | |
952 | { | |
953 | sendto_one(cptr, ":%s MODE %s %s %s", | |
954 | me2, chptr->chname, modebuf, parabuf); | |
955 | } | |
956 | ||
957 | *parabuf = '\0'; | |
958 | *modebuf = '+'; | |
959 | modebuf[1] = '\0'; | |
960 | send_mode_list(cptr, chptr->chname, chptr->mlist, CHFL_BAN, 'b'); | |
961 | send_mode_list(cptr, chptr->chname, chptr->mlist, | |
962 | CHFL_EXCEPTION, 'e'); | |
963 | send_mode_list(cptr, chptr->chname, chptr->mlist, | |
964 | CHFL_INVITE, 'I'); | |
965 | send_mode_list(cptr, chptr->chname, chptr->mlist, | |
966 | CHFL_REOPLIST, 'R'); | |
967 | if (modebuf[1] || *parabuf) | |
968 | { | |
969 | /* complete sending, if anything left in buffers */ | |
970 | sendto_one(cptr, ":%s MODE %s %s %s", | |
971 | me2, chptr->chname, modebuf, parabuf); | |
972 | } | |
973 | } | |
974 | ||
975 | /* | |
976 | * send "cptr" a full list of the channel "chptr" members and their | |
977 | * +ov status, using NJOIN | |
978 | */ | |
979 | void send_channel_members(aClient *cptr, aChannel *chptr) | |
980 | { | |
981 | Reg Link *lp; | |
982 | Reg aClient *c2ptr; | |
983 | Reg int cnt = 0, len = 0, nlen; | |
984 | char *p; | |
985 | char *me2 = me.serv->sid; | |
986 | ||
987 | if (check_channelmask(&me, cptr, chptr->chname) == -1) | |
988 | return; | |
989 | #ifdef JAPANESE | |
990 | /* We do not send channel members to servers that are not prepared | |
991 | ** to handle JIS encoding. */ | |
992 | if (!jp_valid(cptr, chptr, NULL)) | |
993 | return; | |
994 | #endif | |
995 | sprintf(buf, ":%s NJOIN %s :", me2, chptr->chname); | |
996 | len = strlen(buf); | |
997 | ||
998 | for (lp = chptr->members; lp; lp = lp->next) | |
999 | { | |
1000 | c2ptr = lp->value.cptr; | |
1001 | p = c2ptr->user ? c2ptr->user->uid : c2ptr->name; | |
1002 | nlen = strlen(p); | |
1003 | if ((len + nlen) > (size_t) (BUFSIZE - 9)) /* ,@+ \r\n\0 */ | |
1004 | { | |
1005 | sendto_one(cptr, "%s", buf); | |
1006 | sprintf(buf, ":%s NJOIN %s :", me2, chptr->chname); | |
1007 | len = strlen(buf); | |
1008 | cnt = 0; | |
1009 | } | |
1010 | if (cnt) | |
1011 | { | |
1012 | buf[len++] = ','; | |
1013 | buf[len] = '\0'; | |
1014 | } | |
1015 | if (lp->flags & (CHFL_UNIQOP|CHFL_CHANOP|CHFL_VOICE)) | |
1016 | { | |
1017 | if (lp->flags & CHFL_UNIQOP) | |
1018 | { | |
1019 | buf[len++] = '@'; | |
1020 | buf[len++] = '@'; | |
1021 | } | |
1022 | else | |
1023 | { | |
1024 | if (lp->flags & CHFL_CHANOP) | |
1025 | buf[len++] = '@'; | |
1026 | } | |
1027 | if (lp->flags & CHFL_VOICE) | |
1028 | buf[len++] = '+'; | |
1029 | buf[len] = '\0'; | |
1030 | } | |
1031 | (void)strcpy(buf + len, p); | |
1032 | len += nlen; | |
1033 | cnt++; | |
1034 | } | |
1035 | if (*buf && cnt) | |
1036 | sendto_one(cptr, "%s", buf); | |
1037 | ||
1038 | return; | |
1039 | } | |
1040 | ||
1041 | /* | |
1042 | * m_mode | |
1043 | * parv[0] - sender | |
1044 | * parv[1] - target; channels and/or user | |
1045 | * parv[2] - optional modes | |
1046 | * parv[n] - optional parameters | |
1047 | */ | |
1048 | ||
1049 | int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
1050 | { | |
1051 | int penalty = 0; | |
1052 | aChannel *chptr; | |
1053 | char *name, *p = NULL; | |
1054 | ||
1055 | parv[1] = canonize(parv[1]); | |
1056 | ||
1057 | for (name = strtoken(&p, parv[1], ","); name; | |
1058 | name = strtoken(&p, NULL, ",")) | |
1059 | { | |
1060 | if (clean_channelname(name) == -1) | |
1061 | { | |
1062 | penalty += 1; | |
1063 | continue; | |
1064 | } | |
1065 | chptr = find_channel(name, NullChn); | |
1066 | if (chptr == NullChn) | |
1067 | { | |
1068 | parv[1] = name; | |
1069 | penalty += m_umode(cptr, sptr, parc, parv); | |
1070 | continue; | |
1071 | } | |
1072 | if (check_channelmask(sptr, cptr, name)) | |
1073 | { | |
1074 | penalty += 1; | |
1075 | continue; | |
1076 | } | |
1077 | if (!UseModes(name)) | |
1078 | { | |
1079 | sendto_one(sptr, replies[ERR_NOCHANMODES], ME, BadTo(parv[0]), | |
1080 | name); | |
1081 | penalty += 1; | |
1082 | continue; | |
1083 | } | |
1084 | if (parc < 3) /* Only a query */ | |
1085 | { | |
1086 | *modebuf = *parabuf = '\0'; | |
1087 | modebuf[1] = '\0'; | |
1088 | channel_modes(sptr, modebuf, parabuf, chptr); | |
1089 | sendto_one(sptr, replies[RPL_CHANNELMODEIS], ME, BadTo(parv[0]), | |
1090 | chptr->chname, modebuf, parabuf); | |
1091 | penalty += 1; | |
1092 | } | |
1093 | else /* Check parameters for the channel */ | |
1094 | { | |
1095 | if(0==set_mode(cptr, sptr, chptr, | |
1096 | &penalty, parc - 2, parv + 2)) | |
1097 | continue; /* no valid mode change */ | |
1098 | } /* else(parc>2) */ | |
1099 | } /* for (parv1) */ | |
1100 | return penalty; | |
1101 | } | |
1102 | ||
1103 | /* | |
1104 | * Check and try to apply the channel modes passed in the parv array for | |
1105 | * the client cptr to channel chptr. | |
1106 | * Also sends it to everybody that should get it. | |
1107 | */ | |
1108 | static int set_mode(aClient *cptr, aClient *sptr, aChannel *chptr, | |
1109 | int *penalty, int parc, char *parv[]) | |
1110 | { | |
1111 | static Link chops[MAXMODEPARAMS+3]; | |
1112 | static int flags[] = { | |
1113 | MODE_PRIVATE, 'p', MODE_SECRET, 's', | |
1114 | MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n', | |
1115 | MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i', | |
1116 | MODE_ANONYMOUS, 'a', MODE_REOP, 'r', | |
1117 | 0x0, 0x0 }; | |
1118 | ||
1119 | Reg Link *lp = NULL; | |
1120 | Reg char *curr = parv[0], *cp = NULL, *ucp = NULL; | |
1121 | Reg int *ip; | |
1122 | u_int whatt = MODE_ADD; | |
1123 | int limitset = 0, count = 0, chasing = 0; | |
1124 | int nusers = 0, ischop, new, len, ulen, keychange = 0, opcnt = 0; | |
1125 | int reopseen = 0; | |
1126 | aClient *who; | |
1127 | Mode *mode, oldm; | |
1128 | Link *plp = NULL; | |
1129 | #if 0 | |
1130 | int compat = -1; /* to prevent mixing old/new modes */ | |
1131 | #endif | |
1132 | char *mbuf = modebuf, *pbuf = parabuf, *upbuf = uparabuf; | |
1133 | int tmp_chfl = 0, tmp_rpl = 0, tmp_rpl2 = 0, tmp_mode = 0; | |
1134 | ||
1135 | *mbuf = *pbuf = *upbuf = '\0'; | |
1136 | if (parc < 1) | |
1137 | return 0; | |
1138 | ||
1139 | mode = &(chptr->mode); | |
1140 | bcopy((char *)mode, (char *)&oldm, sizeof(Mode)); | |
1141 | ischop = IsServer(sptr) || is_chan_op(sptr, chptr); | |
1142 | new = mode->mode; | |
1143 | ||
1144 | while (curr && *curr && count >= 0) | |
1145 | { | |
1146 | #if 0 | |
1147 | if (compat == -1 && *curr != '-' && *curr != '+') | |
1148 | { | |
1149 | if (*curr == 'R') | |
1150 | { | |
1151 | compat = 1; | |
1152 | } | |
1153 | else | |
1154 | { | |
1155 | compat = 0; | |
1156 | } | |
1157 | } | |
1158 | #endif | |
1159 | switch (*curr) | |
1160 | { | |
1161 | case '+': | |
1162 | whatt = MODE_ADD; | |
1163 | break; | |
1164 | case '-': | |
1165 | whatt = MODE_DEL; | |
1166 | break; | |
1167 | case 'O': | |
1168 | if (parc > 0) | |
1169 | { | |
1170 | if (*chptr->chname == '!') | |
1171 | { | |
1172 | if (IsMember(sptr, chptr)) | |
1173 | { | |
1174 | *penalty += 1; | |
1175 | parc--; | |
1176 | /* Feature: no other modes after this query */ | |
1177 | *(curr+1) = '\0'; | |
1178 | for (lp = chptr->members; lp; lp = lp->next) | |
1179 | if (lp->flags & CHFL_UNIQOP) | |
1180 | { | |
1181 | sendto_one(sptr, | |
1182 | replies[RPL_UNIQOPIS], | |
1183 | ME, BadTo(sptr->name), | |
1184 | chptr->chname, | |
1185 | lp->value.cptr->name); | |
1186 | break; | |
1187 | } | |
1188 | if (!lp) | |
1189 | sendto_one(sptr, | |
1190 | replies[ERR_NOSUCHNICK], | |
1191 | ME, BadTo(sptr->name), | |
1192 | chptr->chname); | |
1193 | break; | |
1194 | } | |
1195 | else /* not IsMember() */ | |
1196 | { | |
1197 | if (!IsServer(sptr)) | |
1198 | { | |
1199 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], ME, BadTo(sptr->name), | |
1200 | chptr->chname); | |
1201 | *(curr+1) = '\0'; | |
1202 | break; | |
1203 | } | |
1204 | } | |
1205 | } | |
1206 | else /* *chptr->chname != '!' */ | |
1207 | sendto_one(cptr, replies[ERR_UNKNOWNMODE], | |
1208 | ME, BadTo(sptr->name), *curr, chptr->chname); | |
1209 | *(curr+1) = '\0'; | |
1210 | break; | |
1211 | } | |
1212 | /* | |
1213 | * is this really ever used ? | |
1214 | * or do ^G & NJOIN do the trick? | |
1215 | */ | |
1216 | if (*chptr->chname != '!' || whatt == MODE_DEL || | |
1217 | !IsServer(sptr)) | |
1218 | { | |
1219 | *penalty += 1; | |
1220 | --parc; | |
1221 | parv++; | |
1222 | break; | |
1223 | } | |
1224 | case 'o' : | |
1225 | case 'v' : | |
1226 | *penalty += 1; | |
1227 | if (--parc <= 0) | |
1228 | break; | |
1229 | parv++; | |
1230 | *parv = check_string(*parv); | |
1231 | if (opcnt >= MAXMODEPARAMS) | |
1232 | #ifndef V29PlusOnly | |
1233 | if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1) | |
1234 | #endif | |
1235 | break; | |
1236 | if (!IsServer(sptr) && !IsMember(sptr, chptr)) | |
1237 | { | |
1238 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], | |
1239 | ME, BadTo(sptr->name), | |
1240 | chptr->chname); | |
1241 | break; | |
1242 | } | |
1243 | /* | |
1244 | * Check for nickname changes and try to follow these | |
1245 | * to make sure the right client is affected by the | |
1246 | * mode change. | |
1247 | */ | |
1248 | if (!(IsServer(cptr) && | |
1249 | (who = find_uid(parv[0], NULL))) && | |
1250 | !(who = find_chasing(sptr, parv[0], &chasing))) | |
1251 | break; | |
1252 | if (!IsMember(who, chptr)) | |
1253 | { | |
1254 | sendto_one(sptr, replies[ERR_USERNOTINCHANNEL], | |
1255 | ME, BadTo(sptr->name), | |
1256 | who->name, chptr->chname); | |
1257 | break; | |
1258 | } | |
1259 | if (who == cptr && whatt == MODE_ADD && *curr == 'o') | |
1260 | break; | |
1261 | ||
1262 | if (whatt == MODE_ADD) | |
1263 | { | |
1264 | lp = &chops[opcnt++]; | |
1265 | lp->value.cptr = who; | |
1266 | lp->flags = (*curr == 'O') ? MODE_UNIQOP: | |
1267 | (*curr == 'o') ? MODE_CHANOP: | |
1268 | MODE_VOICE; | |
1269 | lp->flags |= MODE_ADD; | |
1270 | if (chptr->reop && IsServer(sptr) && !IsBursting(sptr)) | |
1271 | { | |
1272 | reopseen = 1; | |
1273 | } | |
1274 | } | |
1275 | else if (whatt == MODE_DEL) | |
1276 | { | |
1277 | lp = &chops[opcnt++]; | |
1278 | lp->value.cptr = who; | |
1279 | lp->flags = (*curr == 'o') ? MODE_CHANOP: | |
1280 | MODE_VOICE; | |
1281 | lp->flags |= MODE_DEL; | |
1282 | } | |
1283 | if (plp && plp->flags == lp->flags && | |
1284 | plp->value.cptr == lp->value.cptr) | |
1285 | { | |
1286 | opcnt--; | |
1287 | break; | |
1288 | } | |
1289 | plp = lp; | |
1290 | /* | |
1291 | ** If this server noticed the nick change, the | |
1292 | ** information must be propagated back upstream. | |
1293 | ** This is a bit early, but at most this will generate | |
1294 | ** just some extra messages if nick appeared more than | |
1295 | ** once in the MODE message... --msa | |
1296 | */ | |
1297 | /* nobody can figure this part of the code anymore.. -kalt | |
1298 | if (chasing && ischop) | |
1299 | sendto_one(cptr, ":%s MODE %s %c%c %s", | |
1300 | ME, chptr->chname, | |
1301 | whatt == MODE_ADD ? '+' : '-', | |
1302 | *curr, who->name); | |
1303 | */ | |
1304 | count++; | |
1305 | *penalty += 2; | |
1306 | break; | |
1307 | case 'k': | |
1308 | *penalty += 1; | |
1309 | if (--parc <= 0) | |
1310 | break; | |
1311 | parv++; | |
1312 | /* check now so we eat the parameter if present */ | |
1313 | if (keychange) | |
1314 | break; | |
1315 | { | |
1316 | Reg u_char *s; | |
1317 | ||
1318 | for (s = (u_char *)*parv; *s; ) | |
1319 | { | |
1320 | /* comma cannot be inside key --Beeth */ | |
1321 | if (*s == ',') | |
1322 | *s = '.'; | |
1323 | if (*s > 0x7f) | |
1324 | if (*s > 0xa0) | |
1325 | *s++ &= 0x7f; | |
1326 | else | |
1327 | *s = '\0'; | |
1328 | else | |
1329 | s++; | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | if (!**parv) | |
1334 | break; | |
1335 | *parv = check_string(*parv); | |
1336 | if (opcnt >= MAXMODEPARAMS) | |
1337 | #ifndef V29PlusOnly | |
1338 | if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1) | |
1339 | #endif | |
1340 | break; | |
1341 | if (whatt == MODE_ADD) | |
1342 | { | |
1343 | /* stop key swapping during netjoin | |
1344 | ** (prefer "highest" key) */ | |
1345 | if (IsServer(sptr) && IsBursting(sptr) && | |
1346 | *mode->key && strncmp(mode->key, *parv, | |
1347 | (size_t) KEYLEN) >= 0) | |
1348 | break; | |
1349 | if (ischop) | |
1350 | { | |
1351 | if (**parv == ':') | |
1352 | /* this won't propagate right*/ | |
1353 | break; | |
1354 | lp = &chops[opcnt++]; | |
1355 | lp->value.cp = *parv; | |
1356 | if (strlen(lp->value.cp) > | |
1357 | (size_t) KEYLEN) | |
1358 | lp->value.cp[KEYLEN] = '\0'; | |
1359 | lp->flags = MODE_KEY|MODE_ADD; | |
1360 | keychange = 1; | |
1361 | } | |
1362 | } | |
1363 | else if (whatt == MODE_DEL) | |
1364 | { | |
1365 | if (*mode->key && (ischop || IsServer(cptr))) | |
1366 | { | |
1367 | lp = &chops[opcnt++]; | |
1368 | lp->value.cp = *parv; | |
1369 | lp->value.cp[0] = '*'; | |
1370 | lp->value.cp[1] = '\0'; | |
1371 | lp->flags = MODE_KEY|MODE_DEL; | |
1372 | keychange = 1; | |
1373 | } | |
1374 | } | |
1375 | count++; | |
1376 | *penalty += 2; | |
1377 | break; | |
1378 | case 'b': | |
1379 | case 'e': | |
1380 | case 'I': | |
1381 | case 'R': | |
1382 | switch (*curr) | |
1383 | { | |
1384 | case 'b': | |
1385 | tmp_chfl = CHFL_BAN; | |
1386 | tmp_rpl = RPL_BANLIST; | |
1387 | tmp_rpl2 = RPL_ENDOFBANLIST; | |
1388 | tmp_mode = MODE_BAN; | |
1389 | break; | |
1390 | case 'e': | |
1391 | tmp_chfl = CHFL_EXCEPTION; | |
1392 | tmp_rpl = RPL_EXCEPTLIST; | |
1393 | tmp_rpl2 = RPL_ENDOFEXCEPTLIST; | |
1394 | tmp_mode = MODE_EXCEPTION; | |
1395 | break; | |
1396 | case 'I': | |
1397 | tmp_chfl = CHFL_INVITE; | |
1398 | tmp_rpl = RPL_INVITELIST; | |
1399 | tmp_rpl2 = RPL_ENDOFINVITELIST; | |
1400 | tmp_mode = MODE_INVITE; | |
1401 | break; | |
1402 | case 'R': | |
1403 | tmp_chfl = CHFL_REOPLIST; | |
1404 | tmp_rpl = RPL_REOPLIST; | |
1405 | tmp_rpl2 = RPL_ENDOFREOPLIST; | |
1406 | tmp_mode = MODE_REOPLIST; | |
1407 | break; | |
1408 | } | |
1409 | *penalty += 1; | |
1410 | if (--parc <= 0) /* beIR list query */ | |
1411 | { | |
1412 | /* Feature: no other modes after query */ | |
1413 | *(curr+1) = '\0'; /* Stop MODE # bb.. */ | |
1414 | for (lp = chptr->mlist; lp; lp = lp->next) | |
1415 | { | |
1416 | if (lp->flags == tmp_chfl) | |
1417 | { | |
1418 | sendto_one(cptr, | |
1419 | replies[tmp_rpl], | |
1420 | ME, BadTo(cptr->name), | |
1421 | chptr->chname, | |
1422 | lp->value.alist->nick, | |
1423 | lp->value.alist->user, | |
1424 | lp->value.alist->host); | |
1425 | } | |
1426 | } | |
1427 | sendto_one(cptr, replies[tmp_rpl2], | |
1428 | ME, BadTo(cptr->name), | |
1429 | chptr->chname); | |
1430 | break; | |
1431 | } | |
1432 | parv++; | |
1433 | if (BadPtr(*parv)) | |
1434 | break; | |
1435 | if (opcnt >= MAXMODEPARAMS) | |
1436 | #ifndef V29PlusOnly | |
1437 | if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1) | |
1438 | #endif | |
1439 | break; | |
1440 | if (whatt == MODE_ADD) | |
1441 | { | |
1442 | if (**parv == ':') | |
1443 | /* this won't propagate right */ | |
1444 | break; | |
1445 | lp = &chops[opcnt++]; | |
1446 | /* we deal with it later at parseNUH */ | |
1447 | lp->value.cp = *parv; | |
1448 | lp->flags = MODE_ADD|tmp_mode; | |
1449 | } | |
1450 | else if (whatt == MODE_DEL) | |
1451 | { | |
1452 | lp = &chops[opcnt++]; | |
1453 | lp->value.cp = *parv; | |
1454 | lp->flags = MODE_DEL|tmp_mode; | |
1455 | } | |
1456 | count++; | |
1457 | *penalty += 2; | |
1458 | break; | |
1459 | case 'l': | |
1460 | *penalty += 1; | |
1461 | /* | |
1462 | * limit 'l' to only *1* change per mode command but | |
1463 | * eat up others. | |
1464 | */ | |
1465 | if (limitset || !ischop) | |
1466 | { | |
1467 | if (whatt == MODE_ADD && --parc > 0) | |
1468 | parv++; | |
1469 | break; | |
1470 | } | |
1471 | if (whatt == MODE_DEL) | |
1472 | { | |
1473 | limitset = 1; | |
1474 | nusers = 0; | |
1475 | count++; | |
1476 | break; | |
1477 | } | |
1478 | if (--parc > 0) | |
1479 | { | |
1480 | if (BadPtr(*parv)) | |
1481 | break; | |
1482 | if (opcnt >= MAXMODEPARAMS) | |
1483 | #ifndef V29PlusOnly | |
1484 | if (MyClient(sptr) || | |
1485 | opcnt >= MAXMODEPARAMS + 1) | |
1486 | #endif | |
1487 | break; | |
1488 | if (!(nusers = atoi(*++parv))) | |
1489 | break; | |
1490 | if (IsServer(sptr) && IsBursting(sptr) && | |
1491 | mode->limit >= nusers) | |
1492 | break; | |
1493 | lp = &chops[opcnt++]; | |
1494 | lp->flags = MODE_ADD|MODE_LIMIT; | |
1495 | limitset = 1; | |
1496 | count++; | |
1497 | *penalty += 2; | |
1498 | break; | |
1499 | } | |
1500 | sendto_one(cptr, replies[ERR_NEEDMOREPARAMS], | |
1501 | ME, BadTo(cptr->name), "MODE +l"); | |
1502 | break; | |
1503 | case 'i' : /* falls through for default case */ | |
1504 | if (whatt == MODE_DEL && ischop) | |
1505 | while ((lp = chptr->invites)) | |
1506 | del_invite(lp->value.cptr, chptr); | |
1507 | default: | |
1508 | *penalty += 1; | |
1509 | for (ip = flags; *ip; ip += 2) | |
1510 | if (*(ip+1) == *curr) | |
1511 | break; | |
1512 | ||
1513 | if (*ip) | |
1514 | { | |
1515 | if (*ip == MODE_ANONYMOUS && | |
1516 | whatt == MODE_DEL && *chptr->chname == '!') | |
1517 | sendto_one(sptr, | |
1518 | replies[ERR_UNIQOPRIVSNEEDED], | |
1519 | ME, BadTo(sptr->name), chptr->chname); | |
1520 | else if (((*ip == MODE_ANONYMOUS && | |
1521 | whatt == MODE_ADD && | |
1522 | *chptr->chname == '#') || | |
1523 | (*ip == MODE_REOP && whatt == MODE_ADD && | |
1524 | *chptr->chname != '!')) && | |
1525 | !IsServer(sptr)) | |
1526 | sendto_one(cptr, | |
1527 | replies[ERR_UNKNOWNMODE], | |
1528 | ME, BadTo(sptr->name), *curr, | |
1529 | chptr->chname); | |
1530 | else if (*ip == MODE_ANONYMOUS && whatt == MODE_ADD && | |
1531 | !IsServer(sptr) && | |
1532 | !(is_chan_op(sptr,chptr) &CHFL_UNIQOP) | |
1533 | && *chptr->chname == '!') | |
1534 | /* 2 modes restricted to UNIQOP */ | |
1535 | sendto_one(sptr, | |
1536 | replies[ERR_UNIQOPRIVSNEEDED], | |
1537 | ME, BadTo(sptr->name), chptr->chname); | |
1538 | else | |
1539 | { | |
1540 | /* | |
1541 | ** If the channel is +s, ignore +p | |
1542 | ** modes coming from a server. | |
1543 | ** (Otherwise, it's desynch'ed) -kalt | |
1544 | */ | |
1545 | if (whatt == MODE_ADD && | |
1546 | *ip == MODE_PRIVATE && | |
1547 | IsServer(sptr) && | |
1548 | (new & MODE_SECRET)) | |
1549 | break; | |
1550 | if (whatt == MODE_ADD) | |
1551 | { | |
1552 | if (*ip == MODE_PRIVATE) | |
1553 | new &= ~MODE_SECRET; | |
1554 | else if (*ip == MODE_SECRET) | |
1555 | new &= ~MODE_PRIVATE; | |
1556 | new |= *ip; | |
1557 | } | |
1558 | else | |
1559 | new &= ~*ip; | |
1560 | count++; | |
1561 | *penalty += 2; | |
1562 | } | |
1563 | } | |
1564 | else if (!IsServer(cptr)) | |
1565 | sendto_one(cptr, replies[ERR_UNKNOWNMODE], | |
1566 | ME, BadTo(cptr->name), *curr, chptr->chname); | |
1567 | break; | |
1568 | } | |
1569 | curr++; | |
1570 | /* | |
1571 | * Make sure modes strings such as "+m +t +p +i" are parsed | |
1572 | * fully. | |
1573 | */ | |
1574 | if (!*curr && parc > 0) | |
1575 | { | |
1576 | curr = *++parv; | |
1577 | parc--; | |
1578 | } | |
1579 | #if 0 | |
1580 | /* | |
1581 | * Make sure new (+R) mode won't get mixed with old modes | |
1582 | * together on the same line. | |
1583 | */ | |
1584 | if (MyClient(sptr) && curr && *curr != '-' && *curr != '+') | |
1585 | { | |
1586 | if (*curr == 'R') | |
1587 | { | |
1588 | if (compat == 0) | |
1589 | { | |
1590 | *curr = '\0'; | |
1591 | } | |
1592 | } | |
1593 | else if (compat == 1) | |
1594 | { | |
1595 | *curr = '\0'; | |
1596 | } | |
1597 | } | |
1598 | #endif | |
1599 | } /* end of while loop for MODE processing */ | |
1600 | ||
1601 | if (reopseen) | |
1602 | { | |
1603 | ircstp->is_rreop++; | |
1604 | } | |
1605 | ||
1606 | whatt = 0; | |
1607 | ||
1608 | for (ip = flags; *ip; ip += 2) | |
1609 | if ((*ip & new) && !(*ip & oldm.mode)) | |
1610 | { | |
1611 | if (whatt == 0) | |
1612 | { | |
1613 | *mbuf++ = '+'; | |
1614 | whatt = 1; | |
1615 | } | |
1616 | if (ischop) | |
1617 | { | |
1618 | mode->mode |= *ip; | |
1619 | if (*ip == MODE_ANONYMOUS && MyPerson(sptr)) | |
1620 | { | |
1621 | sendto_channel_butone(NULL, &me, chptr, ":%s NOTICE %s :The anonymous flag is being set on channel %s.", ME, chptr->chname, chptr->chname); | |
1622 | sendto_channel_butone(NULL, &me, chptr, ":%s NOTICE %s :Be aware that anonymity on IRC is NOT securely enforced!", ME, chptr->chname); | |
1623 | } | |
1624 | } | |
1625 | /* +r coming from server must trigger reop. If not | |
1626 | ** needed, it will be reset to 0 elsewhere, --B. */ | |
1627 | if (*ip == MODE_REOP && IsServer(sptr)) | |
1628 | { | |
1629 | chptr->reop = timeofday + LDELAYCHASETIMELIMIT; | |
1630 | } | |
1631 | *mbuf++ = *(ip+1); | |
1632 | } | |
1633 | ||
1634 | for (ip = flags; *ip; ip += 2) | |
1635 | if ((*ip & oldm.mode) && !(*ip & new)) | |
1636 | { | |
1637 | if (whatt != -1) | |
1638 | { | |
1639 | *mbuf++ = '-'; | |
1640 | whatt = -1; | |
1641 | } | |
1642 | if (ischop) | |
1643 | mode->mode &= ~*ip; | |
1644 | *mbuf++ = *(ip+1); | |
1645 | } | |
1646 | ||
1647 | if (limitset && !nusers && mode->limit) | |
1648 | { | |
1649 | if (whatt != -1) | |
1650 | { | |
1651 | *mbuf++ = '-'; | |
1652 | whatt = -1; | |
1653 | } | |
1654 | mode->mode &= ~MODE_LIMIT; | |
1655 | mode->limit = 0; | |
1656 | *mbuf++ = 'l'; | |
1657 | } | |
1658 | ||
1659 | /* | |
1660 | * Reconstruct "+beIRkOov" chain. | |
1661 | */ | |
1662 | if (opcnt) | |
1663 | { | |
1664 | Reg int i = 0; | |
1665 | Reg char c = '\0'; | |
1666 | char *user, *host, numeric[16]; | |
1667 | int tmplen; | |
1668 | ||
1669 | /* if (opcnt > MAXMODEPARAMS) | |
1670 | opcnt = MAXMODEPARAMS; | |
1671 | */ | |
1672 | for (; i < opcnt; i++) | |
1673 | { | |
1674 | lp = &chops[i]; | |
1675 | /* | |
1676 | * make sure we have correct mode change sign | |
1677 | */ | |
1678 | if (whatt != (lp->flags & (MODE_ADD|MODE_DEL))) | |
1679 | { | |
1680 | if (lp->flags & MODE_ADD) | |
1681 | { | |
1682 | *mbuf++ = '+'; | |
1683 | whatt = MODE_ADD; | |
1684 | } | |
1685 | else | |
1686 | { | |
1687 | *mbuf++ = '-'; | |
1688 | whatt = MODE_DEL; | |
1689 | } | |
1690 | } | |
1691 | len = strlen(pbuf); | |
1692 | ulen = strlen(upbuf); | |
1693 | /* | |
1694 | * get c as the mode char and tmp as a pointer to | |
1695 | * the paramter for this mode change. | |
1696 | */ | |
1697 | switch(lp->flags & MODE_WPARAS) | |
1698 | { | |
1699 | case MODE_CHANOP : | |
1700 | c = 'o'; | |
1701 | cp = lp->value.cptr->name; | |
1702 | ucp = lp->value.cptr->user ? | |
1703 | lp->value.cptr->user->uid : cp; | |
1704 | break; | |
1705 | case MODE_UNIQOP : | |
1706 | c = 'O'; | |
1707 | cp = lp->value.cptr->name; | |
1708 | ucp = lp->value.cptr->user ? | |
1709 | lp->value.cptr->user->uid : cp; | |
1710 | break; | |
1711 | case MODE_VOICE : | |
1712 | c = 'v'; | |
1713 | cp = lp->value.cptr->name; | |
1714 | ucp = lp->value.cptr->user ? | |
1715 | lp->value.cptr->user->uid : cp; | |
1716 | break; | |
1717 | case MODE_BAN : | |
1718 | case MODE_EXCEPTION : | |
1719 | case MODE_INVITE : | |
1720 | case MODE_REOPLIST : | |
1721 | switch(lp->flags & MODE_WPARAS) | |
1722 | { | |
1723 | case MODE_BAN : | |
1724 | c = 'b'; break; | |
1725 | case MODE_EXCEPTION : | |
1726 | c = 'e'; break; | |
1727 | case MODE_INVITE : | |
1728 | c = 'I'; break; | |
1729 | case MODE_REOPLIST : | |
1730 | c = 'R'; break; | |
1731 | } | |
1732 | /* parseNUH: */ | |
1733 | cp = lp->value.cp; | |
1734 | if ((user = index(cp, '!'))) | |
1735 | *user++ = '\0'; | |
1736 | if ((host = rindex(user ? user : cp, '@'))) | |
1737 | *host++ = '\0'; | |
1738 | lp->value.alist = make_bei(cp, user, host); | |
1739 | if (user) | |
1740 | user[-1] = '!'; | |
1741 | if (host) | |
1742 | host[-1] = '@'; | |
1743 | break; | |
1744 | case MODE_KEY : | |
1745 | c = 'k'; | |
1746 | cp = lp->value.cp; | |
1747 | break; | |
1748 | case MODE_LIMIT : | |
1749 | c = 'l'; | |
1750 | (void)sprintf(numeric, "%-15d", nusers); | |
1751 | if ((cp = index(numeric, ' '))) | |
1752 | *cp = '\0'; | |
1753 | cp = numeric; | |
1754 | break; | |
1755 | } | |
1756 | ||
1757 | switch(lp->flags & MODE_WPARAS) | |
1758 | { | |
1759 | case MODE_BAN : | |
1760 | case MODE_EXCEPTION : | |
1761 | case MODE_INVITE : | |
1762 | case MODE_REOPLIST : | |
1763 | tmplen = BanLen(lp->value.alist) + 2 /* !@ */; | |
1764 | if (len + tmplen + 2 > (size_t) MODEBUFLEN) | |
1765 | { | |
1766 | free_bei(lp->value.alist); | |
1767 | tmplen = -1; | |
1768 | } | |
1769 | break; | |
1770 | default: | |
1771 | tmplen = strlen(cp); | |
1772 | if (len + tmplen + 2 > (size_t) MODEBUFLEN) | |
1773 | { | |
1774 | tmplen = -1; | |
1775 | } | |
1776 | } | |
1777 | ||
1778 | if (tmplen == -1) | |
1779 | break; | |
1780 | /* | |
1781 | * pass on +/-o/v regardless of whether they are | |
1782 | * redundant or effective but check +b's to see if | |
1783 | * it existed before we created it. | |
1784 | */ | |
1785 | switch(lp->flags & MODE_WPARAS) | |
1786 | { | |
1787 | case MODE_KEY : | |
1788 | if (IsServer(sptr) && | |
1789 | !strncmp(mode->key, cp, (size_t) KEYLEN)) | |
1790 | break; | |
1791 | *mbuf++ = c; | |
1792 | (void)strcat(pbuf, cp); | |
1793 | (void)strcat(upbuf, cp); | |
1794 | len += strlen(cp); | |
1795 | ulen += strlen(cp); | |
1796 | (void)strcat(pbuf, " "); | |
1797 | (void)strcat(upbuf, " "); | |
1798 | len++; | |
1799 | ulen++; | |
1800 | if (!ischop) | |
1801 | break; | |
1802 | if (strlen(cp) > (size_t) KEYLEN) | |
1803 | *(cp+KEYLEN) = '\0'; | |
1804 | if (whatt == MODE_ADD) | |
1805 | strncpyzt(mode->key, cp, | |
1806 | sizeof(mode->key)); | |
1807 | else | |
1808 | *mode->key = '\0'; | |
1809 | break; | |
1810 | case MODE_LIMIT : | |
1811 | if (IsServer(sptr) && mode->limit == nusers) | |
1812 | break; | |
1813 | *mbuf++ = c; | |
1814 | (void)strcat(pbuf, cp); | |
1815 | (void)strcat(upbuf, cp); | |
1816 | len += strlen(cp); | |
1817 | ulen += strlen(cp); | |
1818 | (void)strcat(pbuf, " "); | |
1819 | (void)strcat(upbuf, " "); | |
1820 | len++; | |
1821 | ulen++; | |
1822 | if (!ischop) | |
1823 | break; | |
1824 | mode->limit = nusers; | |
1825 | break; | |
1826 | case MODE_CHANOP : /* fall through case */ | |
1827 | if (ischop && lp->value.cptr == sptr && | |
1828 | lp->flags == (MODE_CHANOP|MODE_DEL)) | |
1829 | { | |
1830 | chptr->reop = timeofday + | |
1831 | LDELAYCHASETIMELIMIT + | |
1832 | myrand() % 300; | |
1833 | } | |
1834 | case MODE_UNIQOP : | |
1835 | case MODE_VOICE : | |
1836 | *mbuf++ = c; | |
1837 | (void)strcat(pbuf, cp); | |
1838 | (void)strcat(upbuf, ucp); | |
1839 | len += strlen(cp); | |
1840 | ulen += strlen(ucp); | |
1841 | (void)strcat(pbuf, " "); | |
1842 | (void)strcat(upbuf, " "); | |
1843 | len++; | |
1844 | ulen++; | |
1845 | if (ischop) | |
1846 | change_chan_flag(lp, chptr); | |
1847 | break; | |
1848 | case MODE_BAN : | |
1849 | case MODE_EXCEPTION : | |
1850 | case MODE_INVITE : | |
1851 | case MODE_REOPLIST : | |
1852 | switch(lp->flags & MODE_WPARAS) | |
1853 | { | |
1854 | case MODE_BAN : | |
1855 | tmp_chfl = CHFL_BAN; break; | |
1856 | case MODE_EXCEPTION : | |
1857 | tmp_chfl = CHFL_EXCEPTION; break; | |
1858 | case MODE_INVITE : | |
1859 | tmp_chfl = CHFL_INVITE; break; | |
1860 | case MODE_REOPLIST : | |
1861 | tmp_chfl = CHFL_REOPLIST; break; | |
1862 | } | |
1863 | if (tmp_chfl == CHFL_REOPLIST && | |
1864 | (whatt & MODE_ADD)) | |
1865 | { | |
1866 | /* Just restarted servers will not have | |
1867 | ** chanops leaving, so no other way to | |
1868 | ** set ->reop. As we prefer not to op | |
1869 | ** remote clients, set this here, upon | |
1870 | ** each +R from remote server, so that | |
1871 | ** reop_channel has a chance to work. | |
1872 | ** It's mostly harmless, as chptr->reop | |
1873 | ** will be reset to 0 in is_chan_op() | |
1874 | ** and even if not, reop_channel() will | |
1875 | ** NOT give ops if ops are already on | |
1876 | ** the channel. --B. */ | |
1877 | if (IsServer(sptr)) | |
1878 | { | |
1879 | chptr->reop = timeofday + | |
1880 | LDELAYCHASETIMELIMIT + | |
1881 | myrand() % 300; | |
1882 | } | |
1883 | } | |
1884 | if (ischop && | |
1885 | (((whatt & MODE_ADD) && | |
1886 | !add_modeid(tmp_chfl, sptr, chptr, | |
1887 | lp->value.alist))|| | |
1888 | ((whatt & MODE_DEL) && | |
1889 | !del_modeid(tmp_chfl, chptr, | |
1890 | lp->value.alist)))) | |
1891 | { | |
1892 | char nuh[NICKLEN+USERLEN+HOSTLEN+3]; | |
1893 | ||
1894 | /* I could strcat on u/pbuf directly, | |
1895 | ** but this looks nicer. Note that alist | |
1896 | ** values were already cleaned. --B. */ | |
1897 | tmplen = sprintf(nuh, "%s!%s@%s", | |
1898 | lp->value.alist->nick, | |
1899 | lp->value.alist->user, | |
1900 | lp->value.alist->host); | |
1901 | *mbuf++ = c; | |
1902 | (void)strcat(pbuf, nuh); | |
1903 | (void)strcat(upbuf, nuh); | |
1904 | len += tmplen; | |
1905 | ulen += tmplen; | |
1906 | (void)strcat(pbuf, " "); | |
1907 | (void)strcat(upbuf, " "); | |
1908 | len++; | |
1909 | ulen++; | |
1910 | if ((whatt & MODE_DEL)) | |
1911 | free_bei(lp->value.alist); | |
1912 | } | |
1913 | else | |
1914 | { | |
1915 | /* We have to free lp->value.alist | |
1916 | ** allocated by make_bei, otherwise | |
1917 | ** it is memleak. del_modeid always | |
1918 | ** succeeds, so it is freed above. | |
1919 | ** If add_modeid succeeds, it uses | |
1920 | ** pointer, if not, we free it here. | |
1921 | ** This also covers all other cases, | |
1922 | ** like !ischop. --B. */ | |
1923 | free_bei(lp->value.alist); | |
1924 | } | |
1925 | break; | |
1926 | } | |
1927 | } /* for (; i < opcnt; i++) */ | |
1928 | } /* if (opcnt) */ | |
1929 | ||
1930 | *mbuf = '\0'; | |
1931 | mbuf = modebuf; | |
1932 | ||
1933 | if ((!ischop) && (count) && MyConnect(sptr) && !IsServer(sptr)) | |
1934 | { | |
1935 | /* rejected mode change */ | |
1936 | int num = ERR_CHANOPRIVSNEEDED; | |
1937 | ||
1938 | if (IsClient(sptr) && IsRestricted(sptr)) | |
1939 | { | |
1940 | num = ERR_RESTRICTED; | |
1941 | } | |
1942 | sendto_one(sptr, replies[num], ME, sptr->name, chptr->chname); | |
1943 | return -count; | |
1944 | } | |
1945 | ||
1946 | /* Send the mode changes out. */ | |
1947 | if (strlen(modebuf) > 1) | |
1948 | { | |
1949 | char *s; /* Sender for messages to 2.11s. */ | |
1950 | ||
1951 | if (IsServer(sptr)) | |
1952 | { | |
1953 | s = sptr->serv->sid; | |
1954 | } | |
1955 | else if (sptr->user) | |
1956 | { | |
1957 | s = sptr->user->uid; | |
1958 | } | |
1959 | else | |
1960 | { | |
1961 | s = sptr->name; | |
1962 | } | |
1963 | ||
1964 | sendto_match_servs_v(chptr, cptr, SV_UID, | |
1965 | ":%s MODE %s %s %s", s, chptr->chname, mbuf, upbuf); | |
1966 | ||
1967 | if ((IsServer(cptr) && !IsServer(sptr) && !ischop)) | |
1968 | { | |
1969 | sendto_flag(SCH_CHAN, "Fake: %s MODE %s %s %s", | |
1970 | sptr->name, chptr->chname, mbuf, pbuf); | |
1971 | ircstp->is_fake++; | |
1972 | } | |
1973 | else | |
1974 | { | |
1975 | sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s", | |
1976 | sptr->name, chptr->chname, mbuf, pbuf); | |
1977 | #ifdef USE_SERVICES | |
1978 | *modebuf = *parabuf = '\0'; | |
1979 | modebuf[1] = '\0'; | |
1980 | channel_modes(&me, modebuf, parabuf, chptr); | |
1981 | check_services_butone(SERVICE_WANT_MODE, NULL, sptr, | |
1982 | "MODE %s %s", chptr->chname, modebuf); | |
1983 | #endif | |
1984 | } | |
1985 | } | |
1986 | ||
1987 | return ischop ? count : -count; | |
1988 | } | |
1989 | ||
1990 | static int can_join(aClient *sptr, aChannel *chptr, char *key) | |
1991 | { | |
1992 | invLink *lp = NULL; | |
1993 | Link *banned; | |
1994 | int limit = 0; | |
1995 | ||
1996 | if (chptr->users == 0 && (bootopt & BOOT_PROT) && | |
1997 | chptr->history != 0 && *chptr->chname != '!') | |
1998 | return (timeofday > chptr->history) ? 0 : ERR_UNAVAILRESOURCE; | |
1999 | ||
2000 | #ifdef CLIENTS_CHANNEL | |
2001 | if (*chptr->chname == '&' && !strcmp(chptr->chname, "&CLIENTS") | |
2002 | && is_allowed(sptr, ACL_CLIENTS)) | |
2003 | return 0; | |
2004 | #endif | |
2005 | if (*chptr->chname == '&' && !strcmp(chptr->chname, "&OPER") | |
2006 | && IsAnOper(sptr)) | |
2007 | return 0; | |
2008 | ||
2009 | for (lp = sptr->user->invited; lp; lp = lp->next) | |
2010 | if (lp->chptr == chptr) | |
2011 | break; | |
2012 | ||
2013 | if ((banned = match_modeid(CHFL_BAN, sptr, chptr))) | |
2014 | { | |
2015 | if (match_modeid(CHFL_EXCEPTION, sptr, chptr)) | |
2016 | { | |
2017 | banned = NULL; | |
2018 | } | |
2019 | else if (lp == NULL) /* not invited */ | |
2020 | { | |
2021 | return (ERR_BANNEDFROMCHAN); | |
2022 | } | |
2023 | } | |
2024 | ||
2025 | if ((chptr->mode.mode & MODE_INVITEONLY) | |
2026 | && !match_modeid(CHFL_INVITE, sptr, chptr) | |
2027 | && (lp == NULL)) | |
2028 | return (ERR_INVITEONLYCHAN); | |
2029 | ||
2030 | if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key))) | |
2031 | return (ERR_BADCHANNELKEY); | |
2032 | ||
2033 | if (chptr->mode.limit && (chptr->users >= chptr->mode.limit)) | |
2034 | { | |
2035 | /* ->reop is set when there are no chanops on the channel, | |
2036 | ** so we allow people matching +R to join no matter limit, | |
2037 | ** so they can get reopped --B. */ | |
2038 | if (chptr->reop > 0 && match_modeid(CHFL_REOPLIST, sptr, chptr)) | |
2039 | return 0; | |
2040 | if (lp == NULL) | |
2041 | return (ERR_CHANNELISFULL); | |
2042 | else | |
2043 | limit = 1; | |
2044 | } | |
2045 | ||
2046 | if (banned) | |
2047 | { | |
2048 | sendto_channel_butone(&me, &me, chptr, | |
2049 | ":%s NOTICE %s :%s carries an invitation from %s" | |
2050 | " (overriding%s ban on %s!%s@%s).", | |
2051 | ME, chptr->chname, sptr->name, lp->who, | |
2052 | limit ? " channel limit and" : "", | |
2053 | banned->value.alist->nick, | |
2054 | banned->value.alist->user, | |
2055 | banned->value.alist->host); | |
2056 | } | |
2057 | else if (limit) | |
2058 | { | |
2059 | sendto_channel_butone(&me, &me, chptr, | |
2060 | ":%s NOTICE %s :%s carries an invitation from %s" | |
2061 | " (overriding channel limit).", ME, chptr->chname, | |
2062 | sptr->name, lp->who); | |
2063 | } | |
2064 | return 0; | |
2065 | } | |
2066 | ||
2067 | /* | |
2068 | ** Remove bells and commas from channel name | |
2069 | */ | |
2070 | ||
2071 | int clean_channelname(char *cn) | |
2072 | { | |
2073 | int flag = 0; | |
2074 | ||
2075 | while (*cn) | |
2076 | { | |
2077 | if (*cn == '\007' || *cn == ' ' || (!flag && *cn == ',')) | |
2078 | { | |
2079 | *cn = '\0'; | |
2080 | return 0; | |
2081 | } | |
2082 | #ifdef JAPANESE | |
2083 | /* Japanese channel names can have comma in their name, but | |
2084 | ** only between "\033$B" (begin) and "\033(B" (end) markers. | |
2085 | ** So we mark it (using flag) for above check. --Beeth */ | |
2086 | if (cn[0] == '\033' | |
2087 | && (cn[1] == '$' || cn[1] == '(') | |
2088 | && cn[2] == 'B') | |
2089 | { | |
2090 | flag = (cn[1] == '$') ? 1 : 0; | |
2091 | cn += 2; | |
2092 | } | |
2093 | #endif | |
2094 | cn++; | |
2095 | } | |
2096 | /* If flag is 1 here, Japanese channel name is incomplete! */ | |
2097 | return flag; | |
2098 | } | |
2099 | ||
2100 | /* | |
2101 | ** Return -1 if mask is present and doesnt match our server name. | |
2102 | */ | |
2103 | static int check_channelmask(aClient *sptr, aClient *cptr, char *chname) | |
2104 | { | |
2105 | char *s; | |
2106 | ||
2107 | if (*chname == '&' && IsServer(cptr)) | |
2108 | return -1; | |
2109 | s = get_channelmask(chname); | |
2110 | if (!s) | |
2111 | return 0; | |
2112 | s++; | |
2113 | if (*s == '\0' /* ':' was last char, thus empty mask --B. */ | |
2114 | || match(s, ME) || (IsServer(cptr) && match(s, cptr->name))) | |
2115 | { | |
2116 | if (MyClient(sptr)) | |
2117 | sendto_one(sptr, replies[ERR_BADCHANMASK], ME, | |
2118 | BadTo(sptr->name), chname); | |
2119 | return -1; | |
2120 | } | |
2121 | return 0; | |
2122 | } | |
2123 | ||
2124 | /* | |
2125 | ** Get Channel block for i (and allocate a new channel | |
2126 | ** block, if it didn't exists before). | |
2127 | */ | |
2128 | static aChannel *get_channel(aClient *cptr, char *chname, int flag) | |
2129 | { | |
2130 | Reg aChannel *chptr; | |
2131 | int len; | |
2132 | ||
2133 | if (BadPtr(chname)) | |
2134 | return NULL; | |
2135 | ||
2136 | len = strlen(chname); | |
2137 | if (MyClient(cptr) && len > CHANNELLEN) | |
2138 | { | |
2139 | len = CHANNELLEN; | |
2140 | *(chname+CHANNELLEN) = '\0'; | |
2141 | #ifdef JAPANESE | |
2142 | #if 0 | |
2143 | /* XXX-JP: I think I know why it is here, but it | |
2144 | ** seems it is completely unneeded. */ | |
2145 | if (check_channelmask(cptr, cptr, chname) == -1) | |
2146 | return NULL; | |
2147 | #endif | |
2148 | #endif | |
2149 | } | |
2150 | if ((chptr = find_channel(chname, (aChannel *)NULL))) | |
2151 | return (chptr); | |
2152 | if (flag == CREATE) | |
2153 | { | |
2154 | chptr = (aChannel *)MyMalloc(sizeof(aChannel) + len); | |
2155 | bzero((char *)chptr, sizeof(aChannel)); | |
2156 | strncpyzt(chptr->chname, chname, len+1); | |
2157 | if (channel) | |
2158 | channel->prevch = chptr; | |
2159 | chptr->prevch = NULL; | |
2160 | chptr->nextch = channel; | |
2161 | chptr->history = 0; | |
2162 | #ifdef JAPANESE | |
2163 | chptr->flags = 0; | |
2164 | if (jp_chname(chname)) | |
2165 | chptr->flags = FLAGS_JP; | |
2166 | #endif | |
2167 | channel = chptr; | |
2168 | (void)add_to_channel_hash_table(chname, chptr); | |
2169 | } | |
2170 | return chptr; | |
2171 | } | |
2172 | ||
2173 | /* | |
2174 | * add_invite(): | |
2175 | * sptr: who invites | |
2176 | * cptr who gets invitation | |
2177 | * chptr what channel | |
2178 | */ | |
2179 | static void add_invite(aClient *sptr, aClient *cptr, aChannel *chptr) | |
2180 | { | |
2181 | ||
2182 | /* | |
2183 | assert(sptr!=NULL); | |
2184 | assert(cptr!=NULL); | |
2185 | assert(chptr!=NULL); | |
2186 | */ | |
2187 | ||
2188 | del_invite(cptr, chptr); | |
2189 | ||
2190 | /* | |
2191 | * delete last link in chain if the list is max length | |
2192 | */ | |
2193 | if (list_length(cptr->user->invited) >= MAXCHANNELSPERUSER) | |
2194 | { | |
2195 | del_invite(cptr, cptr->user->invited->chptr); | |
2196 | } | |
2197 | ||
2198 | /* | |
2199 | * add client to channel invite list | |
2200 | */ | |
2201 | { | |
2202 | Reg Link *inv; | |
2203 | ||
2204 | inv = make_link(); | |
2205 | inv->value.cptr = cptr; | |
2206 | inv->next = chptr->invites; | |
2207 | chptr->invites = inv; | |
2208 | istat.is_useri++; | |
2209 | } | |
2210 | /* | |
2211 | * add channel to the end of the client invite list | |
2212 | */ | |
2213 | { | |
2214 | Reg invLink *inv, **tmp; | |
2215 | char who[NICKLEN+USERLEN+HOSTLEN+3]; | |
2216 | int len; | |
2217 | ||
2218 | for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next)) | |
2219 | ; | |
2220 | inv = make_invlink(); | |
2221 | (*tmp) = inv; | |
2222 | inv->chptr = chptr; | |
2223 | inv->next = NULL; | |
2224 | len = sprintf(who, "%s!%s@%s", sptr->name, | |
2225 | sptr->user->username, sptr->user->host); | |
2226 | inv->who = (char *)MyMalloc(len + 1); | |
2227 | istat.is_banmem += len; | |
2228 | strcpy(inv->who, who); | |
2229 | istat.is_invite++; | |
2230 | } | |
2231 | } | |
2232 | ||
2233 | /* | |
2234 | * Delete Invite block from channel invite list and client invite list | |
2235 | */ | |
2236 | void del_invite(aClient *cptr, aChannel *chptr) | |
2237 | { | |
2238 | { | |
2239 | Reg Link **inv, *tmp; | |
2240 | ||
2241 | for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next) | |
2242 | { | |
2243 | if (tmp->value.cptr == cptr) | |
2244 | { | |
2245 | *inv = tmp->next; | |
2246 | free_link(tmp); | |
2247 | istat.is_invite--; | |
2248 | break; | |
2249 | } | |
2250 | } | |
2251 | } | |
2252 | { | |
2253 | Reg invLink **inv, *tmp; | |
2254 | ||
2255 | for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next) | |
2256 | { | |
2257 | if (tmp->chptr == chptr) | |
2258 | { | |
2259 | *inv = tmp->next; | |
2260 | istat.is_banmem -= (strlen(tmp->who)+1); | |
2261 | free(tmp->who); | |
2262 | free_invlink(tmp); | |
2263 | istat.is_useri--; | |
2264 | break; | |
2265 | } | |
2266 | } | |
2267 | } | |
2268 | } | |
2269 | ||
2270 | /* | |
2271 | ** The last user has left the channel, free data in the channel block, | |
2272 | ** and eventually the channel block itself. | |
2273 | */ | |
2274 | static void free_channel(aChannel *chptr) | |
2275 | { | |
2276 | Reg Link *tmp; | |
2277 | Link *obtmp; | |
2278 | int len = sizeof(aChannel) + strlen(chptr->chname), now = 0; | |
2279 | ||
2280 | if (chptr->history == 0 || timeofday >= chptr->history) | |
2281 | /* no lock, nor expired lock, channel is no more, free it */ | |
2282 | now = 1; | |
2283 | ||
2284 | if (*chptr->chname != '!' || now) | |
2285 | { | |
2286 | while ((tmp = chptr->invites)) | |
2287 | del_invite(tmp->value.cptr, chptr); | |
2288 | ||
2289 | tmp = chptr->mlist; | |
2290 | while (tmp) | |
2291 | { | |
2292 | obtmp = tmp; | |
2293 | tmp = tmp->next; | |
2294 | istat.is_banmem -= BanLen(obtmp->value.alist); | |
2295 | istat.is_bans--; | |
2296 | free_bei(obtmp->value.alist); | |
2297 | free_link(obtmp); | |
2298 | } | |
2299 | chptr->mlist = NULL; | |
2300 | } | |
2301 | ||
2302 | if (now) | |
2303 | { | |
2304 | istat.is_hchan--; | |
2305 | istat.is_hchanmem -= len; | |
2306 | if (chptr->prevch) | |
2307 | chptr->prevch->nextch = chptr->nextch; | |
2308 | else | |
2309 | channel = chptr->nextch; | |
2310 | if (chptr->nextch) | |
2311 | chptr->nextch->prevch = chptr->prevch; | |
2312 | del_from_channel_hash_table(chptr->chname, chptr); | |
2313 | ||
2314 | if (*chptr->chname == '!' && close_chid(chptr->chname+1)) | |
2315 | cache_chid(chptr); | |
2316 | else | |
2317 | MyFree(chptr); | |
2318 | } | |
2319 | } | |
2320 | ||
2321 | /* | |
2322 | ** m_join | |
2323 | ** parv[0] = sender prefix | |
2324 | ** parv[1] = channel | |
2325 | ** parv[2] = channel password (key) | |
2326 | */ | |
2327 | int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
2328 | { | |
2329 | static char jbuf[BUFSIZE]; | |
2330 | Reg Link *lp; | |
2331 | Reg aChannel *chptr; | |
2332 | Reg char *name, *key = NULL; | |
2333 | int i, tmplen, flags = 0; | |
2334 | char *p = NULL, *p2 = NULL; | |
2335 | ||
2336 | /* This is the only case we get JOIN over s2s link. --B. */ | |
2337 | /* It could even be its own command. */ | |
2338 | if (IsServer(cptr)) | |
2339 | { | |
2340 | if (parv[1][0] == '0' && parv[1][1] == '\0') | |
2341 | { | |
2342 | if (sptr->user->channel == NULL) | |
2343 | return 0; | |
2344 | while ((lp = sptr->user->channel)) | |
2345 | { | |
2346 | chptr = lp->value.chptr; | |
2347 | sendto_channel_butserv(chptr, sptr, | |
2348 | PartFmt, | |
2349 | parv[0], chptr->chname, | |
2350 | key ? key : ""); | |
2351 | remove_user_from_channel(sptr, chptr); | |
2352 | } | |
2353 | sendto_match_servs(NULL, cptr, ":%s JOIN 0 :%s", | |
2354 | sptr->user->uid, key ? key : parv[0]); | |
2355 | } | |
2356 | else | |
2357 | { | |
2358 | /* Well, technically this is an error. | |
2359 | ** Let's ignore it for now. --B. */ | |
2360 | } | |
2361 | return 0; | |
2362 | } | |
2363 | /* These should really be assert()s. */ | |
2364 | if (!sptr || !sptr->user) | |
2365 | return 0; | |
2366 | ||
2367 | *jbuf = '\0'; | |
2368 | /* | |
2369 | ** Rebuild list of channels joined to be the actual result of the | |
2370 | ** JOIN. Note that "JOIN 0" is the destructive problem. | |
2371 | ** Also note that this can easily trash the correspondance between | |
2372 | ** parv[1] and parv[2] lists. | |
2373 | */ | |
2374 | for (i = 0, name = strtoken(&p, parv[1], ","); name; | |
2375 | name = strtoken(&p, NULL, ",")) | |
2376 | { | |
2377 | if (check_channelmask(sptr, cptr, name)==-1) | |
2378 | continue; | |
2379 | if (*name == '0' && !atoi(name)) | |
2380 | { | |
2381 | (void)strcpy(jbuf, "0"); | |
2382 | i = 1; | |
2383 | continue; | |
2384 | } | |
2385 | if (clean_channelname(name) == -1) | |
2386 | { | |
2387 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], | |
2388 | ME, BadTo(parv[0]), name); | |
2389 | continue; | |
2390 | } | |
2391 | if (*name == '!') | |
2392 | { | |
2393 | chptr = NULL; | |
2394 | /* | |
2395 | ** !channels are special: | |
2396 | ** !!channel is supposed to be a new channel, | |
2397 | ** and requires a unique name to be built. | |
2398 | ** ( !#channel is obsolete ) | |
2399 | ** !channel cannot be created, and must already | |
2400 | ** exist. | |
2401 | */ | |
2402 | if (*(name+1) == '\0' || | |
2403 | (*(name+1) == '#' && *(name+2) == '\0') || | |
2404 | (*(name+1) == '!' && *(name+2) == '\0')) | |
2405 | { | |
2406 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], | |
2407 | ME, BadTo(parv[0]), name); | |
2408 | continue; | |
2409 | } | |
2410 | if (*name == '!' && (*(name+1) == '#' || | |
2411 | *(name+1) == '!')) | |
2412 | { | |
2413 | chptr = hash_find_channels(name+2, NULL); | |
2414 | if (chptr) | |
2415 | { | |
2416 | sendto_one(sptr, | |
2417 | replies[ERR_TOOMANYTARGETS], | |
2418 | ME, BadTo(parv[0]), | |
2419 | "Duplicate", name, | |
2420 | "Join aborted."); | |
2421 | continue; | |
2422 | } | |
2423 | if (check_chid(name+2)) | |
2424 | { | |
2425 | /* | |
2426 | * This is a bit wrong: if a channel | |
2427 | * rightfully ceases to exist, it | |
2428 | * can still be *locked* for up to | |
2429 | * 2*CHIDNB^3 seconds (~24h) | |
2430 | * Is it a reasonnable price to pay to | |
2431 | * ensure shortname uniqueness? -kalt | |
2432 | */ | |
2433 | sendto_one(sptr, replies[ERR_UNAVAILRESOURCE], | |
2434 | ME, BadTo(parv[0]), name); | |
2435 | continue; | |
2436 | } | |
2437 | sprintf(buf, "!%.*s%s", CHIDLEN, get_chid(), | |
2438 | name+2); | |
2439 | name = buf; | |
2440 | } | |
2441 | else if (!find_channel(name, NullChn) && | |
2442 | !(*name == '!' && *name != 0 && | |
2443 | (chptr = hash_find_channels(name+1, NULL)))) | |
2444 | { | |
2445 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], | |
2446 | ME, BadTo(parv[0]), name); | |
2447 | continue; | |
2448 | } | |
2449 | else if (chptr) | |
2450 | { | |
2451 | /* joining a !channel using the short name */ | |
2452 | if (hash_find_channels(name+1, chptr)) | |
2453 | { | |
2454 | sendto_one(sptr, | |
2455 | replies[ERR_TOOMANYTARGETS], | |
2456 | ME, BadTo(parv[0]), | |
2457 | "Duplicate", name, | |
2458 | "Join aborted."); | |
2459 | continue; | |
2460 | } | |
2461 | name = chptr->chname; | |
2462 | } | |
2463 | } | |
2464 | if (!IsChannelName(name) || | |
2465 | (*name == '+' && (*(name+1) == '#' || *(name+1) == '!')) || | |
2466 | (*name == '!' && IsChannelName(name+1))) | |
2467 | { | |
2468 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], | |
2469 | ME, BadTo(parv[0]), name); | |
2470 | continue; | |
2471 | } | |
2472 | tmplen = strlen(name); | |
2473 | if (i + tmplen + 2 /* comma and \0 */ | |
2474 | >= sizeof(jbuf) ) | |
2475 | { | |
2476 | break; | |
2477 | } | |
2478 | if (*jbuf) | |
2479 | { | |
2480 | jbuf[i++] = ','; | |
2481 | } | |
2482 | (void)strcpy(jbuf + i, name); | |
2483 | i += tmplen; | |
2484 | } | |
2485 | ||
2486 | p = NULL; | |
2487 | if (parv[2]) | |
2488 | key = strtoken(&p2, parv[2], ","); | |
2489 | for (name = strtoken(&p, jbuf, ","); name; | |
2490 | key = (key) ? strtoken(&p2, NULL, ",") : NULL, | |
2491 | name = strtoken(&p, NULL, ",")) | |
2492 | { | |
2493 | /* | |
2494 | ** JOIN 0 sends out a part for all channels a user | |
2495 | ** has joined. | |
2496 | */ | |
2497 | if (*name == '0' && !atoi(name)) | |
2498 | { | |
2499 | if (sptr->user->channel == NULL) | |
2500 | continue; | |
2501 | while ((lp = sptr->user->channel)) | |
2502 | { | |
2503 | chptr = lp->value.chptr; | |
2504 | sendto_channel_butserv(chptr, sptr, | |
2505 | PartFmt, | |
2506 | parv[0], chptr->chname, | |
2507 | key ? key : ""); | |
2508 | remove_user_from_channel(sptr, chptr); | |
2509 | } | |
2510 | sendto_match_servs(NULL, cptr, ":%s JOIN 0 :%s", | |
2511 | sptr->user->uid, key ? key : parv[0]); | |
2512 | continue; | |
2513 | } | |
2514 | ||
2515 | /* Weren't those names just cleaned? --B. */ | |
2516 | if (clean_channelname(name) == -1) | |
2517 | continue; | |
2518 | ||
2519 | /* Get chptr for given name. Do not create channel yet. | |
2520 | ** Can return NULL. */ | |
2521 | chptr = get_channel(sptr, name, !CREATE); | |
2522 | ||
2523 | if (chptr && IsMember(sptr, chptr)) | |
2524 | { | |
2525 | continue; | |
2526 | } | |
2527 | ||
2528 | if (MyConnect(sptr) && !(chptr && IsQuiet(chptr)) && | |
2529 | sptr->user->joined >= MAXCHANNELSPERUSER) | |
2530 | { | |
2531 | sendto_one(sptr, replies[ERR_TOOMANYCHANNELS], | |
2532 | ME, BadTo(parv[0]), name); | |
2533 | /* can't return, need to send the info everywhere */ | |
2534 | continue; | |
2535 | } | |
2536 | ||
2537 | if (!strncmp(name, "\x23\x1f\x02\xb6\x03\x34\x63\x68\x02\x1f", | |
2538 | 10)) | |
2539 | { | |
2540 | sptr->exitc = EXITC_VIRUS; | |
2541 | return exit_client(sptr, sptr, &me, "Virus Carrier"); | |
2542 | } | |
2543 | ||
2544 | if (!chptr) | |
2545 | { | |
2546 | /* Oh well, create the channel. */ | |
2547 | chptr = get_channel(sptr, name, CREATE); | |
2548 | } | |
2549 | ||
2550 | if (!chptr) | |
2551 | { | |
2552 | /* Should NEVER happen. */ | |
2553 | sendto_flag(SCH_ERROR, "Could not create channel!"); | |
2554 | sendto_one(sptr, "%s *** %s :Could not create channel!", | |
2555 | ME, BadTo(parv[0])); | |
2556 | continue; | |
2557 | } | |
2558 | ||
2559 | if ((i = can_join(sptr, chptr, key))) | |
2560 | { | |
2561 | sendto_one(sptr, replies[i], ME, BadTo(parv[0]), name); | |
2562 | continue; | |
2563 | } | |
2564 | ||
2565 | /* | |
2566 | ** local client is first to enter previously nonexistant | |
2567 | ** channel so make them (rightfully) the Channel | |
2568 | ** Operator. | |
2569 | */ | |
2570 | flags = 0; | |
2571 | if (UseModes(name) && | |
2572 | (*name != '#' || !IsSplit()) && | |
2573 | (!IsRestricted(sptr) || (*name == '&')) && !chptr->users && | |
2574 | !(chptr->history && *chptr->chname == '!')) | |
2575 | { | |
2576 | if (*name == '!') | |
2577 | flags |= CHFL_UNIQOP|CHFL_CHANOP; | |
2578 | else | |
2579 | flags |= CHFL_CHANOP; | |
2580 | } | |
2581 | /* Complete user entry to the new channel */ | |
2582 | add_user_to_channel(chptr, sptr, flags); | |
2583 | /* Notify all users on the channel */ | |
2584 | sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", | |
2585 | parv[0], chptr->chname); | |
2586 | ||
2587 | del_invite(sptr, chptr); | |
2588 | if (chptr->topic[0] != '\0') | |
2589 | { | |
2590 | sendto_one(sptr, replies[RPL_TOPIC], ME, | |
2591 | BadTo(parv[0]), chptr->chname, chptr->topic); | |
2592 | #ifdef TOPIC_WHO_TIME | |
2593 | if (chptr->topic_t > 0) | |
2594 | { | |
2595 | sendto_one(sptr, replies[RPL_TOPIC_WHO_TIME], | |
2596 | ME, BadTo(parv[0]), | |
2597 | chptr->chname, IsAnonymous(chptr) ? | |
2598 | "anonymous!anonymous@anonymous." : | |
2599 | chptr->topic_nuh, | |
2600 | chptr->topic_t); | |
2601 | } | |
2602 | #endif | |
2603 | } | |
2604 | ||
2605 | names_channel(cptr, sptr, parv[0], chptr, 1); | |
2606 | if (IsAnonymous(chptr) && !IsQuiet(chptr)) | |
2607 | { | |
2608 | sendto_one(sptr, ":%s NOTICE %s :Channel %s has the anonymous flag set.", ME, chptr->chname, chptr->chname); | |
2609 | sendto_one(sptr, ":%s NOTICE %s :Be aware that anonymity on IRC is NOT securely enforced!", ME, chptr->chname); | |
2610 | } | |
2611 | /* | |
2612 | ** notify other servers | |
2613 | */ | |
2614 | if (get_channelmask(name) || *chptr->chname == '!' /* compat */ | |
2615 | #ifdef JAPANESE | |
2616 | /* sendto_match_servs_v() is checking the same | |
2617 | ** and NOT sending things out. --B. */ | |
2618 | || !jp_valid(NULL, chptr, NULL) | |
2619 | #endif | |
2620 | ) | |
2621 | { | |
2622 | sendto_match_servs_v(chptr, cptr, SV_UID, | |
2623 | ":%s NJOIN %s :%s%s", me.serv->sid, name, | |
2624 | flags & CHFL_UNIQOP ? "@@" : | |
2625 | flags & CHFL_CHANOP ? "@" : "", | |
2626 | sptr->user ? sptr->user->uid : parv[0]); | |
2627 | } | |
2628 | else if (*chptr->chname != '&') | |
2629 | { | |
2630 | sendto_serv_v(cptr, SV_UID, ":%s NJOIN %s :%s%s", | |
2631 | me.serv->sid, name, | |
2632 | flags & CHFL_UNIQOP ? "@@" : | |
2633 | flags & CHFL_CHANOP ? "@" : "", | |
2634 | sptr->user ? sptr->user->uid : parv[0]); | |
2635 | } | |
2636 | } | |
2637 | return 2; | |
2638 | } | |
2639 | ||
2640 | /* | |
2641 | ** m_njoin | |
2642 | ** parv[0] = sender prefix | |
2643 | ** parv[1] = channel | |
2644 | ** parv[2] = channel members and modes | |
2645 | */ | |
2646 | int m_njoin(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
2647 | { | |
2648 | char *name, *target; | |
2649 | char mbuf[3] /* "ov" */; | |
2650 | char uidbuf[BUFSIZE], *u; | |
2651 | char *p = NULL; | |
2652 | int chop, cnt = 0; | |
2653 | aChannel *chptr = NULL; | |
2654 | aClient *acptr; | |
2655 | int maxlen; | |
2656 | ||
2657 | /* get channel pointer */ | |
2658 | if (!IsChannelName(parv[1])) | |
2659 | { | |
2660 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], | |
2661 | ME, BadTo(parv[0]), parv[1]); | |
2662 | return 0; | |
2663 | } | |
2664 | if (check_channelmask(sptr, cptr, parv[1]) == -1) | |
2665 | { | |
2666 | sendto_flag(SCH_DEBUG, "received NJOIN for %s from %s", | |
2667 | parv[1], get_client_name(cptr, TRUE)); | |
2668 | return 0; | |
2669 | } | |
2670 | /* Use '&me', because NJOIN is, unlike JOIN, always for | |
2671 | ** remote clients, see get_channel() what's that for. --B. */ | |
2672 | chptr = get_channel(&me, parv[1], CREATE); | |
2673 | /* assert(chptr != NULL); */ | |
2674 | ||
2675 | /* Hack for creating empty channels and locking them. | |
2676 | ** This also allows for getting MODEs for such channels (otherwise | |
2677 | ** they'd get ignored). We need that to prevent desynch, especially | |
2678 | ** after we (re)started. | |
2679 | ** This requires that single dot cannot be used as a name of | |
2680 | ** remote clients that can join channels. --B. */ | |
2681 | if (parv[2][0] == '.' && parv[2][1] == '\0') | |
2682 | { | |
2683 | /* If we have clients on a channel, it cannot be | |
2684 | ** locked, can it? --B. */ | |
2685 | if (chptr->users == 0 && chptr->history == 0) | |
2686 | { | |
2687 | chptr->history = timeofday + (*chptr->chname == '!' ? | |
2688 | LDELAYCHASETIMELIMIT : DELAYCHASETIMELIMIT); | |
2689 | istat.is_hchan++; | |
2690 | istat.is_hchanmem += sizeof(aChannel) + | |
2691 | strlen(chptr->chname); | |
2692 | } | |
2693 | /* There cannot be anything else in this NJOIN. */ | |
2694 | return 0; | |
2695 | } | |
2696 | ||
2697 | *uidbuf = '\0'; u = uidbuf; | |
2698 | /* 17 comes from syntax ": NJOIN :,@@+\r\n\0" */ | |
2699 | maxlen = BUFSIZE - 17 - strlen(parv[0]) - strlen(parv[1]) - NICKLEN; | |
2700 | for (target = strtoken(&p, parv[2], ","); target; | |
2701 | target = strtoken(&p, NULL, ",")) | |
2702 | { | |
2703 | /* check for modes */ | |
2704 | chop = 0; | |
2705 | mbuf[0] = '\0'; | |
2706 | if (*target == '@') | |
2707 | { | |
2708 | if (*(target+1) == '@') | |
2709 | { | |
2710 | if (*(target+2) == '+') | |
2711 | { | |
2712 | strcpy(mbuf, "ov"); | |
2713 | chop = CHFL_UNIQOP| \ | |
2714 | CHFL_CHANOP|CHFL_VOICE; | |
2715 | name = target + 3; | |
2716 | } | |
2717 | else | |
2718 | { | |
2719 | strcpy(mbuf, "o"); | |
2720 | chop = CHFL_UNIQOP|CHFL_CHANOP; | |
2721 | name = target + 2; | |
2722 | } | |
2723 | } | |
2724 | else | |
2725 | { | |
2726 | if (*(target+1) == '+') | |
2727 | { | |
2728 | strcpy(mbuf, "ov"); | |
2729 | chop = CHFL_CHANOP|CHFL_VOICE; | |
2730 | name = target+2; | |
2731 | } | |
2732 | else | |
2733 | { | |
2734 | strcpy(mbuf, "o"); | |
2735 | chop = CHFL_CHANOP; | |
2736 | name = target+1; | |
2737 | } | |
2738 | } | |
2739 | } | |
2740 | else if (*target == '+') | |
2741 | { | |
2742 | strcpy(mbuf, "v"); | |
2743 | chop = CHFL_VOICE; | |
2744 | name = target+1; | |
2745 | } | |
2746 | else | |
2747 | { | |
2748 | name = target; | |
2749 | } | |
2750 | /* find user */ | |
2751 | if (!(acptr = find_person(name, NULL)) && | |
2752 | !(acptr = find_uid(name, NULL))) | |
2753 | { | |
2754 | /* shouldn't this be an error? --B. */ | |
2755 | continue; | |
2756 | } | |
2757 | /* is user who we think? */ | |
2758 | if (acptr->from != cptr) | |
2759 | { | |
2760 | /* shouldn't this be a squit-level error? --B. */ | |
2761 | continue; | |
2762 | } | |
2763 | /* make sure user isn't already on channel */ | |
2764 | if (IsMember(acptr, chptr)) | |
2765 | { | |
2766 | if (IsBursting(sptr)) | |
2767 | { | |
2768 | sendto_flag(SCH_ERROR, "NJOIN protocol error" | |
2769 | " from %s (%s already on %s)", | |
2770 | get_client_name(cptr, TRUE), | |
2771 | acptr->name, chptr->chname); | |
2772 | sendto_one(cptr, "ERROR :NJOIN protocol error" | |
2773 | " (%s already on %s)", | |
2774 | acptr->name, chptr->chname); | |
2775 | } | |
2776 | else | |
2777 | { | |
2778 | sendto_flag(SCH_CHAN, "Fake: %s JOIN %s", | |
2779 | acptr->name, chptr->chname); | |
2780 | } | |
2781 | /* ignore such join anyway */ | |
2782 | continue; | |
2783 | } | |
2784 | /* add user to channel */ | |
2785 | add_user_to_channel(chptr, acptr, UseModes(parv[1]) ? chop :0); | |
2786 | ||
2787 | /* build buffer for NJOIN and UID capable servers */ | |
2788 | ||
2789 | /* send it out if too big to fit buffer */ | |
2790 | if (u-uidbuf >= maxlen) | |
2791 | { | |
2792 | *u = '\0'; | |
2793 | sendto_match_servs_v(chptr, cptr, SV_UID, | |
2794 | ":%s NJOIN %s :%s", | |
2795 | sptr->serv->sid, parv[1], uidbuf); | |
2796 | *uidbuf = '\0'; u = uidbuf; | |
2797 | } | |
2798 | ||
2799 | if (u != uidbuf) | |
2800 | { | |
2801 | *u++ = ','; | |
2802 | } | |
2803 | ||
2804 | /* Copy the modes. */ | |
2805 | for (; target < name; target++) | |
2806 | { | |
2807 | *u++ = *target; | |
2808 | } | |
2809 | ||
2810 | target = acptr->user ? acptr->user->uid : acptr->name; | |
2811 | while (*target) | |
2812 | { | |
2813 | *u++ = *target++; | |
2814 | } | |
2815 | ||
2816 | /* send join to local users on channel */ | |
2817 | /* Little syntax trick. Put ":" before channel name if it is | |
2818 | ** not burst, so clients can use it for discriminating normal | |
2819 | ** join from netjoin. 2.10.x is using NJOIN only during | |
2820 | ** burst, but 2.11 always. Hence we check for EOB from 2.11 | |
2821 | ** to know what kind of NJOIN it is. --B. */ | |
2822 | sendto_channel_butserv(chptr, acptr, ":%s JOIN %s%s", acptr->name, ( | |
2823 | #ifdef JAPANESE | |
2824 | /* XXX-JP: explain why jp-patch had that! */ | |
2825 | IsServer(sptr) || | |
2826 | #endif | |
2827 | IsBursting(sptr)) ? "" : ":", chptr->chname); | |
2828 | /* build MODE for local users on channel, eventually send it */ | |
2829 | if (*mbuf) | |
2830 | { | |
2831 | if (!UseModes(parv[1])) | |
2832 | { | |
2833 | sendto_one(cptr, replies[ERR_NOCHANMODES], | |
2834 | ME, BadTo(parv[0]), parv[1]); | |
2835 | continue; | |
2836 | } | |
2837 | switch (cnt) | |
2838 | { | |
2839 | case 0: | |
2840 | *parabuf = '\0'; *modebuf = '\0'; | |
2841 | /* fall through */ | |
2842 | case 1: | |
2843 | strcat(modebuf, mbuf); | |
2844 | cnt += strlen(mbuf); | |
2845 | if (*parabuf) | |
2846 | { | |
2847 | strcat(parabuf, " "); | |
2848 | } | |
2849 | strcat(parabuf, acptr->name); | |
2850 | if (mbuf[1]) | |
2851 | { | |
2852 | strcat(parabuf, " "); | |
2853 | strcat(parabuf, acptr->name); | |
2854 | } | |
2855 | break; | |
2856 | case 2: | |
2857 | sendto_channel_butserv(chptr, &me, | |
2858 | ":%s MODE %s +%s%c %s %s", | |
2859 | sptr->name, chptr->chname, | |
2860 | modebuf, mbuf[0], | |
2861 | parabuf, acptr->name); | |
2862 | if (mbuf[1]) | |
2863 | { | |
2864 | strcpy(modebuf, mbuf+1); | |
2865 | strcpy(parabuf, acptr->name); | |
2866 | cnt = 1; | |
2867 | } | |
2868 | else | |
2869 | cnt = 0; | |
2870 | break; | |
2871 | } | |
2872 | if (cnt == MAXMODEPARAMS) | |
2873 | { | |
2874 | sendto_channel_butserv(chptr, &me, | |
2875 | ":%s MODE %s +%s %s", | |
2876 | sptr->name, chptr->chname, | |
2877 | modebuf, parabuf); | |
2878 | cnt = 0; | |
2879 | } | |
2880 | } | |
2881 | } | |
2882 | /* send eventual MODE leftover */ | |
2883 | if (cnt) | |
2884 | sendto_channel_butserv(chptr, &me, ":%s MODE %s +%s %s", | |
2885 | sptr->name, chptr->chname, modebuf, parabuf); | |
2886 | ||
2887 | /* send NJOIN */ | |
2888 | *u = '\0'; | |
2889 | if (uidbuf[0]) | |
2890 | { | |
2891 | sendto_match_servs_v(chptr, cptr, SV_UID, ":%s NJOIN %s :%s", | |
2892 | sptr->serv->sid, parv[1], uidbuf); | |
2893 | } | |
2894 | ||
2895 | return 0; | |
2896 | } | |
2897 | ||
2898 | /* | |
2899 | ** m_part | |
2900 | ** parv[0] = sender prefix | |
2901 | ** parv[1] = channel | |
2902 | */ | |
2903 | int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
2904 | { | |
2905 | Reg aChannel *chptr; | |
2906 | char *p = NULL, *name, *comment = ""; | |
2907 | int size; | |
2908 | ||
2909 | *buf = '\0'; | |
2910 | ||
2911 | parv[1] = canonize(parv[1]); | |
2912 | comment = (BadPtr(parv[2])) ? "" : parv[2]; | |
2913 | if (strlen(comment) > TOPICLEN) | |
2914 | comment[TOPICLEN] = '\0'; | |
2915 | ||
2916 | /* | |
2917 | ** Broadcasted to other servers is ":nick PART #chan,#chans :comment", | |
2918 | ** so we must make sure buf does not contain too many channels or later | |
2919 | ** they get truncated! "10" comes from all fixed chars: ":", " PART " | |
2920 | ** and ending "\r\n\0". We could subtract strlen(comment)+2 here too, | |
2921 | ** but it's not something we care, is it? :-> | |
2922 | ** Btw: if we ever change m_part to have UID as source, fix this! --B. | |
2923 | */ | |
2924 | size = BUFSIZE - strlen(parv[0]) - 10; | |
2925 | for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) | |
2926 | { | |
2927 | chptr = get_channel(sptr, name, 0); | |
2928 | if (!chptr) | |
2929 | { | |
2930 | if (MyPerson(sptr)) | |
2931 | sendto_one(sptr, | |
2932 | replies[ERR_NOSUCHCHANNEL], ME, BadTo(parv[0]), | |
2933 | name); | |
2934 | continue; | |
2935 | } | |
2936 | if (check_channelmask(sptr, cptr, name)) | |
2937 | continue; | |
2938 | if (!IsMember(sptr, chptr)) | |
2939 | { | |
2940 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], ME, BadTo(parv[0]), | |
2941 | name); | |
2942 | continue; | |
2943 | } | |
2944 | /* | |
2945 | ** Remove user from the old channel (if any) | |
2946 | */ | |
2947 | if (!get_channelmask(name) && (*chptr->chname != '!') | |
2948 | #ifdef JAPANESE | |
2949 | /* jp_valid here because in else{} there's | |
2950 | ** sendto_match_servs() which ignores such | |
2951 | ** channels when sending stuff. --B. */ | |
2952 | && jp_valid(NULL, chptr, NULL) /* XXX-JP: why not | |
2953 | jp_valid(NULL, chptr, name)? | |
2954 | because user cannot be on | |
2955 | jp-named channel which is not | |
2956 | flagged JP? --B. */ | |
2957 | #endif | |
2958 | ) | |
2959 | { /* channel:*.mask */ | |
2960 | if (*name != '&') | |
2961 | { | |
2962 | /* We could've decreased size by 1 when | |
2963 | ** calculating it, but I left it like that | |
2964 | ** for the sake of clarity. --B. */ | |
2965 | if (strlen(buf) + strlen(name) + 1 | |
2966 | > size) | |
2967 | { | |
2968 | /* Anyway, if it would not fit in the | |
2969 | ** buffer, send it right away. --B */ | |
2970 | sendto_serv_butone(cptr, PartFmt, | |
2971 | sptr->user->uid, buf, comment); | |
2972 | *buf = '\0'; | |
2973 | } | |
2974 | if (*buf) | |
2975 | (void)strcat(buf, ","); | |
2976 | (void)strcat(buf, name); | |
2977 | } | |
2978 | } | |
2979 | else | |
2980 | sendto_match_servs(chptr, cptr, PartFmt, | |
2981 | sptr->user->uid, name, comment); | |
2982 | sendto_channel_butserv(chptr, sptr, PartFmt, | |
2983 | parv[0], chptr->chname, comment); | |
2984 | remove_user_from_channel(sptr, chptr); | |
2985 | } | |
2986 | if (*buf) | |
2987 | sendto_serv_butone(cptr, PartFmt, sptr->user->uid, buf, comment); | |
2988 | return 4; | |
2989 | } | |
2990 | ||
2991 | /* | |
2992 | ** m_kick | |
2993 | ** parv[0] = sender prefix | |
2994 | ** parv[1] = channel | |
2995 | ** parv[2] = client to kick | |
2996 | ** parv[3] = kick comment | |
2997 | */ | |
2998 | int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
2999 | { | |
3000 | aClient *who; | |
3001 | aChannel *chptr; | |
3002 | int chasing = 0, penalty = 0; | |
3003 | char *comment, *name, *p = NULL, *user, *p2 = NULL; | |
3004 | char *tmp, *tmp2; | |
3005 | char *sender; | |
3006 | char nbuf[BUFSIZE+1]; | |
3007 | int clen, maxlen; | |
3008 | ||
3009 | if (IsServer(sptr)) | |
3010 | sendto_flag(SCH_NOTICE, "KICK from %s for %s %s", | |
3011 | parv[0], parv[1], parv[2]); | |
3012 | if (BadPtr(parv[3])) | |
3013 | { | |
3014 | comment = "no reason"; | |
3015 | } | |
3016 | else | |
3017 | { | |
3018 | comment = parv[3]; | |
3019 | if (strlen(comment) > (size_t) TOPICLEN) | |
3020 | comment[TOPICLEN] = '\0'; | |
3021 | } | |
3022 | ||
3023 | if (IsServer(sptr)) | |
3024 | { | |
3025 | sender = sptr->serv->sid; | |
3026 | } | |
3027 | else if (sptr->user) | |
3028 | { | |
3029 | sender = sptr->user->uid; | |
3030 | } | |
3031 | else | |
3032 | { | |
3033 | sender = sptr->name; | |
3034 | } | |
3035 | ||
3036 | /* we'll decrease it for each channel later */ | |
3037 | maxlen = BUFSIZE - MAX(strlen(sender), strlen(sptr->name)) | |
3038 | - strlen(comment) - 10; /* ":", " KICK ", " " and " :" */ | |
3039 | ||
3040 | for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) | |
3041 | { | |
3042 | if (penalty >= MAXPENALTY && MyPerson(sptr)) | |
3043 | break; | |
3044 | chptr = get_channel(sptr, name, !CREATE); | |
3045 | if (!chptr) | |
3046 | { | |
3047 | if (MyPerson(sptr)) | |
3048 | sendto_one(sptr, | |
3049 | replies[ERR_NOSUCHCHANNEL], ME, BadTo(parv[0]), | |
3050 | name); | |
3051 | penalty += 2; | |
3052 | continue; | |
3053 | } | |
3054 | if (check_channelmask(sptr, cptr, name)) | |
3055 | continue; | |
3056 | if (!UseModes(name)) | |
3057 | { | |
3058 | sendto_one(sptr, replies[ERR_NOCHANMODES], ME, BadTo(parv[0]), | |
3059 | name); | |
3060 | penalty += 2; | |
3061 | continue; | |
3062 | } | |
3063 | if (!IsServer(sptr) && !is_chan_op(sptr, chptr)) | |
3064 | { | |
3065 | if (!IsMember(sptr, chptr)) | |
3066 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], | |
3067 | ME, BadTo(parv[0]), chptr->chname); | |
3068 | else | |
3069 | sendto_one(sptr, replies[ERR_CHANOPRIVSNEEDED], | |
3070 | ME, BadTo(parv[0]), chptr->chname); | |
3071 | penalty += 2; | |
3072 | continue; | |
3073 | } | |
3074 | ||
3075 | clen = maxlen - strlen(name) - 1; /* for comma, see down */ | |
3076 | nbuf[0] = '\0'; | |
3077 | tmp = mystrdup(parv[2]); | |
3078 | for (tmp2 = tmp; (user = strtoken(&p2, tmp2, ",")); tmp2 = NULL) | |
3079 | { | |
3080 | penalty++; | |
3081 | if (!(IsServer(cptr) && (who = find_uid(user, NULL))) && | |
3082 | !(who = find_chasing(sptr, user, &chasing))) | |
3083 | continue; /* No such user left! */ | |
3084 | if (IsMember(who, chptr)) | |
3085 | { | |
3086 | /* Local clients. */ | |
3087 | sendto_channel_butserv(chptr, sptr, | |
3088 | ":%s KICK %s %s :%s", sptr->name, | |
3089 | chptr->chname, who->name, comment); | |
3090 | ||
3091 | /* Nick buffer to kick out. */ | |
3092 | /* as we need space for ",nick", we should add | |
3093 | ** 1 on the left side; instead we subtracted 1 | |
3094 | ** on the right side, before the loop. */ | |
3095 | if (strlen(nbuf) + (who->user ? UIDLEN : | |
3096 | strlen(who->name)) >= clen) | |
3097 | { | |
3098 | sendto_match_servs_v(chptr, cptr, | |
3099 | SV_UID, ":%s KICK %s %s :%s", | |
3100 | sender, name, nbuf, comment); | |
3101 | nbuf[0] = '\0'; | |
3102 | } | |
3103 | if (*nbuf) | |
3104 | { | |
3105 | strcat(nbuf, ","); | |
3106 | } | |
3107 | strcat(nbuf, who->user ? who->user->uid : | |
3108 | who->name); | |
3109 | ||
3110 | /* kicking last one out may destroy chptr */ | |
3111 | if (chptr->users == 1) | |
3112 | { | |
3113 | if (*nbuf) | |
3114 | { | |
3115 | sendto_match_servs_v(chptr, | |
3116 | cptr, SV_UID, | |
3117 | ":%s KICK %s %s :%s", | |
3118 | sender, name, | |
3119 | nbuf, comment); | |
3120 | nbuf[0] = '\0'; | |
3121 | } | |
3122 | } | |
3123 | remove_user_from_channel(who, chptr); | |
3124 | penalty += 2; | |
3125 | if (MyPerson(sptr) && | |
3126 | /* penalties, obvious */ | |
3127 | (penalty >= MAXPENALTY | |
3128 | /* Stop if user kicks himself out | |
3129 | ** of channel --B. */ | |
3130 | || who == sptr)) | |
3131 | { | |
3132 | break; | |
3133 | } | |
3134 | } | |
3135 | else | |
3136 | sendto_one(sptr, | |
3137 | replies[ERR_USERNOTINCHANNEL], | |
3138 | ME, BadTo(parv[0]), user, name); | |
3139 | } /* loop on parv[2] */ | |
3140 | MyFree(tmp); | |
3141 | /* finish sending KICK for given channel */ | |
3142 | if (*nbuf) | |
3143 | { | |
3144 | sendto_match_servs_v(chptr, cptr, SV_UID, | |
3145 | ":%s KICK %s %s :%s", sender, name, | |
3146 | nbuf, comment); | |
3147 | } | |
3148 | } /* loop on parv[1] */ | |
3149 | ||
3150 | return penalty; | |
3151 | } | |
3152 | ||
3153 | /* | |
3154 | ** m_topic | |
3155 | ** parv[0] = sender prefix | |
3156 | ** parv[1] = channels list | |
3157 | ** parv[2] = topic text | |
3158 | */ | |
3159 | int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
3160 | { | |
3161 | aChannel *chptr = NullChn; | |
3162 | char *topic = NULL, *name, *p = NULL; | |
3163 | int penalty = 1; | |
3164 | ||
3165 | parv[1] = canonize(parv[1]); | |
3166 | ||
3167 | for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) | |
3168 | { | |
3169 | if (!IsChannelName(name)) | |
3170 | { | |
3171 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], ME, | |
3172 | parv[0], name); | |
3173 | continue; | |
3174 | } | |
3175 | if (!UseModes(name)) | |
3176 | { | |
3177 | sendto_one(sptr, replies[ERR_NOCHANMODES], ME, | |
3178 | parv[0], name); | |
3179 | continue; | |
3180 | } | |
3181 | chptr = find_channel(name, NullChn); | |
3182 | if (!chptr) | |
3183 | { | |
3184 | sendto_one(sptr, replies[ERR_NOSUCHCHANNEL], ME, | |
3185 | parv[0], name); | |
3186 | return penalty; | |
3187 | } | |
3188 | if (!IsMember(sptr, chptr)) | |
3189 | { | |
3190 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], ME, | |
3191 | parv[0], name); | |
3192 | continue; | |
3193 | } | |
3194 | ||
3195 | /* should never be true at this point --B. */ | |
3196 | if (check_channelmask(sptr, cptr, name)) | |
3197 | continue; | |
3198 | ||
3199 | if (parc > 2) | |
3200 | topic = parv[2]; | |
3201 | ||
3202 | if (!topic) /* only asking for topic */ | |
3203 | { | |
3204 | if (chptr->topic[0] == '\0') | |
3205 | { | |
3206 | sendto_one(sptr, replies[RPL_NOTOPIC], ME, | |
3207 | parv[0], chptr->chname); | |
3208 | } | |
3209 | else | |
3210 | { | |
3211 | sendto_one(sptr, replies[RPL_TOPIC], ME, | |
3212 | parv[0], chptr->chname, chptr->topic); | |
3213 | #ifdef TOPIC_WHO_TIME | |
3214 | if (chptr->topic_t > 0) | |
3215 | sendto_one(sptr, replies[RPL_TOPIC_WHO_TIME], | |
3216 | ME, BadTo(parv[0]), chptr->chname, | |
3217 | IsAnonymous(chptr) ? | |
3218 | "anonymous!anonymous@anonymous." : | |
3219 | chptr->topic_nuh, chptr->topic_t); | |
3220 | #endif | |
3221 | } | |
3222 | } | |
3223 | else if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || | |
3224 | is_chan_op(sptr, chptr)) | |
3225 | { /* setting a topic */ | |
3226 | strncpyzt(chptr->topic, topic, sizeof(chptr->topic)); | |
3227 | #ifdef TOPIC_WHO_TIME | |
3228 | sprintf(chptr->topic_nuh, "%s!%s@%s", sptr->name, | |
3229 | sptr->user->username, sptr->user->host); | |
3230 | chptr->topic_t = timeofday; | |
3231 | #endif | |
3232 | sendto_match_servs(chptr, cptr,":%s TOPIC %s :%s", | |
3233 | sptr->user->uid, chptr->chname, | |
3234 | chptr->topic); | |
3235 | sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s", | |
3236 | parv[0], | |
3237 | chptr->chname, chptr->topic); | |
3238 | #ifdef USE_SERVICES | |
3239 | check_services_butone(SERVICE_WANT_TOPIC, | |
3240 | NULL, sptr, ":%s TOPIC %s :%s", | |
3241 | parv[0], chptr->chname, | |
3242 | chptr->topic); | |
3243 | #endif | |
3244 | penalty += 2; | |
3245 | } | |
3246 | else | |
3247 | sendto_one(sptr, replies[ERR_CHANOPRIVSNEEDED], ME, BadTo(parv[0]), | |
3248 | chptr->chname); | |
3249 | } | |
3250 | return penalty; | |
3251 | } | |
3252 | ||
3253 | /* | |
3254 | ** m_invite | |
3255 | ** parv[0] - sender prefix | |
3256 | ** parv[1] - user to invite | |
3257 | ** parv[2] - channel number | |
3258 | */ | |
3259 | int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
3260 | { | |
3261 | aClient *acptr; | |
3262 | aChannel *chptr; | |
3263 | ||
3264 | if (!(acptr = find_person(parv[1], (aClient *)NULL))) | |
3265 | { | |
3266 | sendto_one(sptr, replies[ERR_NOSUCHNICK], ME, BadTo(parv[0]), parv[1]); | |
3267 | return 1; | |
3268 | } | |
3269 | if (clean_channelname(parv[2]) == -1) | |
3270 | return 1; | |
3271 | if (check_channelmask(sptr, acptr->user->servp->bcptr, parv[2])) | |
3272 | return 1; | |
3273 | if (*parv[2] == '&' && !MyClient(acptr)) | |
3274 | return 1; | |
3275 | chptr = find_channel(parv[2], NullChn); | |
3276 | ||
3277 | #ifdef JAPANESE | |
3278 | if (!jp_valid(acptr->from, chptr, parv[2])) | |
3279 | { | |
3280 | sendto_one(sptr, replies[ERR_BADCHANMASK], ME, | |
3281 | chptr ? chptr->chname : parv[2]); | |
3282 | return 1; | |
3283 | } | |
3284 | #endif | |
3285 | if (!chptr && parv[2][0] == '!') | |
3286 | { | |
3287 | /* Try to find !channel using shortname */ | |
3288 | chptr = hash_find_channels(parv[2] + 1, NULL); | |
3289 | } | |
3290 | ||
3291 | if (!chptr) | |
3292 | { | |
3293 | sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", | |
3294 | parv[0], parv[1], parv[2]); | |
3295 | if (MyConnect(sptr)) | |
3296 | { | |
3297 | sendto_one(sptr, replies[RPL_INVITING], ME, BadTo(parv[0]), | |
3298 | acptr->name, parv[2]); | |
3299 | if (acptr->user->flags & FLAGS_AWAY) | |
3300 | send_away(sptr, acptr); | |
3301 | } | |
3302 | return 3; | |
3303 | } | |
3304 | ||
3305 | if (!IsMember(sptr, chptr)) | |
3306 | { | |
3307 | sendto_one(sptr, replies[ERR_NOTONCHANNEL], ME, BadTo(parv[0]), parv[2]); | |
3308 | return 1; | |
3309 | } | |
3310 | ||
3311 | if (IsMember(acptr, chptr)) | |
3312 | { | |
3313 | sendto_one(sptr, replies[ERR_USERONCHANNEL], ME, BadTo(parv[0]), | |
3314 | parv[1], parv[2]); | |
3315 | return 1; | |
3316 | } | |
3317 | ||
3318 | if ((chptr->mode.mode & MODE_INVITEONLY) && !is_chan_op(sptr, chptr)) | |
3319 | { | |
3320 | sendto_one(sptr, replies[ERR_CHANOPRIVSNEEDED], | |
3321 | ME, BadTo(parv[0]), chptr->chname); | |
3322 | return 1; | |
3323 | } | |
3324 | ||
3325 | if (MyConnect(sptr)) | |
3326 | { | |
3327 | sendto_one(sptr, replies[RPL_INVITING], ME, BadTo(parv[0]), | |
3328 | acptr->name, ((chptr) ? (chptr->chname) : parv[2])); | |
3329 | if (acptr->user->flags & FLAGS_AWAY) | |
3330 | send_away(sptr, acptr); | |
3331 | } | |
3332 | ||
3333 | if (MyConnect(acptr)) | |
3334 | if (chptr && /* (chptr->mode.mode & MODE_INVITEONLY) && */ | |
3335 | sptr->user && is_chan_op(sptr, chptr)) | |
3336 | add_invite(sptr, acptr, chptr); | |
3337 | ||
3338 | sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s",parv[0], | |
3339 | acptr->name, ((chptr) ? (chptr->chname) : parv[2])); | |
3340 | return 2; | |
3341 | } | |
3342 | ||
3343 | ||
3344 | /* | |
3345 | ** m_list | |
3346 | ** parv[0] = sender prefix | |
3347 | ** parv[1] = channel | |
3348 | */ | |
3349 | int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
3350 | { | |
3351 | aChannel *chptr; | |
3352 | char *name, *p = NULL; | |
3353 | int rlen = 0; | |
3354 | ||
3355 | if (parc > 2 && | |
3356 | hunt_server(cptr, sptr, ":%s LIST %s %s", 2, parc, parv)) | |
3357 | return 10; | |
3358 | ||
3359 | if (BadPtr(parv[1])) | |
3360 | { | |
3361 | Link *lp; | |
3362 | int listedchannels = 0; | |
3363 | int maxsendq = 0; | |
3364 | ||
3365 | if (!sptr->user) | |
3366 | { | |
3367 | sendto_one(sptr, replies[RPL_LISTEND], ME, | |
3368 | BadTo(parv[0])); | |
3369 | return 2; | |
3370 | } | |
3371 | ||
3372 | #ifdef LIST_ALIS_NOTE | |
3373 | if (MyConnect(sptr)) | |
3374 | { | |
3375 | sendto_one(sptr, ":%s NOTICE %s :%s", ME, parv[0], | |
3376 | LIST_ALIS_NOTE); | |
3377 | } | |
3378 | #endif | |
3379 | /* Keep 10% of sendQ free | |
3380 | * Note: Definition of LIST command prevents obtaining | |
3381 | * of complete LIST from remote server, if this | |
3382 | * behaviour is changed, MyConnect() check needs to be added | |
3383 | * here and within following loops as well. - jv | |
3384 | */ | |
3385 | maxsendq = (int) ((float) get_sendq(sptr, 0) * (float) 0.9); | |
3386 | ||
3387 | /* First, show all +s/+p channels user is on */ | |
3388 | for (lp = sptr->user->channel; lp; lp = lp->next) | |
3389 | { | |
3390 | chptr = lp->value.chptr; | |
3391 | if (SecretChannel(chptr) || HiddenChannel(chptr)) | |
3392 | { | |
3393 | sendto_one(sptr, replies[RPL_LIST], ME, | |
3394 | BadTo(parv[0]), chptr->chname, | |
3395 | chptr->users, chptr->topic); | |
3396 | listedchannels++; | |
3397 | ||
3398 | if (DBufLength(&sptr->sendQ) > maxsendq) | |
3399 | { | |
3400 | sendto_one(sptr, | |
3401 | replies[ERR_TOOMANYMATCHES], | |
3402 | ME, BadTo(parv[0]), "LIST"); | |
3403 | goto end_of_list; | |
3404 | } | |
3405 | } | |
3406 | } | |
3407 | ||
3408 | /* Second, show all visible channels; +p channels are not | |
3409 | * reported if user is not their member - jv. | |
3410 | */ | |
3411 | for (chptr = channel; chptr; chptr = chptr->nextch) | |
3412 | { | |
3413 | if (!chptr->users || /* empty locked channel */ | |
3414 | SecretChannel(chptr) || HiddenChannel(chptr)) | |
3415 | { | |
3416 | continue; | |
3417 | } | |
3418 | sendto_one(sptr, replies[RPL_LIST], ME, BadTo(parv[0]), | |
3419 | chptr->chname, chptr->users, | |
3420 | chptr->topic); | |
3421 | ||
3422 | listedchannels++; | |
3423 | ||
3424 | if (DBufLength(&sptr->sendQ) > maxsendq) | |
3425 | { | |
3426 | sendto_one(sptr, replies[ERR_TOOMANYMATCHES], | |
3427 | ME, BadTo(parv[0]), "LIST"); | |
3428 | ||
3429 | break; | |
3430 | } | |
3431 | ||
3432 | } | |
3433 | ||
3434 | end_of_list:; | |
3435 | #ifdef LIST_ALIS_NOTE | |
3436 | /* Send second notice if we listed more than 24 channels | |
3437 | * - usual height of irc client in text mode. | |
3438 | */ | |
3439 | if (MyConnect(sptr) && (listedchannels > 24)) | |
3440 | { | |
3441 | sendto_one(sptr, ":%s NOTICE %s :%s", ME, parv[0], | |
3442 | LIST_ALIS_NOTE); | |
3443 | } | |
3444 | #endif | |
3445 | ||
3446 | } | |
3447 | else | |
3448 | { | |
3449 | parv[1] = canonize(parv[1]); | |
3450 | for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) | |
3451 | { | |
3452 | chptr = find_channel(name, NullChn); | |
3453 | if (chptr && ShowChannel(sptr, chptr) && sptr->user) | |
3454 | { | |
3455 | rlen += sendto_one(sptr, replies[RPL_LIST], | |
3456 | ME, BadTo(parv[0]), chptr->chname, | |
3457 | chptr->users, chptr->topic); | |
3458 | if (!MyConnect(sptr) && rlen > CHREPLLEN) | |
3459 | break; | |
3460 | } | |
3461 | if (*name == '!') | |
3462 | { | |
3463 | chptr = NULL; | |
3464 | while ((chptr = hash_find_channels(name + 1, | |
3465 | chptr))) | |
3466 | { | |
3467 | int scr = SecretChannel(chptr) && | |
3468 | !IsMember(sptr, chptr); | |
3469 | rlen += sendto_one(sptr, | |
3470 | replies[RPL_LIST], | |
3471 | ME, BadTo(parv[0]), | |
3472 | chptr->chname, | |
3473 | (scr) ? -1 : | |
3474 | chptr->users, | |
3475 | (scr) ? "" : | |
3476 | chptr->topic); | |
3477 | if (!MyConnect(sptr) && | |
3478 | rlen > CHREPLLEN) | |
3479 | break; | |
3480 | } | |
3481 | } | |
3482 | } | |
3483 | } | |
3484 | if (!MyConnect(sptr) && rlen > CHREPLLEN) | |
3485 | sendto_one(sptr, replies[ERR_TOOMANYMATCHES], ME, | |
3486 | BadTo(parv[0]), "LIST"); | |
3487 | sendto_one(sptr, replies[RPL_LISTEND], ME, BadTo(parv[0])); | |
3488 | return 2; | |
3489 | } | |
3490 | /* | |
3491 | * names_channel - send NAMES for one specific channel | |
3492 | * sends RPL_ENDOFNAMES when sendeon > 0 | |
3493 | */ | |
3494 | static void names_channel(aClient *cptr, aClient *sptr, char *to, | |
3495 | aChannel *chptr, int sendeon) | |
3496 | { | |
3497 | Reg Link *lp = NULL; | |
3498 | Reg aClient *acptr; | |
3499 | int pxlen, ismember = 0, nlen, maxlen; | |
3500 | char *pbuf = buf; | |
3501 | int showusers = 1; | |
3502 | ||
3503 | if (!chptr->users) /* channel in ND */ | |
3504 | { | |
3505 | showusers = 0; | |
3506 | } | |
3507 | else | |
3508 | { | |
3509 | ismember = (lp = find_channel_link(sptr->user->channel, chptr)) | |
3510 | ? 1 : 0; | |
3511 | } | |
3512 | ||
3513 | if (SecretChannel(chptr)) | |
3514 | { | |
3515 | if (!ismember) | |
3516 | { | |
3517 | showusers = 0; | |
3518 | } | |
3519 | else | |
3520 | { | |
3521 | *pbuf++ = '@'; | |
3522 | } | |
3523 | } | |
3524 | else if (HiddenChannel(chptr)) | |
3525 | { | |
3526 | *pbuf++ = '*'; | |
3527 | } | |
3528 | else | |
3529 | { | |
3530 | *pbuf++ = '='; | |
3531 | } | |
3532 | ||
3533 | if (showusers) | |
3534 | { | |
3535 | *pbuf++ = ' '; | |
3536 | pxlen = strlen(chptr->chname); | |
3537 | memcpy(pbuf, chptr->chname, pxlen); | |
3538 | pbuf += pxlen; | |
3539 | *pbuf++ = ' '; | |
3540 | *pbuf++ = ':'; | |
3541 | *pbuf = '\0'; | |
3542 | pxlen += 4; /* '[=|*|@]' ' + ' ' + ':' */ | |
3543 | ||
3544 | if (IsAnonymous(chptr)) | |
3545 | { | |
3546 | if (ismember) | |
3547 | { | |
3548 | if (lp->flags & CHFL_CHANOP) | |
3549 | { | |
3550 | *pbuf++ = '@'; | |
3551 | } | |
3552 | else if (lp->flags & CHFL_VOICE) | |
3553 | { | |
3554 | *pbuf++ = '+'; | |
3555 | } | |
3556 | strcpy(pbuf,to); | |
3557 | } | |
3558 | sendto_one(sptr, replies[RPL_NAMREPLY], ME, BadTo(to), | |
3559 | buf); | |
3560 | } | |
3561 | else | |
3562 | { | |
3563 | /* server names + : : + spaces + "353" + nick length | |
3564 | * +\r\n */ | |
3565 | maxlen = BUFSIZE | |
3566 | - 1 /* : */ | |
3567 | - strlen(ME) | |
3568 | - 5 /* " 353 " */ | |
3569 | - strlen(to) | |
3570 | - 1 /* " " */ | |
3571 | - pxlen /* "= #chan :" */ | |
3572 | - 2; /* \r\n */ | |
3573 | ||
3574 | for (lp = chptr->members; lp; lp = lp->next) | |
3575 | { | |
3576 | acptr = lp->value.cptr; | |
3577 | ||
3578 | nlen = strlen(acptr->name); | |
3579 | /* This check is needed for server channels | |
3580 | * when someone removes +a mode from them. | |
3581 | * (server is member of such channel). | |
3582 | */ | |
3583 | if (strchr(acptr->name, '.')) | |
3584 | { | |
3585 | continue; | |
3586 | } | |
3587 | ||
3588 | /* Exceeded allowed length. */ | |
3589 | if (((size_t) pbuf - (size_t) buf) + nlen | |
3590 | >= maxlen) | |
3591 | { | |
3592 | *pbuf = '\0'; | |
3593 | sendto_one(sptr, | |
3594 | replies[RPL_NAMREPLY],ME , | |
3595 | BadTo(to) , buf); | |
3596 | pbuf = buf + pxlen; /* save prefix | |
3597 | for another | |
3598 | iteration */ | |
3599 | pbuf[0] = '\0'; | |
3600 | } | |
3601 | ||
3602 | if (!ismember && IsInvisible(acptr)) | |
3603 | { | |
3604 | continue; | |
3605 | } | |
3606 | if (lp->flags & CHFL_CHANOP) | |
3607 | { | |
3608 | *pbuf++ = '@'; | |
3609 | } | |
3610 | else if (lp->flags & CHFL_VOICE) | |
3611 | { | |
3612 | *pbuf++ = '+'; | |
3613 | } | |
3614 | ||
3615 | memcpy(pbuf, acptr->name, nlen); | |
3616 | pbuf += nlen; | |
3617 | *pbuf++ = ' '; | |
3618 | } | |
3619 | ||
3620 | *pbuf = '\0'; | |
3621 | sendto_one(sptr, replies[RPL_NAMREPLY], ME, | |
3622 | BadTo(to), buf); | |
3623 | } | |
3624 | } | |
3625 | if (sendeon) | |
3626 | { | |
3627 | sendto_one(sptr, replies[RPL_ENDOFNAMES],ME ,BadTo(to), | |
3628 | chptr->chname); | |
3629 | } | |
3630 | return; | |
3631 | ||
3632 | } | |
3633 | ||
3634 | ||
3635 | /************************************************************************ | |
3636 | * m_names() - Added by Jto 27 Apr 1989 | |
3637 | * Rewritten by jv 27 Apr 2001 | |
3638 | ************************************************************************/ | |
3639 | ||
3640 | /* | |
3641 | ** m_names | |
3642 | ** parv[0] = sender prefix | |
3643 | ** parv[1] = channel | |
3644 | */ | |
3645 | int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[]) | |
3646 | { | |
3647 | Reg aChannel *chptr; | |
3648 | Reg aClient *acptr; | |
3649 | Reg Link *lp; | |
3650 | int maxlen ,pxlen,nlen,cansend = 0, sent = 1; | |
3651 | char *para = parc > 1 ? parv[1] : NULL,*name, *p = NULL, *pbuf = buf; | |
3652 | ||
3653 | if (parc > 2 && | |
3654 | hunt_server(cptr, sptr, ":%s NAMES %s %s", 2, parc, parv)) | |
3655 | { | |
3656 | return MAXPENALTY; | |
3657 | } | |
3658 | ||
3659 | if (!BadPtr(para)) | |
3660 | { | |
3661 | for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) | |
3662 | { | |
3663 | if (clean_channelname(name) == -1) | |
3664 | continue; | |
3665 | if BadPtr(name) | |
3666 | { | |
3667 | continue; | |
3668 | } | |
3669 | chptr = find_channel(name, NULL); | |
3670 | if (chptr) | |
3671 | { | |
3672 | names_channel(cptr, sptr, parv[0], chptr, 1); | |
3673 | } | |
3674 | else | |
3675 | { | |
3676 | sendto_one(sptr, replies[RPL_ENDOFNAMES],ME , | |
3677 | parv[0], name); | |
3678 | } | |
3679 | sent++; | |
3680 | if (!MyConnect(sptr) || sent > MAXCHANNELSPERUSER) | |
3681 | { | |
3682 | break; | |
3683 | } | |
3684 | } | |
3685 | ||
3686 | sent = sent < 2 ? 2 : (sent * MAXCHANNELSPERUSER) / MAXPENALTY; | |
3687 | return sent < 2 ? 2 : sent; | |
3688 | } | |
3689 | /* Client wants all nicks/channels which is seriously cpu intensive | |
3690 | * Allowed for local clients only. | |
3691 | * First, list all secret channels user is on | |
3692 | */ | |
3693 | for (lp = sptr->user->channel; lp; lp = lp->next) | |
3694 | { | |
3695 | chptr = lp->value.chptr; | |
3696 | if (SecretChannel(chptr)) | |
3697 | { | |
3698 | names_channel(cptr, sptr, parv[0], chptr, 0); | |
3699 | } | |
3700 | } | |
3701 | ||
3702 | /* Second, list all non-secret channels */ | |
3703 | for (chptr = channel; chptr; chptr = chptr->nextch) | |
3704 | { | |
3705 | if (!chptr->users || /* channel in CD */ | |
3706 | SecretChannel(chptr)) | |
3707 | { | |
3708 | continue; | |
3709 | } | |
3710 | names_channel(cptr, sptr, parv[0], chptr, 0); | |
3711 | } | |
3712 | /* Third, list all remaining users | |
3713 | * ie, those which aren't on any channel, or are at Anonymous one | |
3714 | */ | |
3715 | strcpy(pbuf, "* * :"); | |
3716 | pxlen = 5; | |
3717 | pbuf += pxlen; | |
3718 | maxlen = BUFSIZE | |
3719 | - 1 /* : */ | |
3720 | - strlen(ME) | |
3721 | - 5 /* " 353 " */ | |
3722 | - strlen(parv[0]) | |
3723 | - 1 /* " " */ | |
3724 | - pxlen /* "* * :" */ | |
3725 | - 2; /* \r\n */ | |
3726 | ||
3727 | ||
3728 | for (acptr = client; acptr ;acptr = acptr->next) | |
3729 | { | |
3730 | if (!IsPerson(acptr) || IsInvisible(acptr)) | |
3731 | { | |
3732 | continue; | |
3733 | } | |
3734 | ||
3735 | lp = acptr->user->channel; | |
3736 | cansend = 1; | |
3737 | while (lp) | |
3738 | { | |
3739 | chptr = lp->value.chptr; | |
3740 | if (PubChannel(chptr) || SecretChannel(chptr) | |
3741 | || IsMember(sptr, chptr)) | |
3742 | { /* already shown */ | |
3743 | cansend = 0; | |
3744 | break; | |
3745 | } | |
3746 | lp = lp->next; | |
3747 | } | |
3748 | if (!cansend) | |
3749 | { | |
3750 | continue; | |
3751 | } | |
3752 | nlen = strlen(acptr->name); | |
3753 | if (strchr(acptr->name, '.')) | |
3754 | { | |
3755 | continue; | |
3756 | } | |
3757 | ||
3758 | if (((size_t) pbuf - (size_t) buf) + nlen >= maxlen) | |
3759 | { | |
3760 | *pbuf = '\0'; | |
3761 | sendto_one(sptr, replies[RPL_NAMREPLY], ME, parv[0], | |
3762 | buf); | |
3763 | sent = 1; | |
3764 | pbuf = buf + pxlen; | |
3765 | pbuf[0] = '\0'; | |
3766 | } | |
3767 | ||
3768 | memcpy(pbuf, acptr->name, nlen); | |
3769 | pbuf += nlen; | |
3770 | *pbuf++ = ' '; | |
3771 | sent = 0; | |
3772 | } | |
3773 | ||
3774 | *pbuf = '\0'; | |
3775 | sendto_one(sptr, replies[RPL_NAMREPLY], ME, parv[0], buf); | |
3776 | sendto_one(sptr, replies[RPL_ENDOFNAMES], ME, parv[0], "*"); | |
3777 | return MAXPENALTY; | |
3778 | } | |
3779 | ||
3780 | #define CHECKFREQ 300 | |
3781 | /* consider reoping an opless channel */ | |
3782 | static int reop_channel(time_t now, aChannel *chptr, int reopmode) | |
3783 | { | |
3784 | Link *lp, op; | |
3785 | ||
3786 | if (IsSplit() || chptr->reop == 0) | |
3787 | { | |
3788 | /* Should never happen. */ | |
3789 | sendto_flag(SCH_DEBUG, "reop_channel should not happen"); | |
3790 | return 0; | |
3791 | } | |
3792 | ||
3793 | op.value.chptr = NULL; | |
3794 | /* Why do we wait until CD expires? --B. */ | |
3795 | if (now - chptr->history > DELAYCHASETIMELIMIT) | |
3796 | { | |
3797 | int idlelimit1 = 0, idlelimit2 = 0; | |
3798 | ||
3799 | if (reopmode != CHFL_REOPLIST) | |
3800 | { | |
3801 | /* | |
3802 | ** This selects random idle limits in the range | |
3803 | ** from CHECKFREQ to 4*CHECKFREQ | |
3804 | */ | |
3805 | idlelimit1 = CHECKFREQ + myrand() % (2*CHECKFREQ); | |
3806 | idlelimit2 = idlelimit1 + CHECKFREQ + | |
3807 | myrand() % (2*CHECKFREQ); | |
3808 | } | |
3809 | ||
3810 | for (lp = chptr->members; lp; lp = lp->next) | |
3811 | { | |
3812 | /* not restricted */ | |
3813 | if (IsRestricted(lp->value.cptr)) | |
3814 | { | |
3815 | continue; | |
3816 | } | |
3817 | if (lp->flags & CHFL_CHANOP) | |
3818 | { | |
3819 | chptr->reop = 0; | |
3820 | return 0; | |
3821 | } | |
3822 | /* Our client */ | |
3823 | if (!MyConnect(lp->value.cptr)) | |
3824 | { | |
3825 | continue; | |
3826 | } | |
3827 | /* matching +R list */ | |
3828 | if (reopmode == CHFL_REOPLIST && | |
3829 | NULL == match_modeid(CHFL_REOPLIST, | |
3830 | lp->value.cptr, chptr)) | |
3831 | { | |
3832 | continue; | |
3833 | } | |
3834 | /* If +R list or channel reop is heavily overdue, | |
3835 | ** don't care about idle. Find the least idle client. | |
3836 | */ | |
3837 | if (reopmode == CHFL_REOPLIST || | |
3838 | now - chptr->reop > 7*LDELAYCHASETIMELIMIT) | |
3839 | { | |
3840 | if (op.value.cptr == NULL || | |
3841 | lp->value.cptr->user->last > | |
3842 | op.value.cptr->user->last) | |
3843 | { | |
3844 | op.value.cptr = lp->value.cptr; | |
3845 | continue; | |
3846 | } | |
3847 | continue; | |
3848 | } | |
3849 | /* else hidden in above continue, | |
3850 | ** not to indent too much :> --B. */ | |
3851 | ||
3852 | /* Channel reop is not heavily overdue. So pick | |
3853 | ** a client, which is a bit idle, but not too much. | |
3854 | ** Find the least idle client possible, though. | |
3855 | */ | |
3856 | if (now - lp->value.cptr->user->last >= idlelimit1 && | |
3857 | now - lp->value.cptr->user->last < idlelimit2 && | |
3858 | (op.value.cptr == NULL || | |
3859 | lp->value.cptr->user->last > | |
3860 | op.value.cptr->user->last)) | |
3861 | { | |
3862 | op.value.cptr = lp->value.cptr; | |
3863 | } | |
3864 | } | |
3865 | if (op.value.cptr == NULL) | |
3866 | { | |
3867 | return 0; | |
3868 | } | |
3869 | sendto_channel_butone(&me, &me, chptr, | |
3870 | ":%s NOTICE %s :Enforcing channel mode +%c (%d)", ME, | |
3871 | chptr->chname, reopmode == CHFL_REOPLIST ? 'R' : 'r', | |
3872 | now - chptr->reop); | |
3873 | op.flags = MODE_ADD|MODE_CHANOP; | |
3874 | change_chan_flag(&op, chptr); | |
3875 | sendto_match_servs(chptr, NULL, ":%s MODE %s +o %s", | |
3876 | ME, chptr->chname, op.value.cptr->name); | |
3877 | sendto_channel_butserv(chptr, &me, ":%s MODE %s +o %s", | |
3878 | ME, chptr->chname, op.value.cptr->name); | |
3879 | chptr->reop = 0; | |
3880 | ircstp->is_reop++; | |
3881 | return 1; | |
3882 | } | |
3883 | return 0; | |
3884 | } | |
3885 | ||
3886 | /* | |
3887 | * Cleanup locked channels, run frequently. | |
3888 | * | |
3889 | * A channel life is defined by its users and the history stamp. | |
3890 | * It is alive if one of the following is true: | |
3891 | * chptr->users > 0 (normal state) | |
3892 | * chptr->history >= time(NULL) (eventually locked) | |
3893 | * It is locked if empty but alive. | |
3894 | * | |
3895 | * The history stamp is set when a remote user with channel op exits. | |
3896 | */ | |
3897 | time_t collect_channel_garbage(time_t now) | |
3898 | { | |
3899 | static u_int max_nb = 0; /* maximum of live channels */ | |
3900 | static u_char split = 0; | |
3901 | Reg aChannel *chptr = channel; | |
3902 | Reg u_int cur_nb = 1, curh_nb = 0, r_cnt = 0; | |
3903 | aChannel *del_ch; | |
3904 | #ifdef DEBUGMODE | |
3905 | u_int del = istat.is_hchan; | |
3906 | #endif | |
3907 | #define SPLITBONUS (CHECKFREQ - 50) | |
3908 | ||
3909 | collect_chid(); | |
3910 | ||
3911 | for (; chptr; chptr = chptr->nextch) | |
3912 | { | |
3913 | if (chptr->users == 0) | |
3914 | { | |
3915 | curh_nb++; | |
3916 | } | |
3917 | else | |
3918 | { | |
3919 | Link *tmp; | |
3920 | ||
3921 | cur_nb++; | |
3922 | ||
3923 | if (IsSplit()) | |
3924 | { | |
3925 | if (chptr->reop > 0) | |
3926 | { | |
3927 | /* Extend reop */ | |
3928 | chptr->reop += CHECKFREQ; | |
3929 | } | |
3930 | continue; | |
3931 | } | |
3932 | if (chptr->reop == 0 || chptr->reop > now) | |
3933 | { | |
3934 | continue; | |
3935 | } | |
3936 | for (tmp = chptr->mlist; tmp; tmp = tmp->next) | |
3937 | { | |
3938 | if (tmp->flags == CHFL_REOPLIST) | |
3939 | { | |
3940 | break; | |
3941 | } | |
3942 | } | |
3943 | if (tmp && tmp->flags == CHFL_REOPLIST) | |
3944 | { | |
3945 | r_cnt += reop_channel(now, chptr, CHFL_REOPLIST); | |
3946 | } | |
3947 | else if (chptr->mode.mode & MODE_REOP) | |
3948 | { | |
3949 | r_cnt += reop_channel(now, chptr, !CHFL_REOPLIST); | |
3950 | } | |
3951 | } | |
3952 | } | |
3953 | if (cur_nb > max_nb) | |
3954 | { | |
3955 | max_nb = cur_nb; | |
3956 | } | |
3957 | ||
3958 | if (r_cnt > 0) | |
3959 | { | |
3960 | sendto_flag(SCH_CHAN, "Re-opped %u channel(s).", r_cnt); | |
3961 | } | |
3962 | ||
3963 | /* | |
3964 | ** check for huge netsplits, if so, garbage collection is not really | |
3965 | ** done but make sure there aren't too many channels kept for | |
3966 | ** history - krys | |
3967 | ** This is dubious, but I'll leave it for now, until better split | |
3968 | ** detection. --B. | |
3969 | */ | |
3970 | if ((2*curh_nb > cur_nb) && curh_nb < max_nb) | |
3971 | split = 1; | |
3972 | else | |
3973 | { | |
3974 | split = 0; | |
3975 | /* no empty channel? let's skip the while! */ | |
3976 | if (curh_nb == 0) | |
3977 | { | |
3978 | #ifdef DEBUGMODE | |
3979 | sendto_flag(SCH_NOTICE, | |
3980 | "Channel garbage: live %u (max %u), hist %u (extended)", | |
3981 | cur_nb - 1, max_nb - 1, curh_nb); | |
3982 | #endif | |
3983 | /* Check again after CHECKFREQ seconds */ | |
3984 | return (time_t) (now + CHECKFREQ); | |
3985 | } | |
3986 | } | |
3987 | ||
3988 | chptr = channel; | |
3989 | while (chptr) | |
3990 | { | |
3991 | /* | |
3992 | ** In case we are likely to be split, extend channel locking. | |
3993 | ** most splits should be short, but reality seems to prove some | |
3994 | ** aren't. | |
3995 | */ | |
3996 | if (!chptr->history) | |
3997 | { | |
3998 | chptr = chptr->nextch; | |
3999 | continue; | |
4000 | } | |
4001 | if (split) /* net splitted recently and we have a lock */ | |
4002 | chptr->history += SPLITBONUS; /* extend lock */ | |
4003 | ||
4004 | if ((chptr->users == 0) && (chptr->history <= now)) | |
4005 | { | |
4006 | del_ch = chptr; | |
4007 | ||
4008 | chptr = del_ch->nextch; | |
4009 | free_channel(del_ch); | |
4010 | } | |
4011 | else | |
4012 | chptr = chptr->nextch; | |
4013 | } | |
4014 | ||
4015 | #ifdef DEBUGMODE | |
4016 | sendto_flag(SCH_NOTICE, | |
4017 | "Channel garbage: live %u (max %u), hist %u (removed %u)%s", | |
4018 | cur_nb - 1, max_nb - 1, curh_nb, del - istat.is_hchan, | |
4019 | (split) ? " split detected" : ""); | |
4020 | #endif | |
4021 | /* Check again after CHECKFREQ seconds */ | |
4022 | return (time_t) (now + CHECKFREQ); | |
4023 | } | |
4024 |