ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/cmode.C
Revision: 1.3
Committed: Sat Jul 21 13:23:21 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.2: +1 -1 lines
Log Message:
- added rcsid to some files
- more documentation tweaks
- made most protocol commands local to phandler.C
- added ircd metadata (inspircd only for now)
- added inspircd swhois support

File Contents

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