ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/cmode.C
Revision: 1.8
Committed: Sun Sep 16 18:54:44 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +7 -2 lines
Log Message:
#defines to enum

File Contents

# Content
1 /*
2 * cmode.C: Channel mode change tracking.
3 *
4 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5 * Rights to this code are as documented in COPYING.
6 *
7 *
8 * Portions of this file were derived from sources bearing the following license:
9 * Rights to this code are documented in doc/pod/license.pod.
10 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 */
12
13 static char const rcsid[] = "$Id: cmode.C,v 1.7 2007-09-09 20:05:52 pippijn Exp $";
14
15 #include "atheme.h"
16 #include "servers.h"
17 #include <account/chanacs.h>
18 #include <account/mychan.h>
19
20 /* convert mode flags to a text mode string */
21 char *
22 flags_to_string (int flags)
23 {
24 static char buf[32];
25 char *s = buf;
26 int i;
27
28 for (i = 0; mode_list[i].mode != 0; i++)
29 if (flags & mode_list[i].value)
30 *s++ = mode_list[i].mode;
31
32 *s = 0;
33
34 return buf;
35 }
36
37 /* convert a mode character to a flag. */
38 int
39 mode_to_flag (char c)
40 {
41 int i;
42
43 for (i = 0; mode_list[i].mode != 0 && mode_list[i].mode != c; i++);
44
45 return mode_list[i].value;
46 }
47
48 /* yeah, this should be fun. */
49 /* If source == NULL, apply a mode change from outside to our structures
50 * If source != NULL, apply the mode change and send it out from that user
51 */
52 void
53 channel_mode (user_t *source, channel_t *chan, int parc, char const * const parv[])
54 {
55 bool matched = false;
56 bool simple_modes_changed = false;
57 int i, parpos = 0;
58 mtype whatt = MTYPE_NUL;
59 unsigned int newlimit;
60 char const *pos = parv[0];
61 mychan_t *mc;
62 chanuser_t *cu = NULL;
63 user_t *first_deopped_service = NULL;
64 node_t *n;
65
66 if ((!pos) || (*pos == '\0'))
67 return;
68
69 if (!chan)
70 return;
71
72 /* SJOIN modes of 0 means no change */
73 if (*pos == '0')
74 return;
75
76 for (; *pos != '\0'; pos++)
77 {
78 matched = false;
79
80 if (*pos == '+')
81 {
82 whatt = MTYPE_ADD;
83 continue;
84 }
85 if (*pos == '-')
86 {
87 whatt = MTYPE_DEL;
88 continue;
89 }
90
91 for (i = 0; mode_list[i].mode != '\0'; i++)
92 {
93 if (*pos == mode_list[i].mode)
94 {
95 matched = true;
96
97 if (whatt == MTYPE_ADD)
98 {
99 if (!(chan->modes & mode_list[i].value))
100 simple_modes_changed = true;
101 chan->modes |= mode_list[i].value;
102 }
103 else
104 {
105 if (chan->modes & mode_list[i].value)
106 simple_modes_changed = true;
107 chan->modes &= ~mode_list[i].value;
108 }
109
110 if (source)
111 modestack_mode_simple (source->nick, chan, whatt, mode_list[i].value);
112
113 break;
114 }
115 }
116 if (matched)
117 continue;
118
119 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
120 {
121 if (*pos == ignore_mode_list[i].mode)
122 {
123 matched = true;
124 if (whatt == MTYPE_ADD)
125 {
126 if (++parpos >= parc)
127 break;
128 if (source && !ignore_mode_list[i].check (parv[parpos], chan, NULL, NULL, NULL))
129 break;
130 if (chan->extmodes[i])
131 {
132 if (strcmp (chan->extmodes[i], parv[parpos]))
133 simple_modes_changed = true;
134 sfree (chan->extmodes[i]);
135 }
136 else
137 simple_modes_changed = true;
138 chan->extmodes[i] = sstrdup (parv[parpos]);
139 if (source)
140 modestack_mode_ext (source->nick, chan, MTYPE_ADD, i, chan->extmodes[i]);
141 }
142 else
143 {
144 if (chan->extmodes[i])
145 {
146 simple_modes_changed = true;
147 sfree (chan->extmodes[i]);
148 chan->extmodes[i] = NULL;
149 }
150 if (source)
151 modestack_mode_ext (source->nick, chan, MTYPE_DEL, i, NULL);
152 }
153 break;
154 }
155 }
156 if (matched)
157 continue;
158
159 if (*pos == 'l')
160 {
161 if (whatt == MTYPE_ADD)
162 {
163 if (++parpos >= parc)
164 continue;
165 chan->modes |= CMODE_LIMIT;
166 newlimit = atoi (parv[parpos]);
167 if (chan->limit != newlimit)
168 simple_modes_changed = true;
169 chan->limit = newlimit;
170 if (source)
171 modestack_mode_limit (source->nick, chan, MTYPE_ADD, chan->limit);
172 }
173 else
174 {
175 chan->modes &= ~CMODE_LIMIT;
176 if (chan->limit != 0)
177 simple_modes_changed = true;
178 chan->limit = 0;
179 if (source)
180 modestack_mode_limit (source->nick, chan, MTYPE_DEL, 0);
181 }
182 continue;
183 }
184
185 if (*pos == 'k')
186 {
187 if (whatt == MTYPE_ADD)
188 {
189 if (++parpos >= parc)
190 continue;
191 chan->modes |= CMODE_KEY;
192 if (chan->key)
193 {
194 if (strcmp (chan->key, parv[parpos]))
195 simple_modes_changed = true;
196 sfree (chan->key);
197 }
198 else
199 simple_modes_changed = true;
200 chan->key = sstrdup (parv[parpos]);
201 if (source)
202 modestack_mode_param (source->nick, chan, MTYPE_ADD, 'k', chan->key);
203 }
204 else
205 {
206 if (chan->key)
207 simple_modes_changed = true;
208 chan->modes &= ~CMODE_KEY;
209 if (source)
210 modestack_mode_param (source->nick, chan, MTYPE_DEL, 'k', chan->key ? chan->key : "*");
211 sfree (chan->key);
212 chan->key = NULL;
213 /* ratbox typically sends either the key or a `*' on -k, so you
214 * should eat a parameter
215 */
216 parpos++;
217 }
218 continue;
219 }
220
221 if (strchr (ircd->ban_like_modes, *pos))
222 {
223 if (++parpos >= parc)
224 continue;
225 if (whatt == MTYPE_ADD)
226 {
227 chanban_add (chan, parv[parpos], *pos);
228 if (source)
229 modestack_mode_param (source->nick, chan, MTYPE_ADD, *pos, parv[parpos]);
230 }
231 else
232 {
233 chanban_t *c;
234
235 c = chanban_find (chan, parv[parpos], *pos);
236 chanban_delete (c);
237 if (source)
238 modestack_mode_param (source->nick, chan, MTYPE_DEL, *pos, parv[parpos]);
239 }
240 continue;
241 }
242
243 for (i = 0; status_mode_list[i].mode != '\0'; i++)
244 {
245 if (*pos == status_mode_list[i].mode)
246 {
247 if (++parpos >= parc)
248 break;
249 cu = chanuser_find (chan, source ? user_find_named (parv[parpos]) : user_find (parv[parpos]));
250
251 if (cu == NULL)
252 {
253 slog (LG_ERROR, "channel_mode(): MODE %s %c%c %s", chan->name, (whatt == MTYPE_ADD) ? '+' : '-', status_mode_list[i].mode, parv[parpos]);
254 break;
255 }
256
257 matched = true;
258
259 if (whatt == MTYPE_ADD)
260 {
261 cu->modes |= status_mode_list[i].value;
262
263 if (source)
264 modestack_mode_param (source->nick, chan, MTYPE_ADD, *pos, CLIENT_NAME (cu->user));
265
266 /* see if they did something we have to undo */
267 if (source == NULL && cu->user->server != me.me && chansvs.me != NULL && (mc = mychan_t::find (chan->name)) && mc->flags & MC_SECURE)
268 {
269 if (status_mode_list[i].mode == 'o' && !(chanacs_user_flags (mc, cu->user) & (CA_OP | CA_AUTOOP)))
270 {
271 /* they were opped and aren't on the list, deop them */
272 modestack_mode_param (chansvs.nick, chan, MTYPE_DEL, 'o', CLIENT_NAME (cu->user));
273 cu->modes &= ~status_mode_list[i].value;
274 }
275 else if (ircd->uses_halfops && status_mode_list[i].mode == ircd->halfops_mchar[1] && !(chanacs_user_flags (mc, cu->user) & (CA_HALFOP | CA_AUTOHALFOP)))
276 {
277 /* same for halfops -- jilles */
278 modestack_mode_param (chansvs.nick, chan, MTYPE_DEL, ircd->halfops_mchar[1], CLIENT_NAME (cu->user));
279 cu->modes &= ~status_mode_list[i].value;
280 }
281 }
282 }
283 else
284 {
285 if (cu->user->server == me.me && status_mode_list[i].value == CMODE_OP)
286 {
287 if (source == NULL)
288 {
289 if (first_deopped_service == NULL)
290 {
291 if (chan->nummembers > 1)
292 {
293 slog (LG_DEBUG, "channel_mode(): %s deopped on %s, rejoining", cu->user->nick, chan->name);
294 phandler->part_sts (chan, cu->user);
295 phandler->join_sts (chan, cu->user, false, channel_modes (chan, true));
296 }
297 else
298 {
299 slog (LG_DEBUG, "channel_mode(): %s deopped on %s, opping from other service", cu->user->nick, chan->name);
300 LIST_FOREACH (n, me.me->userlist.head)
301 {
302 if (n->data != cu->user)
303 {
304 modestack_mode_param (((user_t *) n->data)->nick, chan, MTYPE_ADD, *pos, CLIENT_NAME (cu->user));
305 break;
306 }
307 }
308 }
309 first_deopped_service = cu->user;
310 }
311 else if (first_deopped_service != cu->user)
312 {
313 slog (LG_DEBUG, "channel_mode(): %s deopped on %s, opping from %s", cu->user->nick, chan->name, first_deopped_service->nick);
314 modestack_mode_param (first_deopped_service->nick, chan, MTYPE_ADD, *pos, CLIENT_NAME (cu->user));
315 }
316
317 }
318
319 continue;
320 }
321
322 if (source)
323 modestack_mode_param (source->nick, chan, MTYPE_DEL, *pos, CLIENT_NAME (cu->user));
324
325 cu->modes &= ~status_mode_list[i].value;
326 }
327
328 break;
329 }
330 }
331 if (matched)
332 continue;
333
334 slog (LG_DEBUG, "channel_mode(): mode %c not matched", *pos);
335 }
336
337 if (source == NULL && chansvs.me != NULL)
338 {
339 mc = mychan_t::find (chan->name);
340 if (mc != NULL && (simple_modes_changed || (mc->flags & MC_MLOCK_CHECK)))
341 check_modes (mc, true);
342 }
343 }
344
345 void
346 channel_mode (user_t *source, channel_t *chan, char const * const arg)
347 {
348 char const * const parv[] = { arg };
349 channel_mode (source, chan, 1, parv);
350 }
351
352 /* like channel_mode() but parv array passed as varargs */
353 void
354 channel_mode_va (user_t *source, channel_t *chan, int parc, char const * const parv0, ...)
355 {
356 char const *parv[255];
357 int i;
358 va_list va;
359
360 if (parc == 0)
361 return;
362 #if 0
363 if (parc > sizeof parv / sizeof *parv)
364 {
365 slog (LG_DEBUG, "channel_mode_va(): parc too big (%d), truncating", parc);
366 parc = sizeof parv / sizeof *parv;
367 }
368 #endif
369 parv[0] = parv0;
370 va_start (va, parv0);
371 for (i = 1; i < parc; i++)
372 parv[i] = va_arg (va, char *);
373 va_end (va);
374 channel_mode (source, chan, parc, parv);
375 }
376
377 static struct modestackdata
378 {
379 char source[HOSTLEN]; /* name */
380 channel_t *channel;
381 int modes_on;
382 int modes_off;
383 unsigned int limit;
384 char extmodes[MAXEXTMODES][512];
385 bool limitused, extmodesused[MAXEXTMODES];
386 char pmodes[2 * MAXMODES + 2];
387 char params[512]; /* includes leading space */
388 int totalparamslen; /* includes leading space */
389 int totallen;
390 int paramcount;
391
392 unsigned int event;
393 } modestackdata;
394
395 static void modestack_calclen (struct modestackdata *md);
396
397 static void
398 modestack_debugprint (struct modestackdata *md)
399 {
400 int i;
401
402 slog (LG_DEBUG, "modestack_debugprint(): %s MODE %s", md->source, md->channel->name);
403 slog (LG_DEBUG, "simple %x/%x", md->modes_on, md->modes_off);
404 if (md->limitused)
405 slog (LG_DEBUG, "limit %u", (unsigned) md->limit);
406 for (i = 0; i < MAXEXTMODES; i++)
407 if (md->extmodesused[i])
408 slog (LG_DEBUG, "ext %d %s", i, md->extmodes[i]);
409 slog (LG_DEBUG, "pmodes %s%s", md->pmodes, md->params);
410 modestack_calclen (md);
411 slog (LG_DEBUG, "totallen %d/%d", md->totalparamslen, md->totallen);
412 }
413
414 /* calculates the length fields */
415 static void
416 modestack_calclen (struct modestackdata *md)
417 {
418 int i;
419 char const *p;
420
421 md->totallen = strlen (md->source) + USERLEN + HOSTLEN + 1 + 4 + 1 + 10 + strlen (md->channel->name) + 1;
422 md->totallen += 2 + 32 + strlen (md->pmodes);
423 md->totalparamslen = 0;
424 md->paramcount = (md->limitused != 0);
425 if (md->limitused && md->limit != 0)
426 md->totalparamslen += 11;
427 for (i = 0; i < MAXEXTMODES; i++)
428 if (md->extmodesused[i])
429 {
430 md->paramcount++;
431 if (*md->extmodes[i] != '\0')
432 md->totalparamslen += 1 + strlen (md->extmodes[i]);
433 }
434 md->totalparamslen += strlen (md->params);
435 p = md->params;
436 while (*p != '\0')
437 if (*p++ == ' ')
438 md->paramcount++;
439 md->totallen += md->totalparamslen;
440 }
441
442 /* clears the data */
443 static void
444 modestack_clear (struct modestackdata *md)
445 {
446 int i;
447
448 md->modes_on = 0;
449 md->modes_off = 0;
450 md->limitused = 0;
451 for (i = 0; i < MAXEXTMODES; i++)
452 md->extmodesused[i] = 0, *md->extmodes[i] = '\0';
453 md->pmodes[0] = '\0';
454 md->params[0] = '\0';
455 md->totallen = 0;
456 md->totalparamslen = 0;
457 md->paramcount = 0;
458 }
459
460 /* sends a MODE and clears the data */
461 static void
462 modestack_flush (struct modestackdata *md)
463 {
464 char buf[512];
465 char *end, *p;
466 mtype dir = MTYPE_NUL;
467 int i;
468
469 p = buf;
470 end = buf + sizeof buf;
471
472 /* first do the mode letters */
473 if (md->modes_off)
474 {
475 if (dir != MTYPE_DEL)
476 dir = MTYPE_DEL, *p++ = '-';
477 strlcpy (p, flags_to_string (md->modes_off), end - p);
478 p += strlen (p);
479 }
480 if (md->limitused && md->limit == 0)
481 {
482 if (dir != MTYPE_DEL)
483 dir = MTYPE_DEL, *p++ = '-';
484 *p++ = 'l';
485 }
486 for (i = 0; i < MAXEXTMODES; i++)
487 {
488 if (md->extmodesused[i] && *md->extmodes[i] == '\0')
489 {
490 if (dir != MTYPE_DEL)
491 dir = MTYPE_DEL, *p++ = '-';
492 *p++ = ignore_mode_list[i].mode;
493 }
494 }
495 if (md->modes_on)
496 {
497 if (dir != MTYPE_ADD)
498 dir = MTYPE_ADD, *p++ = '+';
499 strlcpy (p, flags_to_string (md->modes_on), end - p);
500 p += strlen (p);
501 }
502 if (md->limitused && md->limit != 0)
503 {
504 if (dir != MTYPE_ADD)
505 dir = MTYPE_ADD, *p++ = '+';
506 *p++ = 'l';
507 }
508 for (i = 0; i < MAXEXTMODES; i++)
509 {
510 if (md->extmodesused[i] && *md->extmodes[i] != '\0')
511 {
512 if (dir != MTYPE_ADD)
513 dir = MTYPE_ADD, *p++ = '+';
514 *p++ = ignore_mode_list[i].mode;
515 }
516 }
517 strlcpy (p, md->pmodes + ((dir == MTYPE_ADD && *md->pmodes == '+') || (dir == MTYPE_DEL && *md->pmodes == '-') ? 1 : 0), end - p);
518 p += strlen (p);
519
520 /* all mode letters done, now for some checks */
521 if (p == buf)
522 {
523 /*slog(LG_DEBUG, "modestack_flush(): nothing to do"); */
524 return;
525 }
526 if (p + md->totalparamslen >= end)
527 {
528 slog (LG_ERROR, "modestack_flush() overflow: %s", buf);
529 modestack_debugprint (md);
530 modestack_clear (md);
531 return;
532 }
533
534 /* now the parameters, in the same order */
535 if (md->limitused && md->limit != 0)
536 {
537 snprintf (p, end - p, " %u", (unsigned) md->limit);
538 p += strlen (p);
539 }
540 for (i = 0; i < MAXEXTMODES; i++)
541 {
542 if (md->extmodesused[i] && *md->extmodes[i] != '\0')
543 {
544 snprintf (p, end - p, " %s", md->extmodes[i]);
545 p += strlen (p);
546 }
547 }
548 if (*md->params)
549 {
550 strlcpy (p, md->params, end - p);
551 p += strlen (p);
552 }
553 phandler->mode_sts (md->source, md->channel, buf);
554 modestack_clear (md);
555 }
556
557 static struct modestackdata *
558 modestack_init (char *source, channel_t *channel)
559 {
560 if (irccasecmp (source, modestackdata.source) || channel != modestackdata.channel)
561 {
562 /*slog(LG_DEBUG, "modestack_init(): new source/channel, flushing"); */
563 modestack_flush (&modestackdata);
564 }
565 strlcpy (modestackdata.source, source, sizeof modestackdata.source);
566 modestackdata.channel = channel;
567 return &modestackdata;
568 }
569
570 static void
571 modestack_add_simple (struct modestackdata *md, mtype dir, int flags)
572 {
573 if (dir == MTYPE_ADD)
574 md->modes_on |= flags, md->modes_off &= ~flags;
575 else if (dir == MTYPE_DEL)
576 md->modes_off |= flags, md->modes_on &= ~flags;
577 else
578 slog (LG_ERROR, "modestack_add_simple(): invalid direction");
579 }
580
581 static void
582 modestack_add_limit (struct modestackdata *md, mtype dir, unsigned int limit)
583 {
584 md->limitused = 0;
585 modestack_calclen (md);
586 if (md->paramcount >= MAXMODES)
587 modestack_flush (md);
588 if (dir == MTYPE_ADD)
589 {
590 if (md->totallen + 11 > 512)
591 modestack_flush (md);
592 md->limit = limit;
593 }
594 else if (dir == MTYPE_DEL)
595 md->limit = 0;
596 else
597 slog (LG_ERROR, "modestack_add_limit(): invalid direction");
598 md->limitused = 1;
599 }
600
601 static void
602 modestack_add_ext (struct modestackdata *md, mtype dir, int i, char const * const value)
603 {
604 md->extmodesused[i] = 0;
605 modestack_calclen (md);
606 if (md->paramcount >= MAXMODES)
607 modestack_flush (md);
608 if (dir == MTYPE_ADD)
609 {
610 if (md->totallen + 1 + strlen (value) > 512)
611 modestack_flush (md);
612 strlcpy (md->extmodes[i], value, sizeof md->extmodes[i]);
613 }
614 else if (dir == MTYPE_DEL)
615 md->extmodes[i][0] = '\0';
616 else
617 slog (LG_ERROR, "modestack_add_ext(): invalid direction");
618 md->extmodesused[i] = 1;
619 }
620
621 static void
622 modestack_add_param (struct modestackdata *md, mtype dir, char type, char const * const value)
623 {
624 char *p;
625 int n = 0, i;
626 char dir2 = MTYPE_NUL;
627 char str[3];
628
629 p = md->pmodes;
630 while (*p != '\0')
631 {
632 if (*p == '+')
633 dir2 = MTYPE_ADD;
634 else if (*p == '-')
635 dir2 = MTYPE_DEL;
636 else
637 n++;
638 p++;
639 }
640 n += (md->limitused != 0);
641 for (i = 0; i < MAXEXTMODES; i++)
642 n += (md->extmodesused[i] != 0);
643 modestack_calclen (md);
644 if (n >= MAXMODES || md->totallen + (dir != dir2) + 2 + strlen (value) > 512 || (type == 'k' && strchr (md->pmodes, 'k')))
645 {
646 modestack_flush (md);
647 dir2 = MTYPE_NUL;
648 }
649 if (dir != dir2)
650 {
651 str[0] = dir == MTYPE_ADD ? '+' : '-';
652 str[1] = type;
653 str[2] = '\0';
654 }
655 else
656 {
657 str[0] = type;
658 str[1] = '\0';
659 }
660 strlcat (md->pmodes, str, sizeof md->pmodes);
661 strlcat (md->params, " ", sizeof md->params);
662 strlcat (md->params, value, sizeof md->params);
663 }
664
665 static void
666 modestack_flush_callback (void *arg)
667 {
668 modestack_flush ((struct modestackdata *) arg);
669 ((struct modestackdata *) arg)->event = 0;
670 }
671
672 /* flush pending modes for a certain channel */
673 void
674 modestack_flush_channel (channel_t *channel)
675 {
676 if (channel == NULL || channel == modestackdata.channel)
677 modestack_flush (&modestackdata);
678 }
679
680 /* forget pending modes for a certain channel */
681 void
682 modestack_forget_channel (channel_t *channel)
683 {
684 if (channel == NULL || channel == modestackdata.channel)
685 modestack_clear (&modestackdata);
686 }
687
688 /* handle a channel that is going to be destroyed */
689 void
690 modestack_finalize_channel (channel_t *channel)
691 {
692 user_t *u;
693
694 if (channel == modestackdata.channel)
695 {
696 if (modestackdata.modes_off & ircd->perm_mode)
697 {
698 /* A mode change is not a good way to destroy a channel */
699 slog (LG_DEBUG, "modestack_finalize_channel(): flushing modes for %s to clear perm mode", channel->name);
700 u = user_find_named (modestackdata.source);
701 if (u != NULL)
702 phandler->join_sts (channel, u, false, channel_modes (channel, true));
703 modestack_flush (&modestackdata);
704 if (u != NULL)
705 phandler->part_sts (channel, u);
706 }
707 else
708 modestack_clear (&modestackdata);
709 }
710 }
711
712 /* stack simple modes without parameters */
713 void
714 modestack_mode_simple (char *source, channel_t *channel, mtype dir, int flags)
715 {
716 struct modestackdata *md;
717
718 if (flags == 0)
719 return;
720 md = modestack_init (source, channel);
721 modestack_add_simple (md, dir, flags);
722 if (!md->event)
723 md->event = event_add_once ("flush_cmode_callback", modestack_flush_callback, md, 0);
724 }
725
726 /* stack a limit */
727 void
728 modestack_mode_limit (char *source, channel_t *channel, mtype dir, unsigned int limit)
729 {
730 struct modestackdata *md;
731
732 md = modestack_init (source, channel);
733 modestack_add_limit (md, dir, limit);
734 if (!md->event)
735 md->event = event_add_once ("flush_cmode_callback", modestack_flush_callback, md, 0);
736 }
737
738 /* stack a non-standard type C mode */
739 void
740 modestack_mode_ext (char *source, channel_t *channel, mtype dir, int i, char const * const value)
741 {
742 struct modestackdata *md;
743
744 md = modestack_init (source, channel);
745 if (i < 0 || i >= MAXEXTMODES)
746 {
747 slog (LG_ERROR, "modestack_mode_ext(): i=%d out of range (value=\"%s\")", i, value);
748 return;
749 }
750 modestack_add_ext (md, dir, i, value);
751 if (!md->event)
752 md->event = event_add_once ("flush_cmode_callback", modestack_flush_callback, md, 0);
753 }
754
755 /* stack a type A, B or E mode */
756 void
757 modestack_mode_param (char *source, channel_t *channel, mtype dir, char type, char const * const value)
758 {
759 struct modestackdata *md;
760
761 md = modestack_init (source, channel);
762 modestack_add_param (md, dir, type, value);
763 if (!md->event)
764 md->event = event_add_once ("flush_cmode_callback", modestack_flush_callback, md, 0);
765 }
766
767 /* Clear all simple modes (+imnpstkl etc) on a channel */
768 void
769 clear_simple_modes (channel_t *c)
770 {
771 int i;
772
773 if (c == NULL)
774 return;
775 c->modes = 0;
776 c->limit = 0;
777 if (c->key != NULL)
778 sfree (c->key);
779 c->key = NULL;
780 for (i = 0; i < MAXEXTMODES; i++)
781 if (c->extmodes[i] != NULL)
782 {
783 sfree (c->extmodes[i]);
784 c->extmodes[i] = NULL;
785 }
786 }
787
788 char *
789 channel_modes (channel_t *c, bool doparams)
790 {
791 static char fullmode[512];
792 char params[512];
793 int i;
794 char *p;
795 char *q;
796
797 if (c == NULL)
798 return NULL;
799
800 p = fullmode;
801 q = params;
802 *p++ = '+';
803 *q = '\0';
804 for (i = 0; mode_list[i].mode != '\0'; i++)
805 {
806 if (c->modes & mode_list[i].value)
807 *p++ = mode_list[i].mode;
808 }
809 if (c->limit)
810 {
811 *p++ = 'l';
812 if (doparams)
813 {
814 snprintf (q, params + sizeof params - q, " %d", c->limit);
815 q += strlen (q);
816 }
817 }
818 if (c->key)
819 {
820 *p++ = 'k';
821 if (doparams)
822 {
823 *q++ = ' ';
824 strlcpy (q, c->key, params + sizeof params - q);
825 q += strlen (q);
826 }
827 }
828 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
829 {
830 if (c->extmodes[i] != NULL)
831 {
832 *p++ = ignore_mode_list[i].mode;
833 if (doparams)
834 {
835 *q++ = ' ';
836 strlcpy (q, c->extmodes[i], params + sizeof params - q);
837 q += strlen (q);
838 }
839 }
840 }
841 strlcpy (p, params, fullmode + sizeof fullmode - p);
842 return fullmode;
843 }
844
845 void
846 check_modes (mychan_t *mychan, bool sendnow)
847 {
848 int modes;
849 int i;
850 metadata *md;
851 char *p, *q;
852 char str2[512];
853
854 if (!mychan || !mychan->chan)
855 return;
856 mychan->flags &= ~MC_MLOCK_CHECK;
857
858 /* check what's locked on */
859 modes = ~mychan->chan->modes & mychan->mlock_on;
860 modes &= ~(CMODE_KEY | CMODE_LIMIT);
861 if (sendnow)
862 modestack_mode_simple (chansvs.nick, mychan->chan, MTYPE_ADD, modes);
863 mychan->chan->modes |= modes;
864
865 if (mychan->mlock_limit && mychan->mlock_limit != mychan->chan->limit)
866 {
867 mychan->chan->limit = mychan->mlock_limit;
868 if (sendnow)
869 modestack_mode_limit (chansvs.nick, mychan->chan, MTYPE_ADD, mychan->mlock_limit);
870 }
871
872 if (mychan->mlock_key)
873 {
874 if (mychan->chan->key && strcmp (mychan->chan->key, mychan->mlock_key))
875 {
876 /* some ircds still need this... :\ -- jilles */
877 if (sendnow)
878 modestack_mode_param (chansvs.nick, mychan->chan, MTYPE_DEL, 'k', mychan->chan->key);
879 sfree (mychan->chan->key);
880 mychan->chan->key = NULL;
881 }
882
883 if (mychan->chan->key == NULL)
884 {
885 mychan->chan->key = sstrdup (mychan->mlock_key);
886 if (sendnow)
887 modestack_mode_param (chansvs.nick, mychan->chan, MTYPE_ADD, 'k', mychan->mlock_key);
888 }
889 }
890
891 /* check what's locked off */
892 modes = mychan->chan->modes & mychan->mlock_off;
893 modes &= ~(CMODE_KEY | CMODE_LIMIT);
894 if (sendnow)
895 modestack_mode_simple (chansvs.nick, mychan->chan, MTYPE_DEL, modes);
896 mychan->chan->modes &= ~modes;
897
898 if (mychan->chan->limit && (mychan->mlock_off & CMODE_LIMIT))
899 {
900 if (sendnow)
901 modestack_mode_limit (chansvs.nick, mychan->chan, MTYPE_DEL, 0);
902 mychan->chan->limit = 0;
903 }
904
905 if (mychan->chan->key && (mychan->mlock_off & CMODE_KEY))
906 {
907 if (sendnow)
908 modestack_mode_param (chansvs.nick, mychan->chan, MTYPE_DEL, 'k', mychan->chan->key);
909 sfree (mychan->chan->key);
910 mychan->chan->key = NULL;
911 }
912
913 /* non-standard type C modes separately */
914 md = mychan->find_metadata ("private:mlockext");
915 if (md != NULL)
916 {
917 p = md->value;
918 while (*p != '\0')
919 {
920 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
921 {
922 if (ignore_mode_list[i].mode == *p)
923 {
924 if ((p[1] == ' ' || p[1] == '\0') && mychan->chan->extmodes[i] != NULL)
925 {
926 sfree (mychan->chan->extmodes[i]);
927 mychan->chan->extmodes[i] = NULL;
928 if (sendnow)
929 modestack_mode_ext (chansvs.nick, mychan->chan, MTYPE_DEL, i, NULL);
930 }
931 else if (p[1] != ' ' && p[1] != '\0')
932 {
933 strlcpy (str2, p + 1, sizeof str2);
934 q = strchr (str2, ' ');
935 if (q != NULL)
936 *q = '\0';
937 if ((mychan->chan->extmodes[i] == NULL || strcmp (mychan->chan->extmodes[i], str2)) && ignore_mode_list[i].check (str2, mychan->chan, mychan, NULL, NULL))
938 {
939 if (mychan->chan->extmodes[i] != NULL)
940 sfree (mychan->chan->extmodes[i]);
941 mychan->chan->extmodes[i] = sstrdup (str2);
942 if (sendnow)
943 modestack_mode_ext (chansvs.nick, mychan->chan, MTYPE_ADD, i, mychan->chan->extmodes[i]);
944 }
945 }
946 }
947 }
948 while (*p != ' ' && *p != '\0')
949 p++;
950 while (*p == ' ')
951 p++;
952 }
953 }
954
955 }
956
957 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
958 * vim:ts=8
959 * vim:sw=8
960 * vim:noexpandtab
961 */