ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/src/cmode.C
Revision: 1.6
Committed: Wed Sep 5 11:23:15 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.5: +13 -12 lines
Log Message:
removed GPLed code and put license back to BSD

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