ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/actions.c
Revision: 1209
Committed: Thu Aug 25 19:05:49 2011 UTC (14 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 22941 byte(s)
Log Message:
- run everything thru indent
  "-bli0 -di1 -npcs -nut -cdw -bls -nbbo -bap"

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