ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/actions.c
Revision: 1171
Committed: Fri Aug 12 20:00:46 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 24354 byte(s)
Log Message:
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

File Contents

# Content
1 /* Various routines to perform simple actions.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "language.h"
12 #include "modules.h"
13 #include "timeout.h"
14
15 /*************************************************************************/
16
17 static int cb_clear_channel = -1;
18 static int cb_set_topic = -1;
19
20 /* Sender to be used with clear_channel() (empty string: use server name) */
21 static char clear_channel_sender[NICKMAX] = {0};
22
23 /*************************************************************************/
24 /*************************************************************************/
25
26 int actions_init(int ac, char **av)
27 {
28 cb_clear_channel = register_callback("clear channel");
29 cb_set_topic = register_callback("set topic");
30 if (cb_clear_channel < 0 || cb_set_topic < 0) {
31 log("actions_init: register_callback() failed\n");
32 return 0;
33 }
34 return 1;
35 }
36
37 /*************************************************************************/
38
39 void actions_cleanup(void)
40 {
41 unregister_callback(cb_set_topic);
42 unregister_callback(cb_clear_channel);
43 }
44
45 /*************************************************************************/
46 /*************************************************************************/
47
48 /* Note a bad password attempt for the given user from the given service.
49 * If they've used up their limit, toss them off. `service' is used only
50 * for the sender of the bad-password and warning messages; these messages
51 * are not sent if `service' is NULL. `what' describes what the password
52 * was for, and is used in the kill message if the user is killed. The
53 * function's return value is 1 if the user was warned, 2 if the user was
54 * killed, and 0 otherwise.
55 */
56
57 int bad_password(const char *service, User *u, const char *what)
58 {
59 time_t now = time(NULL);
60
61 if (service)
62 notice_lang(service, u, PASSWORD_INCORRECT);
63
64 if (!BadPassLimit)
65 return 0;
66
67 if (BadPassTimeout > 0 && u->bad_pw_time > 0
68 && now >= u->bad_pw_time + BadPassTimeout)
69 u->bad_pw_count = 0;
70 u->bad_pw_count++;
71 u->bad_pw_time = now;
72 if (u->bad_pw_count >= BadPassLimit) {
73 char buf[BUFSIZE];
74 snprintf(buf, sizeof(buf), "Too many invalid passwords (%s)", what);
75 kill_user(NULL, u->nick, buf);
76 return 2;
77 } else if (u->bad_pw_count == BadPassLimit-1) {
78 if (service)
79 notice_lang(service, u, PASSWORD_WARNING);
80 return 1;
81 }
82 return 0;
83 }
84
85 /*************************************************************************/
86
87 /* Clear modes/users from a channel. The "what" parameter is one or more
88 * of the CLEAR_* constants defined in services.h. "param" is:
89 * - for CLEAR_USERS, a kick message (const char *)
90 * - for CLEAR_UMODES, a bitmask of modes to clear (int32)
91 * - for CLEAR_BANS and CLEAR_EXCEPTS, a User * to match against, or
92 * NULL for all bans/exceptions
93 * Note that CLEAR_EXCEPTS must be handled via callback for protocols which
94 * support it.
95 */
96
97 static void clear_modes(const char *sender, Channel *chan);
98 static void clear_bans(const char *sender, Channel *chan, User *u);
99 static void clear_umodes(const char *sender, Channel *chan, int32 modes);
100 static void clear_users(const char *sender, Channel *chan, const char *reason);
101
102 void clear_channel(Channel *chan, int what, const void *param)
103 {
104 const char *sender =
105 *clear_channel_sender ? clear_channel_sender : ServerName;
106
107 if (call_callback_4(cb_clear_channel, sender, chan, what, param) > 0) {
108 set_cmode(NULL, chan);
109 return;
110 }
111
112 if (what & CLEAR_USERS) {
113 clear_users(sender, chan, (const char *)param);
114 /* Once we kick all the users, nothing else will matter */
115 return;
116 }
117
118 if (what & CLEAR_MODES)
119 clear_modes(sender, chan);
120 if (what & CLEAR_BANS)
121 clear_bans(sender, chan, (User *)param);
122 if (what & CLEAR_UMODES)
123 clear_umodes(sender, chan, (int32)(long)param);
124 set_cmode(NULL, chan); /* Flush modes out */
125 }
126
127 static void clear_modes(const char *sender, Channel *chan)
128 {
129 char buf[BUFSIZE];
130 snprintf(buf, sizeof(buf), "-%s",
131 mode_flags_to_string(chan->mode & ~chanmode_reg, MODE_CHANNEL));
132 set_cmode(sender, chan, buf, chan->key);
133 }
134
135 static void clear_bans(const char *sender, Channel *chan, User *u)
136 {
137 int i, count;
138 char **bans;
139
140 if (!chan->bans_count)
141 return;
142
143 /* Save original ban info */
144 count = chan->bans_count;
145 bans = smalloc(sizeof(char *) * count);
146 memcpy(bans, chan->bans, sizeof(char *) * count);
147
148 for (i = 0; i < count; i++) {
149 if (!u || match_usermask(bans[i], u))
150 set_cmode(sender, chan, "-b", bans[i]);
151 }
152 free(bans);
153 }
154
155 static void clear_umodes(const char *sender, Channel *chan, int32 modes)
156 {
157 struct c_userlist *cu;
158
159 LIST_FOREACH (cu, chan->users) {
160 int32 to_clear = cu->mode & modes; /* modes we need to clear */
161 int32 flag = 1; /* mode we're clearing now */
162 while (to_clear) {
163 if (flag == MODE_INVALID) {
164 log("BUG: hit invalid flag in clear_umodes!"
165 " modes to clear = %08X, user modes = %08X",
166 to_clear, cu->mode);
167 break;
168 }
169 if (to_clear & flag) {
170 char buf[3] = "-x";
171 buf[1] = mode_flag_to_char(flag, MODE_CHANUSER);
172 set_cmode(sender, chan, buf, cu->user->nick);
173 to_clear &= ~flag;
174 }
175 flag <<= 1;
176 }
177 cu->mode &= ~modes;
178 }
179 }
180
181 static void clear_users(const char *sender, Channel *chan, const char *reason)
182 {
183 char *av[3];
184 struct c_userlist *cu, *next;
185
186 /* Prevent anyone from coming back in. The ban will disappear
187 * once everyone's gone. */
188 set_cmode(sender, chan, "+b", "*!*@*");
189 set_cmode(NULL, chan); /* Flush modes out */
190
191 av[0] = chan->name;
192 av[2] = (char *)reason;
193 LIST_FOREACH_SAFE (cu, chan->users, next) {
194 av[1] = cu->user->nick;
195 send_channel_cmd(sender, "KICK %s %s :%s",
196 av[0], av[1], av[2]);
197 do_kick(sender, 3, av);
198 }
199 }
200
201 /*************************************************************************/
202
203 /* Set the nickname to be used to send commands in clear_channel() calls.
204 * If NULL, the server name is used; if PTR_INVALID, the name is not
205 * changed. Returns the old value of the sender, or the empty string if no
206 * nickname was set, in a static buffer.
207 */
208
209 const char *set_clear_channel_sender(const char *newsender)
210 {
211 static char oldsender[NICKMAX];
212 strbcpy(oldsender, clear_channel_sender);
213 if (newsender != PTR_INVALID) {
214 if (newsender) {
215 strbcpy(clear_channel_sender, newsender);
216 } else {
217 *clear_channel_sender = 0;
218 }
219 }
220 return oldsender;
221 }
222
223 /*************************************************************************/
224
225 /* Remove a user from the IRC network. `source' is the nick which should
226 * generate the kill, or NULL for a server-generated kill.
227 */
228
229 void kill_user(const char *source, const char *user, const char *reason)
230 {
231 char *av[2];
232 char buf[BUFSIZE];
233
234 if (!user || !*user)
235 return;
236 if (!source || !*source)
237 source = ServerName;
238 if (!reason)
239 reason = "";
240 snprintf(buf, sizeof(buf), "%s (%s)", source, reason);
241 av[0] = (char *)user;
242 av[1] = buf;
243 send_cmd(source, "KILL %s :%s", user, av[1]);
244 do_kill(source, 2, av);
245 }
246
247 /*************************************************************************/
248
249 /* Set the topic on a channel. `setter' must not be NULL. `source' is the
250 * nick to use to send the TOPIC message; if NULL, the server name is used.
251 */
252
253 void set_topic(const char *source, Channel *c, const char *topic,
254 const char *setter, time_t t)
255 {
256 if (!source)
257 source = ServerName;
258 call_callback_5(cb_set_topic, source, c, topic, setter, t);
259 free(c->topic);
260 if (topic && *topic)
261 c->topic = sstrdup(topic);
262 else
263 c->topic = NULL;
264 strbcpy(c->topic_setter, setter);
265 if (call_callback_5(cb_set_topic, source, c, NULL, NULL, t) > 0)
266 return;
267 }
268
269 /*************************************************************************/
270 /*************************************************************************/
271
272 /* set_cmode(): Set modes for a channel and send those modes to remote
273 * servers. Using this routine eliminates the necessity to modify the
274 * internal Channel structure and send the command out separately, and also
275 * allows the modes for a channel to be collected up over several calls and
276 * sent out in a single command, decreasing network traffic (and scroll).
277 * This function should be called as either:
278 * set_cmode(sender, channel, modes, param1, param2...)
279 * to send one or more channel modes, or
280 * set_cmode(NULL, channel)
281 * to flush buffered modes for a channel (if `channel' is NULL, flushes
282 * buffered modes for all channels).
283 *
284 * NOTE: When setting modes with parameters, all parameters MUST be
285 * strings. Numeric parameters must be converted to strings (with
286 * snprintf() or the like) before being passed.
287 */
288
289 #define MAXMODES 6 /* Maximum number of mode parameters */
290
291 #define MAXPARAMSLEN \
292 (510- NICKMAX - 6 - CHANMAX -(3+31+2*MAXMODES)-MAXMODES)
293 /* |:sender| | MODE | |#channel| | -...+...| { param}*
294 * Note that "-...+..." can contain at most 31 (binary) plus 2*MAXMODES
295 * (MAXMODES modes with parameters plus +/-) characters. */
296
297 static struct modedata {
298 time_t used;
299 Channel *channel;
300 char sender[NICKMAX];
301 int32 binmodes_on;
302 int32 binmodes_off;
303 char opmodes[MAXMODES*2+1];
304 char params[MAXMODES][MAXPARAMSLEN+1];
305 int nopmodes, nparams, paramslen;
306 Timeout *timeout; /* For timely flushing */
307 } modedata[MERGE_CHANMODES_MAX];
308
309 static void possibly_remove_mode(struct modedata *md, char mode,
310 const char *user);
311 static void add_mode_with_params(struct modedata *md, char mode, int is_add,
312 int params, const char *parambuf, int len);
313 static void flush_cmode(struct modedata *md);
314 static void flush_cmode_callback(Timeout *t);
315
316 /*************************************************************************/
317
318 void set_cmode(const char *sender, Channel *channel, ...)
319 {
320 va_list args;
321 const char *modes, *modes_orig;
322 struct modedata *md;
323 int which = -1, add;
324 int i;
325 char c;
326
327
328 /* If `sender' is NULL, flush out pending modes for the channel (for
329 * all channels if `channel' is also NULL) and return. */
330 if (!sender) {
331 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
332 if (modedata[i].used && (!channel || modedata[i].channel==channel))
333 flush_cmode(&modedata[i]);
334 }
335 return;
336 }
337
338 /* Get the mode string from the argument list; save the original value
339 * for error messages. */
340 va_start(args, channel);
341 modes = modes_orig = va_arg(args, const char *);
342
343 /* See if we already have pending modes for the channel; if so, reuse
344 * that entry (if the entry is for a different sender, flush out the
345 * pending modes first). */
346 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
347 if (modedata[i].used != 0 && modedata[i].channel == channel) {
348 if (irc_stricmp(modedata[i].sender, sender) != 0)
349 flush_cmode(&modedata[i]);
350 which = i;
351 break;
352 }
353 }
354 /* If there are no pending modes for the channel, look for an empty
355 * slot in the array. */
356 if (which < 0) {
357 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
358 if (modedata[i].used == 0) {
359 which = i;
360 break;
361 }
362 }
363 }
364 /* If no slots are free, we'll have to purge one. Find the oldest,
365 * send its modes out, then clear and reuse it. */
366 if (which < 0) {
367 int oldest = 0;
368 time_t oldest_time = modedata[0].used;
369 for (i = 1; i < MERGE_CHANMODES_MAX; i++) {
370 if (modedata[i].used < oldest_time) {
371 oldest_time = modedata[i].used;
372 oldest = i;
373 }
374 }
375 flush_cmode(&modedata[oldest]);
376 which = oldest;
377 }
378
379 /* Save a pointer to the entry, then set up sender and channel. */
380 md = &modedata[which];
381 strbcpy(md->sender, sender);
382 md->channel = channel;
383
384 /* Loop through and process all modes in the mode string. */
385 add = -2; /* -2 means we haven't warned about a missing leading +/- yet */
386 while ((c = *modes++) != 0) {
387 int32 flag;
388 int params, is_chanuser;
389
390 log_debug(2, "set_cmode(%s,%s): char=%c(%02X)",
391 sender, channel->name, c<0x20||c>0x7E ? '.' : c, c);
392
393 /* + and - are handled specially. */
394 if (c == '+') {
395 add = 1;
396 continue;
397 } else if (c == '-') {
398 add = 0;
399 continue;
400 }
401 /* If we see any other character without first seeing a + or -,
402 * note a bug in the logfile and move along. */
403 if (add < 0) {
404 if (add == -2) {
405 log("set_cmode(): BUG: mode string `%s' needs leading +/-",
406 modes_orig);
407 add = -1;
408 }
409 continue;
410 }
411
412 /* Find the flag value and parameter count for the character. */
413 is_chanuser = 0;
414 flag = mode_char_to_flag(c, MODE_CHANNEL);
415 params = mode_char_to_params(c, MODE_CHANNEL);
416 if (!flag) {
417 is_chanuser = 1;
418 flag = mode_char_to_flag(c, MODE_CHANUSER);
419 params = mode_char_to_params(c, MODE_CHANUSER);
420 if (!flag) {
421 log("set_cmode: bad mode '%c'", c);
422 continue;
423 }
424 }
425 params = (params >> (add*8)) & 0xFF;
426
427 if (params) { /* Mode with parameters */
428 char parambuf[BUFSIZE]; /* for putting the parameters in */
429 int len = 0;
430
431 if (params > MAXMODES) {
432 /* Sanity check */
433 fatal("set_cmode(): too many parameters (%d) for mode `%c'\n",
434 params, c);
435 }
436 /* Merge all the parameters into a single string (with no
437 * leading whitespace) */
438 for (i = 0; i < params; i++) {
439 const char *s = va_arg(args, const char *);
440 log_debug(2, "set_cmode(%s,%s): param=%s",
441 sender, channel->name, s);
442 len += snprintf(parambuf+len, sizeof(parambuf)-len,
443 "%s%s", len ? " " : "", s);
444 }
445 if (flag != MODE_INVALID) {
446 /* If it's a binary mode, see if we've set this mode before.
447 * If so (and if the nick is the same for channel user
448 * modes), remove it; the new one will be appended
449 * afterwards. Note that this assumes that setting each
450 * mode is independent, i.e. that -a+ba 2 1 has the same
451 * effect as +ba 2 1 by itself when +a is set. This doesn't
452 * work for +k/-k, so we let multiple such modes remain. */
453 if (c != 'k') {
454 possibly_remove_mode(md, c, is_chanuser ? parambuf : NULL);
455 }
456 }
457 add_mode_with_params(md, c, add, params, parambuf, len);
458 } else { /* Binary mode */
459 /* Note that `flag' should already be set to this value, since
460 * all channel user modes take parameters and thus will never
461 * get here, but just in case... */
462 flag = mode_char_to_flag(c, MODE_CHANNEL);
463 if (add) {
464 md->binmodes_on |= flag;
465 md->binmodes_off &= ~flag;
466 } else {
467 md->binmodes_off |= flag;
468 md->binmodes_on &= ~flag;
469 }
470 }
471 }
472 va_end(args);
473 md->used = time(NULL);
474
475 if (MergeChannelModes) {
476 if (!md->timeout) {
477 md->timeout = add_timeout_ms(MergeChannelModes,
478 flush_cmode_callback, 0);
479 md->timeout->data = md;
480 }
481 }
482 }
483
484 /*************************************************************************/
485
486 /* Remove the most recent occurrence of mode `mode' from the mode list if
487 * there is one, provided either `user' is NULL or the parameter associated
488 * with the previous mode is equal (according to irc_stricmp()) to the
489 * string pointed to by `user'.
490 */
491
492 static void possibly_remove_mode(struct modedata *md, char mode,
493 const char *user)
494 {
495 int i;
496 char *s;
497
498 log_debug(2, "possibly_remove_mode %c from %.*s%s%s",
499 mode, md->nopmodes*2, md->opmodes,
500 user ? " for user " : "", user ? user : "");
501 for (i = md->nopmodes-1; i >= 0; i--) {
502 if (md->opmodes[i*2+1] == mode) {
503 /* We've already set this mode once */
504 if (user) {
505 /* Only remove the old mode if the nick matches */
506 if (irc_stricmp(md->params[i], user) != 0)
507 continue;
508 }
509 /* Remove the mode */
510 log_debug(2, " removing mode %d/%d", i, md->nopmodes);
511 md->nopmodes--;
512 s = md->opmodes + (i*2);
513 memmove(s, s+2, strlen(s+2)+1);
514 /* Count parameters for this mode and decrement total by count */
515 md->nparams--;
516 s = md->params[i]-1;
517 while ((s = strchr(s+1, ' ')) != NULL)
518 md->nparams--;
519 /* Move parameter pointers */
520 if (i < md->nopmodes) {
521 memmove(md->params+i, md->params+i+1,
522 sizeof(md->params[0])*(md->nopmodes-i));
523 }
524 /* Clear tail slot */
525 memset(md->params+md->nopmodes, 0, sizeof(md->params[0]));
526 }
527 }
528 }
529
530 /*************************************************************************/
531
532 /* Add a single mode with parameters to the given mode data structure.
533 * `params' is the number of parameters, `parambuf' is the space-separated
534 * parameter list, and `len' is strlen(parambuf).
535 */
536
537 static void add_mode_with_params(struct modedata *md, char mode, int is_add,
538 int params, const char *parambuf, int len)
539 {
540 char *s;
541
542 if (len < 0) {
543 log("add_mode_with_params(): BUG: parameter length < 0 (%d)", len);
544 len = 0;
545 }
546 log_debug(2, "add_mode_with_params: current=%.*s mode=%c add=%d"
547 " params=%d[%.*s]", md->nopmodes*2, md->opmodes, mode, is_add,
548 params, len, parambuf);
549
550 /* Check for overflow of parameter count or length */
551 if (md->nparams+params > MAXMODES
552 || md->paramslen+1+len > MAXPARAMSLEN
553 ) {
554 /* Doesn't fit, so flush modes out first */
555 struct modedata mdtmp = *md;
556 log_debug(2, "add_mode_with_params: ...flushing first");
557 flush_cmode(md);
558 memcpy(md->sender, mdtmp.sender, sizeof(md->sender));
559 md->channel = mdtmp.channel;
560 md->used = time(NULL);
561 }
562 s = md->opmodes + 2*md->nopmodes;
563 *s++ = is_add ? '+' : '-';
564 *s++ = mode;
565 if (len > sizeof(md->params[0])-1) {
566 log("set_cmode(): Parameter string for mode %c%c is too long,"
567 " truncating to %d characters",
568 is_add ? '+' : '-', mode, sizeof(md->params[0])-1);
569 len = sizeof(md->params[0])-1;
570 }
571 if (len > 0)
572 memcpy(md->params[md->nopmodes], parambuf, len);
573 md->params[md->nopmodes][len] = 0;
574 md->nopmodes++;
575 md->nparams += params;
576 if (md->paramslen)
577 md->paramslen++;
578 md->paramslen += len;
579 /* If the parameters for this mode alone exceed MAXPARAMSLEN,
580 * we'll now have a string longer than MAXPARAMSLEN in
581 * md->params; not much we can do about it, though, and it'll
582 * get flushed next time around anyway. */
583 }
584
585 /*************************************************************************/
586
587 /* Flush out pending mode changes for the given mode data structure. If
588 * `clear' is nonzero, clear the entry, else leave it alone.
589 */
590
591 static void flush_cmode(struct modedata *md)
592 {
593 char buf[BUFSIZE], *s;
594 char *argv[MAXMODES+2];
595 int len = 0, i;
596 char lastc = 0;
597
598 /* Clear timeout for this entry if one is set */
599 if (md->timeout) {
600 del_timeout(md->timeout);
601 md->timeout = NULL;
602 }
603
604 if (!md->channel) {
605 /* This entry is unused, just return */
606 goto done;
607 }
608 if (!md->binmodes_on && !md->binmodes_off && !*md->opmodes) {
609 /* No actual modes here */
610 goto done;
611 }
612
613 if (debug >= 2) {
614 char onbuf[512];
615 strbcpy(onbuf, mode_flags_to_string(md->binmodes_on,MODE_CHANNEL));
616 log_debug(2, "flush_cmode(%s): bin_on=%s bin_off=%s opmodes=%d(%.*s)",
617 md->channel->name, onbuf,
618 mode_flags_to_string(md->binmodes_off, MODE_CHANNEL),
619 md->nopmodes, md->nopmodes*2, md->opmodes);
620 }
621
622 /* Note that - must come before + because some servers (Unreal, others?)
623 * ignore +s if followed by -p. */
624 if (md->binmodes_off) {
625 len += snprintf(buf+len, sizeof(buf)-len, "-%s",
626 mode_flags_to_string(md->binmodes_off, MODE_CHANNEL));
627 lastc = '-';
628 }
629 if (md->binmodes_on) {
630 len += snprintf(buf+len, sizeof(buf)-len, "+%s",
631 mode_flags_to_string(md->binmodes_on, MODE_CHANNEL));
632 lastc = '+';
633 }
634 s = md->opmodes;
635 while (*s) {
636 if (*s == lastc) {
637 /* +/- matches last mode change */
638 s++;
639 } else {
640 if (len < sizeof(buf)-1)
641 buf[len++] = *s;
642 else
643 fatal("BUG: buf too small in flush_cmode() (1)");
644 lastc = *s++;
645 }
646 if (len < sizeof(buf)-1) {
647 buf[len++] = *s;
648 buf[len] = 0;
649 } else {
650 fatal("BUG: buf too small in flush_cmode() (2)");
651 }
652 s++;
653 }
654 for (i = 0; i < md->nopmodes; i++) {
655 if (*md->params[i])
656 len += snprintf(buf+len, sizeof(buf)-len, " %s", md->params[i]);
657 }
658
659 /* Actually send the command */
660 send_cmode_cmd(md->sender, md->channel->name, "%s", buf);
661
662 /* Split buffer back up into individual parameters for do_cmode().
663 * This is inefficient, but taking the faster route of setting modes
664 * when they are sent to set_cmode() runs the risk of temporary desyncs.
665 * (Example: SomeNick enters #channel -> autoop, but delayed -> SomeNick
666 * does /cs op SomeNick -> ChanServ says "SomeNick is already opped" ->
667 * SomeNick goes "Huh?")
668 */
669 argv[0] = md->channel->name;
670 s = buf;
671 for (i = 0; i <= md->nparams; i++) {
672 argv[i+1] = s;
673 s = strchr(s, ' ');
674 if (!s) {
675 md->nparams = i;
676 break;
677 }
678 *s++ = 0;
679 }
680 /* Clear md->channel so a recursive set_cmode() doesn't find this entry
681 * and try to use/flush it */
682 md->channel = NULL;
683 /* Adjust our idea of the channel modes */
684 do_cmode(md->sender, md->nparams+2, argv);
685
686 done:
687 /* Clear entry and return */
688 memset(md, 0, sizeof(*md));
689 }
690
691 /*************************************************************************/
692
693 /* Timeout called to flush mode changes for a channel after
694 * `MergeChannelModes' seconds of inactivity.
695 */
696
697 static void flush_cmode_callback(Timeout *t)
698 {
699 flush_cmode((struct modedata *)t->data);
700 }
701
702 /*************************************************************************/
703
704 /*
705 * Local variables:
706 * c-file-style: "stroustrup"
707 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
708 * indent-tabs-mode: nil
709 * End:
710 *
711 * vim: expandtab shiftwidth=4:
712 */