ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/channel_mode.c
Revision: 572
Committed: Sun Apr 30 16:57:48 2006 UTC (19 years, 4 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid-7.2/src/channel_mode.c
File size: 47894 byte(s)
Log Message:
- Backported changes made in HEAD to get rid of Channel::locmembers.
  This is mainly to save about 5megs of ram on networks like efnet where
  we have about 600k allocated Membership structures.

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * channel_mode.c: Controls modes on channels.
4 *
5 * Copyright (C) 2005 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "tools.h"
27 #include "channel.h"
28 #include "channel_mode.h"
29 #include "client.h"
30 #include "common.h"
31 #include "hash.h"
32 #include "hostmask.h"
33 #include "irc_string.h"
34 #include "sprintf_irc.h"
35 #include "ircd.h"
36 #include "list.h"
37 #include "numeric.h"
38 #include "s_serv.h" /* captab */
39 #include "s_user.h"
40 #include "send.h"
41 #include "whowas.h"
42 #include "s_conf.h" /* ConfigFileEntry, ConfigChannel */
43 #include "event.h"
44 #include "memory.h"
45 #include "balloc.h"
46 #include "s_log.h"
47 #include "msg.h"
48
49 /* some small utility functions */
50 static char *check_string(char *);
51 static char *fix_key(char *);
52 static char *fix_key_old(char *);
53 static void chm_nosuch(struct Client *, struct Client *,
54 struct Channel *, int, int *, char **, int *, int,
55 int, char, void *, const char *);
56 static void chm_simple(struct Client *, struct Client *, struct Channel *,
57 int, int *, char **, int *, int, int, char, void *,
58 const char *);
59 static void chm_limit(struct Client *, struct Client *, struct Channel *,
60 int, int *, char **, int *, int, int, char, void *,
61 const char *);
62 static void chm_key(struct Client *, struct Client *, struct Channel *,
63 int, int *, char **, int *, int, int, char, void *,
64 const char *);
65 static void chm_op(struct Client *, struct Client *, struct Channel *, int,
66 int *, char **, int *, int, int, char, void *,
67 const char *);
68 #ifdef HALFOPS
69 static void chm_hop(struct Client *, struct Client *, struct Channel *, int,
70 int *, char **, int *, int, int, char, void *,
71 const char *);
72 #endif
73 static void chm_voice(struct Client *, struct Client *, struct Channel *,
74 int, int *, char **, int *, int, int, char, void *,
75 const char *);
76 static void chm_ban(struct Client *, struct Client *, struct Channel *, int,
77 int *, char **, int *, int, int, char, void *,
78 const char *);
79 static void chm_except(struct Client *, struct Client *, struct Channel *,
80 int, int *, char **, int *, int, int, char, void *,
81 const char *);
82 static void chm_invex(struct Client *, struct Client *, struct Channel *,
83 int, int *, char **, int *, int, int, char, void *,
84 const char *);
85 static void send_cap_mode_changes(struct Client *, struct Client *,
86 struct Channel *, int, int);
87 static void send_mode_changes(struct Client *, struct Client *,
88 struct Channel *, char *);
89
90 /* 10 is a magic number in hybrid 6 NFI where it comes from -db */
91 #define BAN_FUDGE 10
92 #define NCHCAPS (sizeof(channel_capabs)/sizeof(int))
93 #define NCHCAP_COMBOS (1 << NCHCAPS)
94
95 static char nuh_mask[MAXPARA][IRCD_BUFSIZE];
96 /* some buffers for rebuilding channel/nick lists with ,'s */
97 static char modebuf[IRCD_BUFSIZE];
98 static char parabuf[MODEBUFLEN];
99 static struct ChModeChange mode_changes[IRCD_BUFSIZE];
100 static int mode_count;
101 static int mode_limit; /* number of modes set other than simple */
102 static int simple_modes_mask; /* bit mask of simple modes already set */
103 #ifdef HALFOPS
104 static int channel_capabs[] = { CAP_EX, CAP_IE, CAP_TS6, CAP_HOPS };
105 #else
106 static int channel_capabs[] = { CAP_EX, CAP_IE, CAP_TS6 };
107 #endif
108 static struct ChCapCombo chcap_combos[NCHCAP_COMBOS];
109 extern BlockHeap *ban_heap;
110
111
112 /* XXX check_string is propably not longer required in add_id and del_id */
113 /* check_string()
114 *
115 * inputs - string to check
116 * output - pointer to modified string
117 * side effects - Fixes a string so that the first white space found
118 * becomes an end of string marker (`\0`).
119 * returns the 'fixed' string or "*" if the string
120 * was NULL length or a NULL pointer.
121 */
122 static char *
123 check_string(char *s)
124 {
125 char *str = s;
126 static char star[] = "*";
127
128 if (EmptyString(s))
129 return (star);
130
131 for (; *s; ++s)
132 {
133 if (IsSpace(*s))
134 {
135 *s = '\0';
136 break;
137 }
138 }
139
140 return (str);
141 }
142
143 /*
144 * Ban functions to work with mode +b/e/d/I
145 */
146 /* add the specified ID to the channel..
147 * -is 8/9/00
148 */
149
150 int
151 add_id(struct Client *client_p, struct Channel *chptr, char *banid, int type)
152 {
153 dlink_list *list;
154 dlink_node *ban;
155 size_t len = 0;
156 struct Ban *actualBan;
157 unsigned int num_mask;
158 char *name = NULL, *user = NULL, *host = NULL;
159
160 /* dont let local clients overflow the b/e/I lists */
161 if (MyClient(client_p))
162 {
163 num_mask = dlink_list_length(&chptr->banlist) +
164 dlink_list_length(&chptr->exceptlist) +
165 dlink_list_length(&chptr->invexlist);
166
167 if (num_mask >= ConfigChannel.max_bans)
168 {
169 sendto_one(client_p, form_str(ERR_BANLISTFULL),
170 me.name, client_p->name, chptr->chname, banid);
171 return 0;
172 }
173
174 collapse(banid);
175 }
176
177 split_nuh(check_string(banid), &name, &user, &host);
178
179 /*
180 * Assemble a n!u@h and print it back to banid for sending
181 * the mode to the channel.
182 */
183 len = ircsprintf(banid, "%s!%s@%s", name, user, host);
184
185 switch (type)
186 {
187 case CHFL_BAN:
188 list = &chptr->banlist;
189 clear_ban_cache(chptr);
190 break;
191 case CHFL_EXCEPTION:
192 list = &chptr->exceptlist;
193 clear_ban_cache(chptr);
194 break;
195 case CHFL_INVEX:
196 list = &chptr->invexlist;
197 break;
198 default:
199 assert(0);
200 return 0;
201 }
202
203 DLINK_FOREACH(ban, list->head)
204 {
205 actualBan = ban->data;
206 if (!irccmp(actualBan->name, name) &&
207 !irccmp(actualBan->username, user) &&
208 !irccmp(actualBan->host, host))
209 {
210 MyFree(name);
211 MyFree(user);
212 MyFree(host);
213 return 0;
214 }
215 }
216
217 actualBan = BlockHeapAlloc(ban_heap);
218 actualBan->when = CurrentTime;
219 actualBan->name = name;
220 actualBan->username = user;
221 actualBan->host = host;
222 actualBan->len = len-2; /* -2 for @ and ! */
223 actualBan->type = parse_netmask(host, &actualBan->addr, &actualBan->bits);
224
225 if (IsClient(client_p))
226 {
227 actualBan->who =
228 MyMalloc(strlen(client_p->name) +
229 strlen(client_p->username) +
230 strlen(client_p->host) + 3);
231 ircsprintf(actualBan->who, "%s!%s@%s",
232 client_p->name, client_p->username, client_p->host);
233 }
234 else
235 DupString(actualBan->who, client_p->name);
236
237 dlinkAdd(actualBan, &actualBan->node, list);
238
239 return 1;
240 }
241
242 /*
243 * inputs - pointer to channel
244 * - pointer to ban id
245 * - type of ban, i.e. ban, exception, invex
246 * output - 0 for failure, 1 for success
247 * side effects -
248 */
249 static int
250 del_id(struct Channel *chptr, char *banid, int type)
251 {
252 dlink_list *list;
253 dlink_node *ban;
254 struct Ban *banptr;
255 char *name = NULL, *user = NULL, *host = NULL;
256
257 if (banid == NULL)
258 return 0;
259
260 split_nuh(check_string(banid), &name, &user, &host);
261
262 /*
263 * Assemble a n!u@h and print it back to banid for sending
264 * the mode to the channel.
265 */
266 ircsprintf(banid, "%s!%s@%s", name, user, host);
267
268 switch (type)
269 {
270 case CHFL_BAN:
271 list = &chptr->banlist;
272 clear_ban_cache(chptr);
273 break;
274 case CHFL_EXCEPTION:
275 list = &chptr->exceptlist;
276 clear_ban_cache(chptr);
277 break;
278 case CHFL_INVEX:
279 list = &chptr->invexlist;
280 break;
281 default:
282 sendto_realops_flags(UMODE_ALL, L_ALL,
283 "del_id() called with unknown ban type %d!", type);
284 MyFree(name);
285 MyFree(user);
286 MyFree(host);
287 return(0);
288 }
289
290 DLINK_FOREACH(ban, list->head)
291 {
292 banptr = ban->data;
293
294 if (!irccmp(name, banptr->name) &&
295 !irccmp(user, banptr->username) &&
296 !irccmp(host, banptr->host))
297 {
298 remove_ban(banptr, list);
299 MyFree(name);
300 MyFree(user);
301 MyFree(host);
302 return 1;
303 }
304 }
305
306 MyFree(name);
307 MyFree(user);
308 MyFree(host);
309 return 0;
310 }
311
312 static const struct mode_letter
313 {
314 const unsigned int mode;
315 const unsigned char letter;
316 } flags[] = {
317 { MODE_INVITEONLY, 'i' },
318 { MODE_MODERATED, 'm' },
319 { MODE_NOPRIVMSGS, 'n' },
320 { MODE_PRIVATE, 'p' },
321 { MODE_SECRET, 's' },
322 { MODE_TOPICLIMIT, 't' },
323 { 0, '\0' }
324 };
325
326 /* channel_modes()
327 *
328 * inputs - pointer to channel
329 * - pointer to client
330 * - pointer to mode buf
331 * - pointer to parameter buf
332 * output - NONE
333 * side effects - write the "simple" list of channel modes for channel
334 * chptr onto buffer mbuf with the parameters in pbuf.
335 */
336 void
337 channel_modes(struct Channel *chptr, struct Client *client_p,
338 char *mbuf, char *pbuf)
339 {
340 int i;
341
342 *mbuf++ = '+';
343 *pbuf = '\0';
344
345 for (i = 0; flags[i].mode; ++i)
346 if (chptr->mode.mode & flags[i].mode)
347 *mbuf++ = flags[i].letter;
348
349 if (chptr->mode.limit)
350 {
351 *mbuf++ = 'l';
352
353 if (IsMember(client_p, chptr) || IsServer(client_p))
354 pbuf += ircsprintf(pbuf, "%d ", chptr->mode.limit);
355 }
356
357 if (chptr->mode.key[0])
358 {
359 *mbuf++ = 'k';
360
361 if (*pbuf || IsMember(client_p, chptr) || IsServer(client_p))
362 ircsprintf(pbuf, "%s ", chptr->mode.key);
363 }
364
365 *mbuf = '\0';
366 }
367
368 /* fix_key()
369 *
370 * inputs - pointer to key to clean up
371 * output - pointer to cleaned up key
372 * side effects - input string is modified
373 *
374 * stolen from Undernet's ircd -orabidoo
375 */
376 static char *
377 fix_key(char *arg)
378 {
379 unsigned char *s, *t, c;
380
381 for (s = t = (unsigned char *)arg; (c = *s); s++)
382 {
383 c &= 0x7f;
384 if (c != ':' && c > ' ' && c != ',')
385 *t++ = c;
386 }
387
388 *t = '\0';
389 return(arg);
390 }
391
392 /* fix_key_old()
393 *
394 * inputs - pointer to key to clean up
395 * output - pointer to cleaned up key
396 * side effects - input string is modifed
397 *
398 * Here we attempt to be compatible with older non-hybrid servers.
399 * We can't back down from the ':' issue however. --Rodder
400 */
401 static char *
402 fix_key_old(char *arg)
403 {
404 unsigned char *s, *t, c;
405
406 for (s = t = (unsigned char *)arg; (c = *s); s++)
407 {
408 c &= 0x7f;
409 if ((c != 0x0a) && (c != ':') && (c != 0x0d) && (c != ','))
410 *t++ = c;
411 }
412
413 *t = '\0';
414 return(arg);
415 }
416
417 /* bitmasks for various error returns that set_channel_mode should only return
418 * once per call -orabidoo
419 */
420
421 #define SM_ERR_NOTS 0x00000001 /* No TS on channel */
422 #define SM_ERR_NOOPS 0x00000002 /* No chan ops */
423 #define SM_ERR_UNKNOWN 0x00000004
424 #define SM_ERR_RPL_B 0x00000008
425 #define SM_ERR_RPL_E 0x00000010
426 #define SM_ERR_NOTONCHANNEL 0x00000020 /* Not on channel */
427 #define SM_ERR_RPL_I 0x00000040
428
429 /* Now lets do some stuff to keep track of what combinations of
430 * servers exist...
431 * Note that the number of combinations doubles each time you add
432 * something to this list. Each one is only quick if no servers use that
433 * combination, but if the numbers get too high here MODE will get too
434 * slow. I suggest if you get more than 7 here, you consider getting rid
435 * of some and merging or something. If it wasn't for irc+cs we would
436 * probably not even need to bother about most of these, but unfortunately
437 * we do. -A1kmm
438 */
439
440 /* void init_chcap_usage_counts(void)
441 *
442 * Inputs - none
443 * Output - none
444 * Side-effects - Initialises the usage counts to zero. Fills in the
445 * chcap_yes and chcap_no combination tables.
446 */
447 void
448 init_chcap_usage_counts(void)
449 {
450 unsigned long m, c, y, n;
451
452 memset(chcap_combos, 0, sizeof(chcap_combos));
453
454 /* For every possible combination */
455 for (m = 0; m < NCHCAP_COMBOS; m++)
456 {
457 /* Check each capab */
458 for (c = y = n = 0; c < NCHCAPS; c++)
459 {
460 if ((m & (1 << c)) == 0)
461 n |= channel_capabs[c];
462 else
463 y |= channel_capabs[c];
464 }
465 chcap_combos[m].cap_yes = y;
466 chcap_combos[m].cap_no = n;
467 }
468 }
469
470 /* void set_chcap_usage_counts(struct Client *serv_p)
471 * Input: serv_p; The client whose capabs to register.
472 * Output: none
473 * Side-effects: Increments the usage counts for the correct capab
474 * combination.
475 */
476 void
477 set_chcap_usage_counts(struct Client *serv_p)
478 {
479 int n;
480
481 for (n = 0; n < NCHCAP_COMBOS; n++)
482 {
483 if (((serv_p->localClient->caps & chcap_combos[n].cap_yes) ==
484 chcap_combos[n].cap_yes) &&
485 ((serv_p->localClient->caps & chcap_combos[n].cap_no) == 0))
486 {
487 chcap_combos[n].count++;
488 return;
489 }
490 }
491
492 /* This should be impossible -A1kmm. */
493 assert(0);
494 }
495
496 /* void set_chcap_usage_counts(struct Client *serv_p)
497 *
498 * Inputs - serv_p; The client whose capabs to register.
499 * Output - none
500 * Side-effects - Decrements the usage counts for the correct capab
501 * combination.
502 */
503 void
504 unset_chcap_usage_counts(struct Client *serv_p)
505 {
506 int n;
507
508 for (n = 0; n < NCHCAP_COMBOS; n++)
509 {
510 if ((serv_p->localClient->caps & chcap_combos[n].cap_yes) ==
511 chcap_combos[n].cap_yes &&
512 (serv_p->localClient->caps & chcap_combos[n].cap_no) == 0)
513 {
514 /* Hopefully capabs can't change dynamically or anything... */
515 assert(chcap_combos[n].count > 0);
516 chcap_combos[n].count--;
517 return;
518 }
519 }
520
521 /* This should be impossible -A1kmm. */
522 assert(0);
523 }
524
525 /* Mode functions handle mode changes for a particular mode... */
526 static void
527 chm_nosuch(struct Client *client_p, struct Client *source_p,
528 struct Channel *chptr, int parc, int *parn,
529 char **parv, int *errors, int alev, int dir, char c, void *d,
530 const char *chname)
531 {
532 if (*errors & SM_ERR_UNKNOWN)
533 return;
534
535 *errors |= SM_ERR_UNKNOWN;
536 sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name,
537 source_p->name, c);
538 }
539
540 static void
541 chm_simple(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
542 int parc, int *parn, char **parv, int *errors, int alev, int dir,
543 char c, void *d, const char *chname)
544 {
545 long mode_type;
546
547 mode_type = (long)d;
548
549 if ((alev < CHACCESS_HALFOP) ||
550 ((mode_type == MODE_PRIVATE) && (alev < CHACCESS_CHANOP)))
551 {
552 if (!(*errors & SM_ERR_NOOPS))
553 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
554 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
555 me.name, source_p->name, chname);
556 *errors |= SM_ERR_NOOPS;
557 return;
558 }
559
560 /* If have already dealt with this simple mode, ignore it */
561 if (simple_modes_mask & mode_type)
562 return;
563
564 simple_modes_mask |= mode_type;
565
566 /* setting + */
567 /* Apparently, (though no one has ever told the hybrid group directly)
568 * admins don't like redundant mode checking. ok. It would have been nice
569 * if you had have told us directly. I've left the original code snippets
570 * in place.
571 *
572 * -Dianora
573 */
574 if ((dir == MODE_ADD)) /* && !(chptr->mode.mode & mode_type)) */
575 {
576 chptr->mode.mode |= mode_type;
577
578 mode_changes[mode_count].letter = c;
579 mode_changes[mode_count].dir = MODE_ADD;
580 mode_changes[mode_count].caps = 0;
581 mode_changes[mode_count].nocaps = 0;
582 mode_changes[mode_count].id = NULL;
583 mode_changes[mode_count].mems = ALL_MEMBERS;
584 mode_changes[mode_count++].arg = NULL;
585 }
586 else if ((dir == MODE_DEL)) /* && (chptr->mode.mode & mode_type)) */
587 {
588 /* setting - */
589
590 chptr->mode.mode &= ~mode_type;
591
592 mode_changes[mode_count].letter = c;
593 mode_changes[mode_count].dir = MODE_DEL;
594 mode_changes[mode_count].caps = 0;
595 mode_changes[mode_count].nocaps = 0;
596 mode_changes[mode_count].mems = ALL_MEMBERS;
597 mode_changes[mode_count].id = NULL;
598 mode_changes[mode_count++].arg = NULL;
599 }
600 }
601
602 static void
603 chm_ban(struct Client *client_p, struct Client *source_p,
604 struct Channel *chptr, int parc, int *parn,
605 char **parv, int *errors, int alev, int dir, char c, void *d,
606 const char *chname)
607 {
608 char *mask = NULL;
609
610 if (dir == MODE_QUERY || parc <= *parn)
611 {
612 dlink_node *ptr = NULL;
613
614 if (*errors & SM_ERR_RPL_B)
615 return;
616
617 *errors |= SM_ERR_RPL_B;
618
619 DLINK_FOREACH(ptr, chptr->banlist.head)
620 {
621 const struct Ban *banptr = ptr->data;
622 sendto_one(client_p, form_str(RPL_BANLIST),
623 me.name, client_p->name, chname,
624 banptr->name, banptr->username, banptr->host,
625 banptr->who, banptr->when);
626 }
627
628 sendto_one(source_p, form_str(RPL_ENDOFBANLIST), me.name,
629 source_p->name, chname);
630 return;
631 }
632
633 if (alev < CHACCESS_HALFOP)
634 {
635 if (!(*errors & SM_ERR_NOOPS))
636 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
637 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
638 me.name, source_p->name, chname);
639 *errors |= SM_ERR_NOOPS;
640 return;
641 }
642
643 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
644 return;
645
646 mask = nuh_mask[*parn];
647 memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
648 ++*parn;
649
650 if (IsServer(client_p))
651 if (strchr(mask, ' '))
652 return;
653
654 switch (dir)
655 {
656 case MODE_ADD:
657 if (!add_id(source_p, chptr, mask, CHFL_BAN))
658 return;
659 break;
660 case MODE_DEL:
661 /* XXX grrrrrrr */
662 #ifdef NO_BAN_COOKIE
663 if (!del_id(chptr, mask, CHFL_BAN))
664 return;
665 #else
666 /* XXX this hack allows /mode * +o-b nick ban.cookie
667 * I'd like to see this hack go away in the future.
668 */
669 del_id(chptr, mask, CHFL_BAN);
670 #endif
671 break;
672 default:
673 assert(0);
674 }
675
676 mode_changes[mode_count].letter = c;
677 mode_changes[mode_count].dir = dir;
678 mode_changes[mode_count].caps = 0;
679 mode_changes[mode_count].nocaps = 0;
680 mode_changes[mode_count].mems = ALL_MEMBERS;
681 mode_changes[mode_count].id = NULL;
682 mode_changes[mode_count++].arg = mask;
683 }
684
685 static void
686 chm_except(struct Client *client_p, struct Client *source_p,
687 struct Channel *chptr, int parc, int *parn,
688 char **parv, int *errors, int alev, int dir, char c, void *d,
689 const char *chname)
690 {
691 char *mask = NULL;
692
693 /* if we have +e disabled, allow local clients to do anything but
694 * set the mode. This prevents the abuse of +e when just a few
695 * servers support it. --fl
696 */
697 if (!ConfigChannel.use_except && MyClient(source_p) &&
698 ((dir == MODE_ADD) && (parc > *parn)))
699 {
700 if (*errors & SM_ERR_RPL_E)
701 return;
702
703 *errors |= SM_ERR_RPL_E;
704 return;
705 }
706
707 if (alev < CHACCESS_HALFOP)
708 {
709 if (!(*errors & SM_ERR_NOOPS))
710 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
711 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
712 me.name, source_p->name, chname);
713 *errors |= SM_ERR_NOOPS;
714 return;
715 }
716
717 if (dir == MODE_QUERY || parc <= *parn)
718 {
719 dlink_node *ptr = NULL;
720
721 if (*errors & SM_ERR_RPL_E)
722 return;
723
724 *errors |= SM_ERR_RPL_E;
725
726 DLINK_FOREACH(ptr, chptr->exceptlist.head)
727 {
728 const struct Ban *banptr = ptr->data;
729 sendto_one(client_p, form_str(RPL_EXCEPTLIST),
730 me.name, client_p->name, chname,
731 banptr->name, banptr->username, banptr->host,
732 banptr->who, banptr->when);
733 }
734
735 sendto_one(source_p, form_str(RPL_ENDOFEXCEPTLIST), me.name,
736 source_p->name, chname);
737 return;
738 }
739
740 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
741 return;
742
743 mask = nuh_mask[*parn];
744 memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
745 ++*parn;
746
747 if (IsServer(client_p))
748 if (strchr(mask, ' '))
749 return;
750
751 switch (dir)
752 {
753 case MODE_ADD:
754 if (!add_id(source_p, chptr, mask, CHFL_EXCEPTION))
755 return;
756 break;
757 case MODE_DEL:
758 if (!del_id(chptr, mask, CHFL_EXCEPTION))
759 return;
760 break;
761 default:
762 assert(0);
763 }
764
765 mode_changes[mode_count].letter = c;
766 mode_changes[mode_count].dir = dir;
767 mode_changes[mode_count].caps = CAP_EX;
768 mode_changes[mode_count].nocaps = 0;
769
770 if (ConfigChannel.use_except)
771 mode_changes[mode_count].mems = ONLY_CHANOPS;
772 else
773 mode_changes[mode_count].mems = ONLY_SERVERS;
774
775 mode_changes[mode_count].id = NULL;
776 mode_changes[mode_count++].arg = mask;
777 }
778
779 static void
780 chm_invex(struct Client *client_p, struct Client *source_p,
781 struct Channel *chptr, int parc, int *parn,
782 char **parv, int *errors, int alev, int dir, char c, void *d,
783 const char *chname)
784 {
785 char *mask = NULL;
786
787 /* if we have +I disabled, allow local clients to do anything but
788 * set the mode. This prevents the abuse of +I when just a few
789 * servers support it --fl
790 */
791 if (!ConfigChannel.use_invex && MyClient(source_p) &&
792 (dir == MODE_ADD) && (parc > *parn))
793 {
794 if (*errors & SM_ERR_RPL_I)
795 return;
796
797 *errors |= SM_ERR_RPL_I;
798 return;
799 }
800
801 if (alev < CHACCESS_HALFOP)
802 {
803 if (!(*errors & SM_ERR_NOOPS))
804 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
805 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
806 me.name, source_p->name, chname);
807 *errors |= SM_ERR_NOOPS;
808 return;
809 }
810
811 if (dir == MODE_QUERY || parc <= *parn)
812 {
813 dlink_node *ptr = NULL;
814
815 if (*errors & SM_ERR_RPL_I)
816 return;
817
818 *errors |= SM_ERR_RPL_I;
819
820 DLINK_FOREACH(ptr, chptr->invexlist.head)
821 {
822 const struct Ban *banptr = ptr->data;
823 sendto_one(client_p, form_str(RPL_INVITELIST), me.name,
824 client_p->name, chname,
825 banptr->name, banptr->username, banptr->host,
826 banptr->who, banptr->when);
827 }
828
829 sendto_one(source_p, form_str(RPL_ENDOFINVITELIST), me.name,
830 source_p->name, chname);
831 return;
832 }
833
834 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
835 return;
836
837 mask = nuh_mask[*parn];
838 memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
839 ++*parn;
840
841 if (IsServer(client_p))
842 if (strchr(mask, ' '))
843 return;
844
845 switch (dir)
846 {
847 case MODE_ADD:
848 if (!add_id(source_p, chptr, mask, CHFL_INVEX))
849 return;
850 break;
851 case MODE_DEL:
852 if (!del_id(chptr, mask, CHFL_INVEX))
853 return;
854 break;
855 default:
856 assert(0);
857 }
858
859 mode_changes[mode_count].letter = c;
860 mode_changes[mode_count].dir = dir;
861 mode_changes[mode_count].caps = CAP_IE;
862 mode_changes[mode_count].nocaps = 0;
863
864 if (ConfigChannel.use_invex)
865 mode_changes[mode_count].mems = ONLY_CHANOPS;
866 else
867 mode_changes[mode_count].mems = ONLY_SERVERS;
868
869 mode_changes[mode_count].id = NULL;
870 mode_changes[mode_count++].arg = mask;
871 }
872
873 /*
874 * inputs - pointer to channel
875 * output - none
876 * side effects - clear ban cache
877 */
878 void
879 clear_ban_cache(struct Channel *chptr)
880 {
881 dlink_node *ptr = NULL;
882
883 DLINK_FOREACH(ptr, chptr->members.head)
884 {
885 struct Membership *ms = ptr->data;
886
887 if (MyConnect(ms->client_p))
888 ms->flags &= ~(CHFL_BAN_SILENCED|CHFL_BAN_CHECKED);
889 }
890 }
891
892 static void
893 chm_op(struct Client *client_p, struct Client *source_p,
894 struct Channel *chptr, int parc, int *parn,
895 char **parv, int *errors, int alev, int dir, char c, void *d,
896 const char *chname)
897 {
898 char *opnick;
899 struct Client *targ_p;
900 struct Membership *member;
901 int caps = 0;
902
903 if (alev < CHACCESS_CHANOP)
904 {
905 if (!(*errors & SM_ERR_NOOPS))
906 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
907 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
908 me.name, source_p->name, chname);
909 *errors |= SM_ERR_NOOPS;
910 return;
911 }
912
913 if ((dir == MODE_QUERY) || (parc <= *parn))
914 return;
915
916 opnick = parv[(*parn)++];
917
918 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
919 return;
920 if (!IsClient(targ_p))
921 return;
922
923 if ((member = find_channel_link(targ_p, chptr)) == NULL)
924 {
925 if (!(*errors & SM_ERR_NOTONCHANNEL))
926 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
927 me.name, source_p->name, opnick, chname);
928 *errors |= SM_ERR_NOTONCHANNEL;
929 return;
930 }
931
932 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
933 return;
934
935 /* no redundant mode changes */
936 if (dir == MODE_ADD && has_member_flags(member, CHFL_CHANOP))
937 return;
938 if (dir == MODE_DEL && !has_member_flags(member, CHFL_CHANOP))
939 {
940 #ifdef HALFOPS
941 if (has_member_flags(member, CHFL_HALFOP))
942 chm_hop(client_p, source_p, chptr, parc, parn, parv, errors, alev,
943 dir, c, d, chname);
944 #endif
945 return;
946 }
947
948 #ifdef HALFOPS
949 if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP))
950 {
951 /* promoting from % to @ is visible only to CAP_HOPS servers */
952 mode_changes[mode_count].letter = 'h';
953 mode_changes[mode_count].dir = MODE_DEL;
954 mode_changes[mode_count].caps = caps = CAP_HOPS;
955 mode_changes[mode_count].nocaps = 0;
956 mode_changes[mode_count].mems = ALL_MEMBERS;
957 mode_changes[mode_count].id = NULL;
958 mode_changes[mode_count].arg = targ_p->name;
959 mode_changes[mode_count++].client = targ_p;
960 }
961 #endif
962
963 mode_changes[mode_count].letter = 'o';
964 mode_changes[mode_count].dir = dir;
965 mode_changes[mode_count].caps = caps;
966 mode_changes[mode_count].nocaps = 0;
967 mode_changes[mode_count].mems = ALL_MEMBERS;
968 mode_changes[mode_count].id = targ_p->id;
969 mode_changes[mode_count].arg = targ_p->name;
970 mode_changes[mode_count++].client = targ_p;
971
972 if (dir == MODE_ADD)
973 {
974 AddMemberFlag(member, CHFL_CHANOP);
975 DelMemberFlag(member, CHFL_DEOPPED | CHFL_HALFOP);
976 }
977 else
978 DelMemberFlag(member, CHFL_CHANOP);
979 }
980
981 #ifdef HALFOPS
982 static void
983 chm_hop(struct Client *client_p, struct Client *source_p,
984 struct Channel *chptr, int parc, int *parn,
985 char **parv, int *errors, int alev, int dir, char c, void *d,
986 const char *chname)
987 {
988 char *opnick;
989 struct Client *targ_p;
990 struct Membership *member;
991
992 /* *sigh* - dont allow halfops to set +/-h, they could fully control a
993 * channel if there were no ops - it doesnt solve anything.. MODE_PRIVATE
994 * when used with MODE_SECRET is paranoid - cant use +p
995 *
996 * it needs to be optional per channel - but not via +p, that or remove
997 * paranoid.. -- fl_
998 *
999 * +p means paranoid, it is useless for anything else on modern IRC, as
1000 * list isn't really usable. If you want to have a private channel these
1001 * days, you set it +s. Halfops can no longer remove simple modes when
1002 * +p is set (although they can set +p) so it is safe to use this to
1003 * control whether they can (de)halfop...
1004 */
1005 if (alev <
1006 ((chptr->mode.mode & MODE_PRIVATE) ? CHACCESS_CHANOP : CHACCESS_HALFOP))
1007 {
1008 if (!(*errors & SM_ERR_NOOPS))
1009 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1010 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1011 me.name, source_p->name, chname);
1012 *errors |= SM_ERR_NOOPS;
1013 return;
1014 }
1015
1016 if ((dir == MODE_QUERY) || (parc <= *parn))
1017 return;
1018
1019 opnick = parv[(*parn)++];
1020
1021 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
1022 return;
1023 if (!IsClient(targ_p))
1024 return;
1025
1026 if ((member = find_channel_link(targ_p, chptr)) == NULL)
1027 {
1028 if (!(*errors & SM_ERR_NOTONCHANNEL))
1029 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
1030 me.name, source_p->name, opnick, chname);
1031 *errors |= SM_ERR_NOTONCHANNEL;
1032 return;
1033 }
1034
1035 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1036 return;
1037
1038 /* no redundant mode changes */
1039 if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP | CHFL_CHANOP))
1040 return;
1041 if (dir == MODE_DEL && !has_member_flags(member, CHFL_HALFOP))
1042 return;
1043
1044 mode_changes[mode_count].letter = 'h';
1045 mode_changes[mode_count].dir = dir;
1046 mode_changes[mode_count].caps = CAP_HOPS;
1047 mode_changes[mode_count].nocaps = 0;
1048 mode_changes[mode_count].mems = ALL_MEMBERS;
1049 mode_changes[mode_count].id = targ_p->id;
1050 mode_changes[mode_count].arg = targ_p->name;
1051 mode_changes[mode_count++].client = targ_p;
1052
1053 mode_changes[mode_count].letter = 'o';
1054 mode_changes[mode_count].dir = dir;
1055 mode_changes[mode_count].caps = 0;
1056 mode_changes[mode_count].nocaps = CAP_HOPS;
1057 mode_changes[mode_count].mems = ONLY_SERVERS;
1058 mode_changes[mode_count].id = targ_p->id;
1059 mode_changes[mode_count].arg = targ_p->name;
1060 mode_changes[mode_count++].client = targ_p;
1061
1062 if (dir == MODE_ADD)
1063 {
1064 AddMemberFlag(member, CHFL_HALFOP);
1065 DelMemberFlag(member, CHFL_DEOPPED);
1066 }
1067 else
1068 DelMemberFlag(member, CHFL_HALFOP);
1069 }
1070 #endif
1071
1072 static void
1073 chm_voice(struct Client *client_p, struct Client *source_p,
1074 struct Channel *chptr, int parc, int *parn,
1075 char **parv, int *errors, int alev, int dir, char c, void *d,
1076 const char *chname)
1077 {
1078 char *opnick;
1079 struct Client *targ_p;
1080 struct Membership *member;
1081
1082 if (alev < CHACCESS_HALFOP)
1083 {
1084 if (!(*errors & SM_ERR_NOOPS))
1085 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1086 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1087 me.name, source_p->name, chname);
1088 *errors |= SM_ERR_NOOPS;
1089 return;
1090 }
1091
1092 if ((dir == MODE_QUERY) || parc <= *parn)
1093 return;
1094
1095 opnick = parv[(*parn)++];
1096
1097 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
1098 return;
1099 if (!IsClient(targ_p))
1100 return;
1101
1102 if ((member = find_channel_link(targ_p, chptr)) == NULL)
1103 {
1104 if (!(*errors & SM_ERR_NOTONCHANNEL))
1105 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
1106 me.name, source_p->name, opnick, chname);
1107 *errors |= SM_ERR_NOTONCHANNEL;
1108 return;
1109 }
1110
1111 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1112 return;
1113
1114 /* no redundant mode changes */
1115 if (dir == MODE_ADD && has_member_flags(member, CHFL_VOICE))
1116 return;
1117 if (dir == MODE_DEL && !has_member_flags(member, CHFL_VOICE))
1118 return;
1119
1120 mode_changes[mode_count].letter = 'v';
1121 mode_changes[mode_count].dir = dir;
1122 mode_changes[mode_count].caps = 0;
1123 mode_changes[mode_count].nocaps = 0;
1124 mode_changes[mode_count].mems = ALL_MEMBERS;
1125 mode_changes[mode_count].id = targ_p->id;
1126 mode_changes[mode_count].arg = targ_p->name;
1127 mode_changes[mode_count++].client = targ_p;
1128
1129 if (dir == MODE_ADD)
1130 AddMemberFlag(member, CHFL_VOICE);
1131 else
1132 DelMemberFlag(member, CHFL_VOICE);
1133 }
1134
1135 static void
1136 chm_limit(struct Client *client_p, struct Client *source_p,
1137 struct Channel *chptr, int parc, int *parn,
1138 char **parv, int *errors, int alev, int dir, char c, void *d,
1139 const char *chname)
1140 {
1141 int i, limit;
1142 char *lstr;
1143
1144 if (alev < CHACCESS_HALFOP)
1145 {
1146 if (!(*errors & SM_ERR_NOOPS))
1147 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1148 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1149 me.name, source_p->name, chname);
1150 *errors |= SM_ERR_NOOPS;
1151 return;
1152 }
1153
1154 if (dir == MODE_QUERY)
1155 return;
1156
1157 if ((dir == MODE_ADD) && parc > *parn)
1158 {
1159 lstr = parv[(*parn)++];
1160
1161 if ((limit = atoi(lstr)) <= 0)
1162 return;
1163
1164 ircsprintf(lstr, "%d", limit);
1165
1166 /* if somebody sets MODE #channel +ll 1 2, accept latter --fl */
1167 for (i = 0; i < mode_count; i++)
1168 {
1169 if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
1170 mode_changes[i].letter = 0;
1171 }
1172
1173 mode_changes[mode_count].letter = c;
1174 mode_changes[mode_count].dir = MODE_ADD;
1175 mode_changes[mode_count].caps = 0;
1176 mode_changes[mode_count].nocaps = 0;
1177 mode_changes[mode_count].mems = ALL_MEMBERS;
1178 mode_changes[mode_count].id = NULL;
1179 mode_changes[mode_count++].arg = lstr;
1180
1181 chptr->mode.limit = limit;
1182 }
1183 else if (dir == MODE_DEL)
1184 {
1185 if (!chptr->mode.limit)
1186 return;
1187
1188 chptr->mode.limit = 0;
1189
1190 mode_changes[mode_count].letter = c;
1191 mode_changes[mode_count].dir = MODE_DEL;
1192 mode_changes[mode_count].caps = 0;
1193 mode_changes[mode_count].nocaps = 0;
1194 mode_changes[mode_count].mems = ALL_MEMBERS;
1195 mode_changes[mode_count].id = NULL;
1196 mode_changes[mode_count++].arg = NULL;
1197 }
1198 }
1199
1200 static void
1201 chm_key(struct Client *client_p, struct Client *source_p,
1202 struct Channel *chptr, int parc, int *parn,
1203 char **parv, int *errors, int alev, int dir, char c, void *d,
1204 const char *chname)
1205 {
1206 int i;
1207 char *key;
1208
1209 if (alev < CHACCESS_HALFOP)
1210 {
1211 if (!(*errors & SM_ERR_NOOPS))
1212 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1213 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1214 me.name, source_p->name, chname);
1215 *errors |= SM_ERR_NOOPS;
1216 return;
1217 }
1218
1219 if (dir == MODE_QUERY)
1220 return;
1221
1222 if ((dir == MODE_ADD) && parc > *parn)
1223 {
1224 key = parv[(*parn)++];
1225
1226 if (MyClient(source_p))
1227 fix_key(key);
1228 else
1229 fix_key_old(key);
1230
1231 if (*key == '\0')
1232 return;
1233
1234 assert(key[0] != ' ');
1235 strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key));
1236
1237 /* if somebody does MODE #channel +kk a b, accept latter --fl */
1238 for (i = 0; i < mode_count; i++)
1239 {
1240 if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
1241 mode_changes[i].letter = 0;
1242 }
1243
1244 mode_changes[mode_count].letter = c;
1245 mode_changes[mode_count].dir = MODE_ADD;
1246 mode_changes[mode_count].caps = 0;
1247 mode_changes[mode_count].nocaps = 0;
1248 mode_changes[mode_count].mems = ALL_MEMBERS;
1249 mode_changes[mode_count].id = NULL;
1250 mode_changes[mode_count++].arg = chptr->mode.key;
1251 }
1252 else if (dir == MODE_DEL)
1253 {
1254 if (parc > *parn)
1255 (*parn)++;
1256
1257 if (chptr->mode.key[0] == '\0')
1258 return;
1259
1260 chptr->mode.key[0] = '\0';
1261
1262 mode_changes[mode_count].letter = c;
1263 mode_changes[mode_count].dir = MODE_DEL;
1264 mode_changes[mode_count].caps = 0;
1265 mode_changes[mode_count].nocaps = 0;
1266 mode_changes[mode_count].mems = ALL_MEMBERS;
1267 mode_changes[mode_count].id = NULL;
1268 mode_changes[mode_count++].arg = "*";
1269 }
1270 }
1271
1272 struct ChannelMode
1273 {
1274 void (*func) (struct Client *client_p, struct Client *source_p,
1275 struct Channel *chptr, int parc, int *parn, char **parv,
1276 int *errors, int alev, int dir, char c, void *d,
1277 const char *chname);
1278 void *d;
1279 };
1280
1281 /* *INDENT-OFF* */
1282 static struct ChannelMode ModeTable[255] =
1283 {
1284 {chm_nosuch, NULL},
1285 {chm_nosuch, NULL}, /* A */
1286 {chm_nosuch, NULL}, /* B */
1287 {chm_nosuch, NULL}, /* C */
1288 {chm_nosuch, NULL}, /* D */
1289 {chm_nosuch, NULL}, /* E */
1290 {chm_nosuch, NULL}, /* F */
1291 {chm_nosuch, NULL}, /* G */
1292 {chm_nosuch, NULL}, /* H */
1293 {chm_invex, NULL}, /* I */
1294 {chm_nosuch, NULL}, /* J */
1295 {chm_nosuch, NULL}, /* K */
1296 {chm_nosuch, NULL}, /* L */
1297 {chm_nosuch, NULL}, /* M */
1298 {chm_nosuch, NULL}, /* N */
1299 {chm_nosuch, NULL}, /* O */
1300 {chm_nosuch, NULL}, /* P */
1301 {chm_nosuch, NULL}, /* Q */
1302 {chm_nosuch, NULL}, /* R */
1303 {chm_nosuch, NULL}, /* S */
1304 {chm_nosuch, NULL}, /* T */
1305 {chm_nosuch, NULL}, /* U */
1306 {chm_nosuch, NULL}, /* V */
1307 {chm_nosuch, NULL}, /* W */
1308 {chm_nosuch, NULL}, /* X */
1309 {chm_nosuch, NULL}, /* Y */
1310 {chm_nosuch, NULL}, /* Z */
1311 {chm_nosuch, NULL},
1312 {chm_nosuch, NULL},
1313 {chm_nosuch, NULL},
1314 {chm_nosuch, NULL},
1315 {chm_nosuch, NULL},
1316 {chm_nosuch, NULL},
1317 {chm_nosuch, NULL}, /* a */
1318 {chm_ban, NULL}, /* b */
1319 {chm_nosuch, NULL}, /* c */
1320 {chm_nosuch, NULL}, /* d */
1321 {chm_except, NULL}, /* e */
1322 {chm_nosuch, NULL}, /* f */
1323 {chm_nosuch, NULL}, /* g */
1324 #ifdef HALFOPS
1325 {chm_hop, NULL}, /* h */
1326 #else
1327 {chm_nosuch, NULL}, /* h */
1328 #endif
1329 {chm_simple, (void *) MODE_INVITEONLY}, /* i */
1330 {chm_nosuch, NULL}, /* j */
1331 {chm_key, NULL}, /* k */
1332 {chm_limit, NULL}, /* l */
1333 {chm_simple, (void *) MODE_MODERATED}, /* m */
1334 {chm_simple, (void *) MODE_NOPRIVMSGS}, /* n */
1335 {chm_op, NULL}, /* o */
1336 {chm_simple, (void *) MODE_PRIVATE}, /* p */
1337 {chm_nosuch, NULL}, /* q */
1338 {chm_nosuch, NULL}, /* r */
1339 {chm_simple, (void *) MODE_SECRET}, /* s */
1340 {chm_simple, (void *) MODE_TOPICLIMIT}, /* t */
1341 {chm_nosuch, NULL}, /* u */
1342 {chm_voice, NULL}, /* v */
1343 {chm_nosuch, NULL}, /* w */
1344 {chm_nosuch, NULL}, /* x */
1345 {chm_nosuch, NULL}, /* y */
1346 {chm_nosuch, NULL}, /* z */
1347 };
1348 /* *INDENT-ON* */
1349
1350 /* get_channel_access()
1351 *
1352 * inputs - pointer to Client struct
1353 * - pointer to Membership struct
1354 * output - CHACCESS_CHANOP if we should let them have
1355 * chanop level access, 0 for peon level access.
1356 * side effects - NONE
1357 */
1358 static int
1359 get_channel_access(struct Client *source_p, struct Membership *member)
1360 {
1361 /* Let hacked servers in for now... */
1362 if (!MyClient(source_p))
1363 return CHACCESS_CHANOP;
1364
1365 if (member == NULL)
1366 return CHACCESS_NOTONCHAN;
1367
1368 /* just to be sure.. */
1369 assert(source_p == member->client_p);
1370
1371 if (has_member_flags(member, CHFL_CHANOP))
1372 return CHACCESS_CHANOP;
1373
1374 #ifdef HALFOPS
1375 if (has_member_flags(member, CHFL_HALFOP))
1376 return CHACCESS_HALFOP;
1377 #endif
1378
1379 return CHACCESS_PEON;
1380 }
1381
1382 /* void send_cap_mode_changes(struct Client *client_p,
1383 * struct Client *source_p,
1384 * struct Channel *chptr, int cap, int nocap)
1385 * Input: The client sending(client_p), the source client(source_p),
1386 * the channel to send mode changes for(chptr)
1387 * Output: None.
1388 * Side-effects: Sends the appropriate mode changes to capable servers.
1389 *
1390 * send_cap_mode_changes() will loop the server list itself, because
1391 * at this point in time we have 4 capabs for channels, CAP_IE, CAP_EX,
1392 * and a server could support any number of these..
1393 * so we make the modebufs per server, tailoring them to each servers
1394 * specific demand. Its not very pretty, but its one of the few realistic
1395 * ways to handle having this many capabs for channel modes.. --fl_
1396 *
1397 * Reverted back to my original design, except that we now keep a count
1398 * of the number of servers which each combination as an optimisation, so
1399 * the capabs combinations which are not needed are not worked out. -A1kmm
1400 */
1401 /* rewritten to ensure parabuf < MODEBUFLEN -db */
1402
1403 static void
1404 send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
1405 struct Channel *chptr, int cap, int nocap)
1406 {
1407 int i, mbl, pbl, arglen, nc, mc;
1408 int len;
1409 const char *arg = NULL;
1410 char *parptr;
1411 int dir = MODE_QUERY;
1412
1413 mc = 0;
1414 nc = 0;
1415 pbl = 0;
1416
1417 parabuf[0] = '\0';
1418 parptr = parabuf;
1419
1420 if ((cap & CAP_TS6) && source_p->id[0] != '\0')
1421 mbl = ircsprintf(modebuf, ":%s TMODE %lu %s ", source_p->id,
1422 (unsigned long)chptr->channelts, chptr->chname);
1423 else
1424 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
1425 chptr->chname);
1426
1427 /* loop the list of - modes we have */
1428 for (i = 0; i < mode_count; i++)
1429 {
1430 /* if they dont support the cap we need, or they do support a cap they
1431 * cant have, then dont add it to the modebuf.. that way they wont see
1432 * the mode
1433 */
1434 if ((mode_changes[i].letter == 0) ||
1435 ((cap & mode_changes[i].caps) != mode_changes[i].caps)
1436 || ((nocap & mode_changes[i].nocaps) != mode_changes[i].nocaps))
1437 continue;
1438
1439 arg = "";
1440
1441 if ((cap & CAP_TS6) && mode_changes[i].id)
1442 arg = mode_changes[i].id;
1443 if (*arg == '\0')
1444 arg = mode_changes[i].arg;
1445
1446 /* if we're creeping past the buf size, we need to send it and make
1447 * another line for the other modes
1448 * XXX - this could give away server topology with uids being
1449 * different lengths, but not much we can do, except possibly break
1450 * them as if they were the longest of the nick or uid at all times,
1451 * which even then won't work as we don't always know the uid -A1kmm.
1452 */
1453 if (arg != NULL)
1454 arglen = strlen(arg);
1455 else
1456 arglen = 0;
1457
1458 if ((mc == MAXMODEPARAMS) ||
1459 ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
1460 (pbl + arglen + BAN_FUDGE) >= MODEBUFLEN)
1461 {
1462 if (nc != 0)
1463 sendto_server(client_p, source_p, chptr, cap, nocap,
1464 LL_ICHAN | LL_ICLIENT, "%s %s",
1465 modebuf, parabuf);
1466 nc = 0;
1467 mc = 0;
1468
1469 if ((cap & CAP_TS6) && source_p->id[0] != '\0')
1470 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->id,
1471 chptr->chname);
1472 else
1473 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
1474 chptr->chname);
1475
1476 pbl = 0;
1477 parabuf[0] = '\0';
1478 parptr = parabuf;
1479 dir = MODE_QUERY;
1480 }
1481
1482 if (dir != mode_changes[i].dir)
1483 {
1484 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
1485 dir = mode_changes[i].dir;
1486 }
1487
1488 modebuf[mbl++] = mode_changes[i].letter;
1489 modebuf[mbl] = '\0';
1490 nc++;
1491
1492 if (arg != NULL)
1493 {
1494 len = ircsprintf(parptr, "%s ", arg);
1495 pbl += len;
1496 parptr += len;
1497 mc++;
1498 }
1499 }
1500
1501 if (pbl && parabuf[pbl - 1] == ' ')
1502 parabuf[pbl - 1] = 0;
1503
1504 if (nc != 0)
1505 sendto_server(client_p, source_p, chptr, cap, nocap,
1506 LL_ICLIENT, "%s %s", modebuf, parabuf);
1507 }
1508
1509 /* void send_mode_changes(struct Client *client_p,
1510 * struct Client *source_p,
1511 * struct Channel *chptr)
1512 * Input: The client sending(client_p), the source client(source_p),
1513 * the channel to send mode changes for(chptr),
1514 * mode change globals.
1515 * Output: None.
1516 * Side-effects: Sends the appropriate mode changes to other clients
1517 * and propagates to servers.
1518 */
1519 /* ensure parabuf < MODEBUFLEN -db */
1520 static void
1521 send_mode_changes(struct Client *client_p, struct Client *source_p,
1522 struct Channel *chptr, char *chname)
1523 {
1524 int i, mbl, pbl, arglen, nc, mc;
1525 int len;
1526 const char *arg = NULL;
1527 char *parptr;
1528 int dir = MODE_QUERY;
1529
1530 /* bail out if we have nothing to do... */
1531 if (!mode_count)
1532 return;
1533
1534 if (IsServer(source_p))
1535 mbl = ircsprintf(modebuf, ":%s MODE %s ", (IsHidden(source_p) ||
1536 ConfigServerHide.hide_servers) ?
1537 me.name : source_p->name, chname);
1538 else
1539 mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
1540 source_p->username, source_p->host, chname);
1541
1542 mc = 0;
1543 nc = 0;
1544 pbl = 0;
1545
1546 parabuf[0] = '\0';
1547 parptr = parabuf;
1548
1549 for (i = 0; i < mode_count; i++)
1550 {
1551 if (mode_changes[i].letter == 0 ||
1552 mode_changes[i].mems == NON_CHANOPS ||
1553 mode_changes[i].mems == ONLY_SERVERS)
1554 continue;
1555
1556 arg = mode_changes[i].arg;
1557 if (arg != NULL)
1558 arglen = strlen(arg);
1559 else
1560 arglen = 0;
1561
1562 if ((mc == MAXMODEPARAMS) ||
1563 ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
1564 ((arglen + pbl + BAN_FUDGE) >= MODEBUFLEN))
1565 {
1566 if (mbl && modebuf[mbl - 1] == '-')
1567 modebuf[mbl - 1] = '\0';
1568
1569 if (nc != 0)
1570 sendto_channel_local(ALL_MEMBERS, NO, chptr, "%s %s", modebuf, parabuf);
1571
1572 nc = 0;
1573 mc = 0;
1574
1575 if (IsServer(source_p))
1576 mbl = ircsprintf(modebuf, ":%s MODE %s ", me.name, chname);
1577 else
1578 mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
1579 source_p->username, source_p->host, chname);
1580
1581 pbl = 0;
1582 parabuf[0] = '\0';
1583 parptr = parabuf;
1584 dir = MODE_QUERY;
1585 }
1586
1587 if (dir != mode_changes[i].dir)
1588 {
1589 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
1590 dir = mode_changes[i].dir;
1591 }
1592
1593 modebuf[mbl++] = mode_changes[i].letter;
1594 modebuf[mbl] = '\0';
1595 nc++;
1596
1597 if (arg != NULL)
1598 {
1599 len = ircsprintf(parptr, "%s ", arg);
1600 pbl += len;
1601 parptr += len;
1602 mc++;
1603 }
1604 }
1605
1606 if (pbl && parabuf[pbl - 1] == ' ')
1607 parabuf[pbl - 1] = 0;
1608
1609 if (nc != 0)
1610 sendto_channel_local(ALL_MEMBERS, NO, chptr, "%s %s", modebuf, parabuf);
1611
1612 nc = 0;
1613 mc = 0;
1614
1615 /* Now send to servers... */
1616 for (i = 0; i < NCHCAP_COMBOS; i++)
1617 if (chcap_combos[i].count != 0)
1618 send_cap_mode_changes(client_p, source_p, chptr,
1619 chcap_combos[i].cap_yes,
1620 chcap_combos[i].cap_no);
1621 }
1622
1623 /* void set_channel_mode(struct Client *client_p, struct Client *source_p,
1624 * struct Channel *chptr, int parc, char **parv,
1625 * char *chname)
1626 * Input: The client we received this from, the client this originated
1627 * from, the channel, the parameter count starting at the modes,
1628 * the parameters, the channel name.
1629 * Output: None.
1630 * Side-effects: Changes the channel membership and modes appropriately,
1631 * sends the appropriate MODE messages to the appropriate
1632 * clients.
1633 */
1634 void
1635 set_channel_mode(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
1636 struct Membership *member, int parc, char *parv[], char *chname)
1637 {
1638 int dir = MODE_ADD;
1639 int parn = 1;
1640 int alevel, errors = 0;
1641 char *ml = parv[0], c;
1642 int table_position;
1643
1644 mode_count = 0;
1645 mode_limit = 0;
1646 simple_modes_mask = 0;
1647
1648 alevel = get_channel_access(source_p, member);
1649
1650 for (; (c = *ml) != '\0'; ml++)
1651 {
1652 #if 0
1653 if(mode_count > 20)
1654 break;
1655 #endif
1656 switch (c)
1657 {
1658 case '+':
1659 dir = MODE_ADD;
1660 break;
1661 case '-':
1662 dir = MODE_DEL;
1663 break;
1664 case '=':
1665 dir = MODE_QUERY;
1666 break;
1667 default:
1668 if (c < 'A' || c > 'z')
1669 table_position = 0;
1670 else
1671 table_position = c - 'A' + 1;
1672 ModeTable[table_position].func(client_p, source_p, chptr,
1673 parc, &parn,
1674 parv, &errors, alevel, dir, c,
1675 ModeTable[table_position].d,
1676 chname);
1677 break;
1678 }
1679 }
1680
1681 send_mode_changes(client_p, source_p, chptr, chname);
1682 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision