ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/src/channel_mode.c
Revision: 388
Committed: Wed Feb 1 12:51:45 2006 UTC (18 years, 2 months ago) by michael
Content type: text/x-csrc
File size: 47861 byte(s)
Log Message:
- Forward-port banfix from 7.2 (reported by ThaPrince)

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->locmembers.head)
884 {
885 struct Membership *ms = ptr->data;
886 ms->flags &= ~(CHFL_BAN_SILENCED|CHFL_BAN_CHECKED);
887 }
888 }
889
890 static void
891 chm_op(struct Client *client_p, struct Client *source_p,
892 struct Channel *chptr, int parc, int *parn,
893 char **parv, int *errors, int alev, int dir, char c, void *d,
894 const char *chname)
895 {
896 char *opnick;
897 struct Client *targ_p;
898 struct Membership *member;
899 int caps = 0;
900
901 if (alev < CHACCESS_CHANOP)
902 {
903 if (!(*errors & SM_ERR_NOOPS))
904 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
905 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
906 me.name, source_p->name, chname);
907 *errors |= SM_ERR_NOOPS;
908 return;
909 }
910
911 if ((dir == MODE_QUERY) || (parc <= *parn))
912 return;
913
914 opnick = parv[(*parn)++];
915
916 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
917 return;
918 if (!IsClient(targ_p))
919 return;
920
921 if ((member = find_channel_link(targ_p, chptr)) == NULL)
922 {
923 if (!(*errors & SM_ERR_NOTONCHANNEL))
924 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
925 me.name, source_p->name, opnick, chname);
926 *errors |= SM_ERR_NOTONCHANNEL;
927 return;
928 }
929
930 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
931 return;
932
933 /* no redundant mode changes */
934 if (dir == MODE_ADD && has_member_flags(member, CHFL_CHANOP))
935 return;
936 if (dir == MODE_DEL && !has_member_flags(member, CHFL_CHANOP))
937 {
938 #ifdef HALFOPS
939 if (has_member_flags(member, CHFL_HALFOP))
940 chm_hop(client_p, source_p, chptr, parc, parn, parv, errors, alev,
941 dir, c, d, chname);
942 #endif
943 return;
944 }
945
946 #ifdef HALFOPS
947 if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP))
948 {
949 /* promoting from % to @ is visible only to CAP_HOPS servers */
950 mode_changes[mode_count].letter = 'h';
951 mode_changes[mode_count].dir = MODE_DEL;
952 mode_changes[mode_count].caps = caps = CAP_HOPS;
953 mode_changes[mode_count].nocaps = 0;
954 mode_changes[mode_count].mems = ALL_MEMBERS;
955 mode_changes[mode_count].id = NULL;
956 mode_changes[mode_count].arg = targ_p->name;
957 mode_changes[mode_count++].client = targ_p;
958 }
959 #endif
960
961 mode_changes[mode_count].letter = 'o';
962 mode_changes[mode_count].dir = dir;
963 mode_changes[mode_count].caps = caps;
964 mode_changes[mode_count].nocaps = 0;
965 mode_changes[mode_count].mems = ALL_MEMBERS;
966 mode_changes[mode_count].id = targ_p->id;
967 mode_changes[mode_count].arg = targ_p->name;
968 mode_changes[mode_count++].client = targ_p;
969
970 if (dir == MODE_ADD)
971 {
972 AddMemberFlag(member, CHFL_CHANOP);
973 DelMemberFlag(member, CHFL_DEOPPED | CHFL_HALFOP);
974 }
975 else
976 DelMemberFlag(member, CHFL_CHANOP);
977 }
978
979 #ifdef HALFOPS
980 static void
981 chm_hop(struct Client *client_p, struct Client *source_p,
982 struct Channel *chptr, int parc, int *parn,
983 char **parv, int *errors, int alev, int dir, char c, void *d,
984 const char *chname)
985 {
986 char *opnick;
987 struct Client *targ_p;
988 struct Membership *member;
989
990 /* *sigh* - dont allow halfops to set +/-h, they could fully control a
991 * channel if there were no ops - it doesnt solve anything.. MODE_PRIVATE
992 * when used with MODE_SECRET is paranoid - cant use +p
993 *
994 * it needs to be optional per channel - but not via +p, that or remove
995 * paranoid.. -- fl_
996 *
997 * +p means paranoid, it is useless for anything else on modern IRC, as
998 * list isn't really usable. If you want to have a private channel these
999 * days, you set it +s. Halfops can no longer remove simple modes when
1000 * +p is set (although they can set +p) so it is safe to use this to
1001 * control whether they can (de)halfop...
1002 */
1003 if (alev <
1004 ((chptr->mode.mode & MODE_PRIVATE) ? CHACCESS_CHANOP : CHACCESS_HALFOP))
1005 {
1006 if (!(*errors & SM_ERR_NOOPS))
1007 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1008 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1009 me.name, source_p->name, chname);
1010 *errors |= SM_ERR_NOOPS;
1011 return;
1012 }
1013
1014 if ((dir == MODE_QUERY) || (parc <= *parn))
1015 return;
1016
1017 opnick = parv[(*parn)++];
1018
1019 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
1020 return;
1021 if (!IsClient(targ_p))
1022 return;
1023
1024 if ((member = find_channel_link(targ_p, chptr)) == NULL)
1025 {
1026 if (!(*errors & SM_ERR_NOTONCHANNEL))
1027 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
1028 me.name, source_p->name, opnick, chname);
1029 *errors |= SM_ERR_NOTONCHANNEL;
1030 return;
1031 }
1032
1033 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1034 return;
1035
1036 /* no redundant mode changes */
1037 if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP | CHFL_CHANOP))
1038 return;
1039 if (dir == MODE_DEL && !has_member_flags(member, CHFL_HALFOP))
1040 return;
1041
1042 mode_changes[mode_count].letter = 'h';
1043 mode_changes[mode_count].dir = dir;
1044 mode_changes[mode_count].caps = CAP_HOPS;
1045 mode_changes[mode_count].nocaps = 0;
1046 mode_changes[mode_count].mems = ALL_MEMBERS;
1047 mode_changes[mode_count].id = targ_p->id;
1048 mode_changes[mode_count].arg = targ_p->name;
1049 mode_changes[mode_count++].client = targ_p;
1050
1051 mode_changes[mode_count].letter = 'o';
1052 mode_changes[mode_count].dir = dir;
1053 mode_changes[mode_count].caps = 0;
1054 mode_changes[mode_count].nocaps = CAP_HOPS;
1055 mode_changes[mode_count].mems = ONLY_SERVERS;
1056 mode_changes[mode_count].id = targ_p->id;
1057 mode_changes[mode_count].arg = targ_p->name;
1058 mode_changes[mode_count++].client = targ_p;
1059
1060 if (dir == MODE_ADD)
1061 {
1062 AddMemberFlag(member, CHFL_HALFOP);
1063 DelMemberFlag(member, CHFL_DEOPPED);
1064 }
1065 else
1066 DelMemberFlag(member, CHFL_HALFOP);
1067 }
1068 #endif
1069
1070 static void
1071 chm_voice(struct Client *client_p, struct Client *source_p,
1072 struct Channel *chptr, int parc, int *parn,
1073 char **parv, int *errors, int alev, int dir, char c, void *d,
1074 const char *chname)
1075 {
1076 char *opnick;
1077 struct Client *targ_p;
1078 struct Membership *member;
1079
1080 if (alev < CHACCESS_HALFOP)
1081 {
1082 if (!(*errors & SM_ERR_NOOPS))
1083 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1084 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1085 me.name, source_p->name, chname);
1086 *errors |= SM_ERR_NOOPS;
1087 return;
1088 }
1089
1090 if ((dir == MODE_QUERY) || parc <= *parn)
1091 return;
1092
1093 opnick = parv[(*parn)++];
1094
1095 if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
1096 return;
1097 if (!IsClient(targ_p))
1098 return;
1099
1100 if ((member = find_channel_link(targ_p, chptr)) == NULL)
1101 {
1102 if (!(*errors & SM_ERR_NOTONCHANNEL))
1103 sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
1104 me.name, source_p->name, opnick, chname);
1105 *errors |= SM_ERR_NOTONCHANNEL;
1106 return;
1107 }
1108
1109 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1110 return;
1111
1112 /* no redundant mode changes */
1113 if (dir == MODE_ADD && has_member_flags(member, CHFL_VOICE))
1114 return;
1115 if (dir == MODE_DEL && !has_member_flags(member, CHFL_VOICE))
1116 return;
1117
1118 mode_changes[mode_count].letter = 'v';
1119 mode_changes[mode_count].dir = dir;
1120 mode_changes[mode_count].caps = 0;
1121 mode_changes[mode_count].nocaps = 0;
1122 mode_changes[mode_count].mems = ALL_MEMBERS;
1123 mode_changes[mode_count].id = targ_p->id;
1124 mode_changes[mode_count].arg = targ_p->name;
1125 mode_changes[mode_count++].client = targ_p;
1126
1127 if (dir == MODE_ADD)
1128 AddMemberFlag(member, CHFL_VOICE);
1129 else
1130 DelMemberFlag(member, CHFL_VOICE);
1131 }
1132
1133 static void
1134 chm_limit(struct Client *client_p, struct Client *source_p,
1135 struct Channel *chptr, int parc, int *parn,
1136 char **parv, int *errors, int alev, int dir, char c, void *d,
1137 const char *chname)
1138 {
1139 int i, limit;
1140 char *lstr;
1141
1142 if (alev < CHACCESS_HALFOP)
1143 {
1144 if (!(*errors & SM_ERR_NOOPS))
1145 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1146 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1147 me.name, source_p->name, chname);
1148 *errors |= SM_ERR_NOOPS;
1149 return;
1150 }
1151
1152 if (dir == MODE_QUERY)
1153 return;
1154
1155 if ((dir == MODE_ADD) && parc > *parn)
1156 {
1157 lstr = parv[(*parn)++];
1158
1159 if ((limit = atoi(lstr)) <= 0)
1160 return;
1161
1162 ircsprintf(lstr, "%d", limit);
1163
1164 /* if somebody sets MODE #channel +ll 1 2, accept latter --fl */
1165 for (i = 0; i < mode_count; i++)
1166 {
1167 if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
1168 mode_changes[i].letter = 0;
1169 }
1170
1171 mode_changes[mode_count].letter = c;
1172 mode_changes[mode_count].dir = MODE_ADD;
1173 mode_changes[mode_count].caps = 0;
1174 mode_changes[mode_count].nocaps = 0;
1175 mode_changes[mode_count].mems = ALL_MEMBERS;
1176 mode_changes[mode_count].id = NULL;
1177 mode_changes[mode_count++].arg = lstr;
1178
1179 chptr->mode.limit = limit;
1180 }
1181 else if (dir == MODE_DEL)
1182 {
1183 if (!chptr->mode.limit)
1184 return;
1185
1186 chptr->mode.limit = 0;
1187
1188 mode_changes[mode_count].letter = c;
1189 mode_changes[mode_count].dir = MODE_DEL;
1190 mode_changes[mode_count].caps = 0;
1191 mode_changes[mode_count].nocaps = 0;
1192 mode_changes[mode_count].mems = ALL_MEMBERS;
1193 mode_changes[mode_count].id = NULL;
1194 mode_changes[mode_count++].arg = NULL;
1195 }
1196 }
1197
1198 static void
1199 chm_key(struct Client *client_p, struct Client *source_p,
1200 struct Channel *chptr, int parc, int *parn,
1201 char **parv, int *errors, int alev, int dir, char c, void *d,
1202 const char *chname)
1203 {
1204 int i;
1205 char *key;
1206
1207 if (alev < CHACCESS_HALFOP)
1208 {
1209 if (!(*errors & SM_ERR_NOOPS))
1210 sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
1211 ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
1212 me.name, source_p->name, chname);
1213 *errors |= SM_ERR_NOOPS;
1214 return;
1215 }
1216
1217 if (dir == MODE_QUERY)
1218 return;
1219
1220 if ((dir == MODE_ADD) && parc > *parn)
1221 {
1222 key = parv[(*parn)++];
1223
1224 if (MyClient(source_p))
1225 fix_key(key);
1226 else
1227 fix_key_old(key);
1228
1229 if (*key == '\0')
1230 return;
1231
1232 assert(key[0] != ' ');
1233 strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key));
1234
1235 /* if somebody does MODE #channel +kk a b, accept latter --fl */
1236 for (i = 0; i < mode_count; i++)
1237 {
1238 if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
1239 mode_changes[i].letter = 0;
1240 }
1241
1242 mode_changes[mode_count].letter = c;
1243 mode_changes[mode_count].dir = MODE_ADD;
1244 mode_changes[mode_count].caps = 0;
1245 mode_changes[mode_count].nocaps = 0;
1246 mode_changes[mode_count].mems = ALL_MEMBERS;
1247 mode_changes[mode_count].id = NULL;
1248 mode_changes[mode_count++].arg = chptr->mode.key;
1249 }
1250 else if (dir == MODE_DEL)
1251 {
1252 if (parc > *parn)
1253 (*parn)++;
1254
1255 if (chptr->mode.key[0] == '\0')
1256 return;
1257
1258 chptr->mode.key[0] = '\0';
1259
1260 mode_changes[mode_count].letter = c;
1261 mode_changes[mode_count].dir = MODE_DEL;
1262 mode_changes[mode_count].caps = 0;
1263 mode_changes[mode_count].nocaps = 0;
1264 mode_changes[mode_count].mems = ALL_MEMBERS;
1265 mode_changes[mode_count].id = NULL;
1266 mode_changes[mode_count++].arg = "*";
1267 }
1268 }
1269
1270 struct ChannelMode
1271 {
1272 void (*func) (struct Client *client_p, struct Client *source_p,
1273 struct Channel *chptr, int parc, int *parn, char **parv,
1274 int *errors, int alev, int dir, char c, void *d,
1275 const char *chname);
1276 void *d;
1277 };
1278
1279 /* *INDENT-OFF* */
1280 static struct ChannelMode ModeTable[255] =
1281 {
1282 {chm_nosuch, NULL},
1283 {chm_nosuch, NULL}, /* A */
1284 {chm_nosuch, NULL}, /* B */
1285 {chm_nosuch, NULL}, /* C */
1286 {chm_nosuch, NULL}, /* D */
1287 {chm_nosuch, NULL}, /* E */
1288 {chm_nosuch, NULL}, /* F */
1289 {chm_nosuch, NULL}, /* G */
1290 {chm_nosuch, NULL}, /* H */
1291 {chm_invex, NULL}, /* I */
1292 {chm_nosuch, NULL}, /* J */
1293 {chm_nosuch, NULL}, /* K */
1294 {chm_nosuch, NULL}, /* L */
1295 {chm_nosuch, NULL}, /* M */
1296 {chm_nosuch, NULL}, /* N */
1297 {chm_nosuch, NULL}, /* O */
1298 {chm_nosuch, NULL}, /* P */
1299 {chm_nosuch, NULL}, /* Q */
1300 {chm_nosuch, NULL}, /* R */
1301 {chm_nosuch, NULL}, /* S */
1302 {chm_nosuch, NULL}, /* T */
1303 {chm_nosuch, NULL}, /* U */
1304 {chm_nosuch, NULL}, /* V */
1305 {chm_nosuch, NULL}, /* W */
1306 {chm_nosuch, NULL}, /* X */
1307 {chm_nosuch, NULL}, /* Y */
1308 {chm_nosuch, NULL}, /* Z */
1309 {chm_nosuch, NULL},
1310 {chm_nosuch, NULL},
1311 {chm_nosuch, NULL},
1312 {chm_nosuch, NULL},
1313 {chm_nosuch, NULL},
1314 {chm_nosuch, NULL},
1315 {chm_nosuch, NULL}, /* a */
1316 {chm_ban, NULL}, /* b */
1317 {chm_nosuch, NULL}, /* c */
1318 {chm_nosuch, NULL}, /* d */
1319 {chm_except, NULL}, /* e */
1320 {chm_nosuch, NULL}, /* f */
1321 {chm_nosuch, NULL}, /* g */
1322 #ifdef HALFOPS
1323 {chm_hop, NULL}, /* h */
1324 #else
1325 {chm_nosuch, NULL}, /* h */
1326 #endif
1327 {chm_simple, (void *) MODE_INVITEONLY}, /* i */
1328 {chm_nosuch, NULL}, /* j */
1329 {chm_key, NULL}, /* k */
1330 {chm_limit, NULL}, /* l */
1331 {chm_simple, (void *) MODE_MODERATED}, /* m */
1332 {chm_simple, (void *) MODE_NOPRIVMSGS}, /* n */
1333 {chm_op, NULL}, /* o */
1334 {chm_simple, (void *) MODE_PRIVATE}, /* p */
1335 {chm_nosuch, NULL}, /* q */
1336 {chm_nosuch, NULL}, /* r */
1337 {chm_simple, (void *) MODE_SECRET}, /* s */
1338 {chm_simple, (void *) MODE_TOPICLIMIT}, /* t */
1339 {chm_nosuch, NULL}, /* u */
1340 {chm_voice, NULL}, /* v */
1341 {chm_nosuch, NULL}, /* w */
1342 {chm_nosuch, NULL}, /* x */
1343 {chm_nosuch, NULL}, /* y */
1344 {chm_nosuch, NULL}, /* z */
1345 };
1346 /* *INDENT-ON* */
1347
1348 /* get_channel_access()
1349 *
1350 * inputs - pointer to Client struct
1351 * - pointer to Membership struct
1352 * output - CHACCESS_CHANOP if we should let them have
1353 * chanop level access, 0 for peon level access.
1354 * side effects - NONE
1355 */
1356 static int
1357 get_channel_access(struct Client *source_p, struct Membership *member)
1358 {
1359 /* Let hacked servers in for now... */
1360 if (!MyClient(source_p))
1361 return CHACCESS_CHANOP;
1362
1363 if (member == NULL)
1364 return CHACCESS_NOTONCHAN;
1365
1366 /* just to be sure.. */
1367 assert(source_p == member->client_p);
1368
1369 if (has_member_flags(member, CHFL_CHANOP))
1370 return CHACCESS_CHANOP;
1371
1372 #ifdef HALFOPS
1373 if (has_member_flags(member, CHFL_HALFOP))
1374 return CHACCESS_HALFOP;
1375 #endif
1376
1377 return CHACCESS_PEON;
1378 }
1379
1380 /* void send_cap_mode_changes(struct Client *client_p,
1381 * struct Client *source_p,
1382 * struct Channel *chptr, int cap, int nocap)
1383 * Input: The client sending(client_p), the source client(source_p),
1384 * the channel to send mode changes for(chptr)
1385 * Output: None.
1386 * Side-effects: Sends the appropriate mode changes to capable servers.
1387 *
1388 * send_cap_mode_changes() will loop the server list itself, because
1389 * at this point in time we have 4 capabs for channels, CAP_IE, CAP_EX,
1390 * and a server could support any number of these..
1391 * so we make the modebufs per server, tailoring them to each servers
1392 * specific demand. Its not very pretty, but its one of the few realistic
1393 * ways to handle having this many capabs for channel modes.. --fl_
1394 *
1395 * Reverted back to my original design, except that we now keep a count
1396 * of the number of servers which each combination as an optimisation, so
1397 * the capabs combinations which are not needed are not worked out. -A1kmm
1398 */
1399 /* rewritten to ensure parabuf < MODEBUFLEN -db */
1400
1401 static void
1402 send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
1403 struct Channel *chptr, int cap, int nocap)
1404 {
1405 int i, mbl, pbl, arglen, nc, mc;
1406 int len;
1407 const char *arg = NULL;
1408 char *parptr;
1409 int dir = MODE_QUERY;
1410
1411 mc = 0;
1412 nc = 0;
1413 pbl = 0;
1414
1415 parabuf[0] = '\0';
1416 parptr = parabuf;
1417
1418 if ((cap & CAP_TS6) && source_p->id[0] != '\0')
1419 mbl = ircsprintf(modebuf, ":%s TMODE %lu %s ", source_p->id,
1420 (unsigned long)chptr->channelts, chptr->chname);
1421 else
1422 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
1423 chptr->chname);
1424
1425 /* loop the list of - modes we have */
1426 for (i = 0; i < mode_count; i++)
1427 {
1428 /* if they dont support the cap we need, or they do support a cap they
1429 * cant have, then dont add it to the modebuf.. that way they wont see
1430 * the mode
1431 */
1432 if ((mode_changes[i].letter == 0) ||
1433 ((cap & mode_changes[i].caps) != mode_changes[i].caps)
1434 || ((nocap & mode_changes[i].nocaps) != mode_changes[i].nocaps))
1435 continue;
1436
1437 arg = "";
1438
1439 if ((cap & CAP_TS6) && mode_changes[i].id)
1440 arg = mode_changes[i].id;
1441 if (*arg == '\0')
1442 arg = mode_changes[i].arg;
1443
1444 /* if we're creeping past the buf size, we need to send it and make
1445 * another line for the other modes
1446 * XXX - this could give away server topology with uids being
1447 * different lengths, but not much we can do, except possibly break
1448 * them as if they were the longest of the nick or uid at all times,
1449 * which even then won't work as we don't always know the uid -A1kmm.
1450 */
1451 if (arg != NULL)
1452 arglen = strlen(arg);
1453 else
1454 arglen = 0;
1455
1456 if ((mc == MAXMODEPARAMS) ||
1457 ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
1458 (pbl + arglen + BAN_FUDGE) >= MODEBUFLEN)
1459 {
1460 if (nc != 0)
1461 sendto_server(client_p, source_p, chptr, cap, nocap,
1462 LL_ICHAN | LL_ICLIENT, "%s %s",
1463 modebuf, parabuf);
1464 nc = 0;
1465 mc = 0;
1466
1467 if ((cap & CAP_TS6) && source_p->id[0] != '\0')
1468 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->id,
1469 chptr->chname);
1470 else
1471 mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
1472 chptr->chname);
1473
1474 pbl = 0;
1475 parabuf[0] = '\0';
1476 parptr = parabuf;
1477 dir = MODE_QUERY;
1478 }
1479
1480 if (dir != mode_changes[i].dir)
1481 {
1482 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
1483 dir = mode_changes[i].dir;
1484 }
1485
1486 modebuf[mbl++] = mode_changes[i].letter;
1487 modebuf[mbl] = '\0';
1488 nc++;
1489
1490 if (arg != NULL)
1491 {
1492 len = ircsprintf(parptr, "%s ", arg);
1493 pbl += len;
1494 parptr += len;
1495 mc++;
1496 }
1497 }
1498
1499 if (pbl && parabuf[pbl - 1] == ' ')
1500 parabuf[pbl - 1] = 0;
1501
1502 if (nc != 0)
1503 sendto_server(client_p, source_p, chptr, cap, nocap,
1504 LL_ICLIENT, "%s %s", modebuf, parabuf);
1505 }
1506
1507 /* void send_mode_changes(struct Client *client_p,
1508 * struct Client *source_p,
1509 * struct Channel *chptr)
1510 * Input: The client sending(client_p), the source client(source_p),
1511 * the channel to send mode changes for(chptr),
1512 * mode change globals.
1513 * Output: None.
1514 * Side-effects: Sends the appropriate mode changes to other clients
1515 * and propagates to servers.
1516 */
1517 /* ensure parabuf < MODEBUFLEN -db */
1518 static void
1519 send_mode_changes(struct Client *client_p, struct Client *source_p,
1520 struct Channel *chptr, char *chname)
1521 {
1522 int i, mbl, pbl, arglen, nc, mc;
1523 int len;
1524 const char *arg = NULL;
1525 char *parptr;
1526 int dir = MODE_QUERY;
1527
1528 /* bail out if we have nothing to do... */
1529 if (!mode_count)
1530 return;
1531
1532 if (IsServer(source_p))
1533 mbl = ircsprintf(modebuf, ":%s MODE %s ", (IsHidden(source_p) ||
1534 ConfigServerHide.hide_servers) ?
1535 me.name : source_p->name, chname);
1536 else
1537 mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
1538 source_p->username, source_p->host, chname);
1539
1540 mc = 0;
1541 nc = 0;
1542 pbl = 0;
1543
1544 parabuf[0] = '\0';
1545 parptr = parabuf;
1546
1547 for (i = 0; i < mode_count; i++)
1548 {
1549 if (mode_changes[i].letter == 0 ||
1550 mode_changes[i].mems == NON_CHANOPS ||
1551 mode_changes[i].mems == ONLY_SERVERS)
1552 continue;
1553
1554 arg = mode_changes[i].arg;
1555 if (arg != NULL)
1556 arglen = strlen(arg);
1557 else
1558 arglen = 0;
1559
1560 if ((mc == MAXMODEPARAMS) ||
1561 ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
1562 ((arglen + pbl + BAN_FUDGE) >= MODEBUFLEN))
1563 {
1564 if (mbl && modebuf[mbl - 1] == '-')
1565 modebuf[mbl - 1] = '\0';
1566
1567 if (nc != 0)
1568 sendto_channel_local(ALL_MEMBERS, NO, chptr, "%s %s", modebuf, parabuf);
1569
1570 nc = 0;
1571 mc = 0;
1572
1573 if (IsServer(source_p))
1574 mbl = ircsprintf(modebuf, ":%s MODE %s ", me.name, chname);
1575 else
1576 mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
1577 source_p->username, source_p->host, chname);
1578
1579 pbl = 0;
1580 parabuf[0] = '\0';
1581 parptr = parabuf;
1582 dir = MODE_QUERY;
1583 }
1584
1585 if (dir != mode_changes[i].dir)
1586 {
1587 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
1588 dir = mode_changes[i].dir;
1589 }
1590
1591 modebuf[mbl++] = mode_changes[i].letter;
1592 modebuf[mbl] = '\0';
1593 nc++;
1594
1595 if (arg != NULL)
1596 {
1597 len = ircsprintf(parptr, "%s ", arg);
1598 pbl += len;
1599 parptr += len;
1600 mc++;
1601 }
1602 }
1603
1604 if (pbl && parabuf[pbl - 1] == ' ')
1605 parabuf[pbl - 1] = 0;
1606
1607 if (nc != 0)
1608 sendto_channel_local(ALL_MEMBERS, NO, chptr, "%s %s", modebuf, parabuf);
1609
1610 nc = 0;
1611 mc = 0;
1612
1613 /* Now send to servers... */
1614 for (i = 0; i < NCHCAP_COMBOS; i++)
1615 if (chcap_combos[i].count != 0)
1616 send_cap_mode_changes(client_p, source_p, chptr,
1617 chcap_combos[i].cap_yes,
1618 chcap_combos[i].cap_no);
1619 }
1620
1621 /* void set_channel_mode(struct Client *client_p, struct Client *source_p,
1622 * struct Channel *chptr, int parc, char **parv,
1623 * char *chname)
1624 * Input: The client we received this from, the client this originated
1625 * from, the channel, the parameter count starting at the modes,
1626 * the parameters, the channel name.
1627 * Output: None.
1628 * Side-effects: Changes the channel membership and modes appropriately,
1629 * sends the appropriate MODE messages to the appropriate
1630 * clients.
1631 */
1632 void
1633 set_channel_mode(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
1634 struct Membership *member, int parc, char *parv[], char *chname)
1635 {
1636 int dir = MODE_ADD;
1637 int parn = 1;
1638 int alevel, errors = 0;
1639 char *ml = parv[0], c;
1640 int table_position;
1641
1642 mode_count = 0;
1643 mode_limit = 0;
1644 simple_modes_mask = 0;
1645
1646 alevel = get_channel_access(source_p, member);
1647
1648 for (; (c = *ml) != '\0'; ml++)
1649 {
1650 #if 0
1651 if(mode_count > 20)
1652 break;
1653 #endif
1654 switch (c)
1655 {
1656 case '+':
1657 dir = MODE_ADD;
1658 break;
1659 case '-':
1660 dir = MODE_DEL;
1661 break;
1662 case '=':
1663 dir = MODE_QUERY;
1664 break;
1665 default:
1666 if (c < 'A' || c > 'z')
1667 table_position = 0;
1668 else
1669 table_position = c - 'A' + 1;
1670 ModeTable[table_position].func(client_p, source_p, chptr,
1671 parc, &parn,
1672 parv, &errors, alevel, dir, c,
1673 ModeTable[table_position].d,
1674 chname);
1675 break;
1676 }
1677 }
1678
1679 send_mode_changes(client_p, source_p, chptr, chname);
1680 }

Properties

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