ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/charybdis.C
Revision: 1.2
Committed: Sat Jul 21 01:29:09 2007 UTC (19 years, 3 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +2 -2 lines
Log Message:
- moved to new documentation system
- fixed small build error

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * Copyright © 2003-2004 E. Will et al.
3     * Copyright © 2005-2007 Atheme Development Group
4 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
5 pippijn 1.1 *
6     * This file contains protocol support for charybdis-based ircd.
7     *
8 pippijn 1.2 * $Id: charybdis.C,v 1.1 2007-07-19 08:24:55 pippijn Exp $
9 pippijn 1.1 */
10    
11     #include "atheme.h"
12     #include <account/mychan.h>
13     #include <account/chanacs.h>
14     #include <account/myuser.h>
15     #include "uplink.h"
16     #include "pmodule.h"
17     #include "protocol/charybdis.h"
18    
19     static char const rcsid[] = "$Id";
20    
21     struct charybdis_listeners : callback::has_listeners
22     {
23     };
24    
25     charybdis_listeners listeners;
26    
27     DECLARE_MODULE_V1 ("protocol/charybdis", true, _modinit, NULL, rcsid, "The Ermyth Team <http://ermyth.schmorp.de>");
28    
29     /* *INDENT-OFF* */
30    
31     ircd_t Charybdis = {
32     "Charybdis", /* IRCd name */
33     "$$", /* TLD Prefix, used by Global. */
34     true, /* Whether or not we use IRCNet/TS6 UID */
35     false, /* Whether or not we use RCOMMAND */
36     false, /* Whether or not we support channel owners. */
37     false, /* Whether or not we support channel protection. */
38     false, /* Whether or not we support halfops. */
39     false, /* Whether or not we use P10 */
40     false, /* Whether or not we use vHosts. */
41     CMODE_EXLIMIT | CMODE_PERM, /* Oper-only cmodes */
42     0, /* Integer flag for owner channel flag. */
43     0, /* Integer flag for protect channel flag. */
44     0, /* Integer flag for halfops. */
45     "+", /* Mode we set for owner. */
46     "+", /* Mode we set for protect. */
47     "+", /* Mode we set for halfops. */
48     PROTOCOL_CHARYBDIS, /* Protocol type */
49     CMODE_PERM, /* Permanent cmodes */
50     "beIq", /* Ban-like cmodes */
51     'e', /* Except mchar */
52     'I', /* Invex mchar */
53     IRCD_CIDR_BANS /* Flags */
54     };
55    
56     cmode_t charybdis_mode_list[] = {
57     { 'i', CMODE_INVITE },
58     { 'm', CMODE_MOD },
59     { 'n', CMODE_NOEXT },
60     { 'p', CMODE_PRIV },
61     { 's', CMODE_SEC },
62     { 't', CMODE_TOPIC },
63     { 'c', CMODE_NOCOLOR},
64     { 'r', CMODE_REGONLY},
65     { 'z', CMODE_OPMOD },
66     { 'g', CMODE_FINVITE},
67     { 'L', CMODE_EXLIMIT},
68     { 'P', CMODE_PERM },
69     { 'F', CMODE_FTARGET},
70     { 'Q', CMODE_DISFWD },
71     { '\0', 0 }
72     };
73    
74     static bool check_forward(const char *, channel_t *, mychan_t *, user_t *, myuser_t *);
75     static bool check_jointhrottle(const char *, channel_t *, mychan_t *, user_t *, myuser_t *);
76    
77     extmode charybdis_ignore_mode_list[] = {
78     { 'f', check_forward },
79     { 'j', check_jointhrottle },
80     { '\0', 0 }
81     };
82    
83     cmode_t charybdis_status_mode_list[] = {
84     { 'o', CMODE_OP },
85     { 'v', CMODE_VOICE },
86     { '\0', 0 }
87     };
88    
89     cmode_t charybdis_prefix_mode_list[] = {
90     { '@', CMODE_OP },
91     { '+', CMODE_VOICE },
92     { '\0', 0 }
93     };
94    
95     static bool use_rserv_support = false;
96     static bool use_tb = false;
97     static bool use_euid = false;
98    
99     static void server_eob(server_t *s);
100     static server_t *sid_find(char *name);
101    
102     static char ts6sid[3 + 1] = "";
103    
104     /* *INDENT-ON* */
105    
106     /* ircd allows forwards to existing channels; the target channel must be
107     * +F or the setter must have ops in it */
108     static bool
109     check_forward (const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
110     {
111     channel_t *target_c;
112     mychan_t *target_mc;
113     chanuser_t *target_cu;
114    
115     if (*value != '#' || strlen (value) > 50)
116     return false;
117     if (u == NULL && mu == NULL)
118     return true;
119     target_c = channel_find (value);
120     target_mc = mychan_find (value);
121     if (target_c == NULL && target_mc == NULL)
122     return false;
123     if (target_c != NULL && target_c->modes & CMODE_FTARGET)
124     return true;
125     if (target_mc != NULL && target_mc->mlock_on & CMODE_FTARGET)
126     return true;
127     if (u != NULL)
128     {
129     target_cu = chanuser_find (target_c, u);
130     if (target_cu != NULL && target_cu->modes & CMODE_OP)
131     return true;
132     if (chanacs_user_flags (target_mc, u) & CA_SET)
133     return true;
134     }
135     else if (mu != NULL)
136     if (chanacs_find (target_mc, mu, CA_SET))
137     return true;
138     return false;
139     }
140    
141     static bool
142     check_jointhrottle (const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
143     {
144     const char *p, *arg2;
145    
146     p = value, arg2 = NULL;
147     while (*p != '\0')
148     {
149     if (*p == ':')
150     {
151     if (arg2 != NULL)
152     return false;
153     arg2 = p + 1;
154     }
155     else if (!isdigit (*p))
156     return false;
157     p++;
158     }
159     if (arg2 == NULL)
160     return false;
161     if (p - arg2 > 10 || arg2 - value - 1 > 10 || !atoi (value) || !atoi (arg2))
162     return false;
163     return true;
164     }
165    
166     /* this may be slow, but it is not used much */
167     /* returns true if it matches, false if not */
168     /* note that the host part matches differently from a regular ban */
169     static bool
170     extgecos_match (const char *mask, user_t *u)
171     {
172     char hostgbuf[NICKLEN + USERLEN + HOSTLEN + GECOSLEN];
173     char realgbuf[NICKLEN + USERLEN + HOSTLEN + GECOSLEN];
174    
175     snprintf (hostgbuf, sizeof hostgbuf, "%s!%s@%s#%s", u->nick, u->user, u->vhost, u->gecos);
176     snprintf (realgbuf, sizeof realgbuf, "%s!%s@%s#%s", u->nick, u->user, u->host, u->gecos);
177     return !match (mask, hostgbuf) || !match (mask, realgbuf);
178     }
179    
180     node_t *
181     charybdis_next_matching_ban (channel_t *c, user_t *u, int type, node_t *first)
182     {
183     chanban_t *cb;
184     node_t *n;
185     char hostbuf[NICKLEN + USERLEN + HOSTLEN];
186     char realbuf[NICKLEN + USERLEN + HOSTLEN];
187     char ipbuf[NICKLEN + USERLEN + HOSTLEN];
188     const char *p;
189     bool negate, matched;
190     int exttype;
191     channel_t *target_c;
192    
193     snprintf (hostbuf, sizeof hostbuf, "%s!%s@%s", u->nick, u->user, u->vhost);
194     snprintf (realbuf, sizeof realbuf, "%s!%s@%s", u->nick, u->user, u->host);
195     /* will be nick!user@ if ip unknown, doesn't matter */
196     snprintf (ipbuf, sizeof ipbuf, "%s!%s@%s", u->nick, u->user, u->ip);
197     LIST_FOREACH (n, first)
198     {
199     cb = static_cast<chanban_t *> (n->data);
200    
201     if (cb->type == type && (!match (cb->mask, hostbuf) || !match (cb->mask, realbuf) || !match (cb->mask, ipbuf) || !match_cidr (cb->mask, ipbuf)))
202     return n;
203     if (cb->type == type && cb->mask[0] == '$')
204     {
205     p = cb->mask + 1;
206     negate = *p == '~';
207     if (negate)
208     p++;
209     exttype = *p++;
210     if (exttype == '\0')
211     continue;
212     /* check parameter */
213     if (*p++ != ':')
214     p = NULL;
215     switch (exttype)
216     {
217     case 'a':
218     matched = u->myuser != NULL && !(u->myuser->flags & MU_WAITAUTH) && (p == NULL || !match (p, u->myuser->name));
219     break;
220     case 'c':
221     if (p == NULL)
222     continue;
223     target_c = channel_find (p);
224     if (target_c == NULL || (target_c->modes & (CMODE_PRIV | CMODE_SEC)))
225     continue;
226     matched = chanuser_find (target_c, u) != NULL;
227     break;
228     case 'o':
229     matched = is_ircop (u);
230     break;
231     case 'r':
232     if (p == NULL)
233     continue;
234     matched = !match (p, u->gecos);
235     break;
236     case 's':
237     if (p == NULL)
238     continue;
239     matched = !match (p, u->server->name);
240     break;
241     case 'x':
242     if (p == NULL)
243     continue;
244     matched = extgecos_match (p, u);
245     break;
246     default:
247     continue;
248     }
249     if (negate ^ matched)
250     return n;
251     }
252     }
253     return NULL;
254     }
255    
256     /* login to our uplink */
257     static unsigned int
258     charybdis_server_login (void)
259     {
260     int ret = 1;
261    
262     if (!me.numeric)
263     {
264     ircd->uses_uid = false;
265     ret = sts ("PASS %s :TS", curr_uplink->pass);
266     }
267     else if (strlen (me.numeric) == 3 && isdigit (*me.numeric))
268     {
269     ircd->uses_uid = true;
270     ret = sts ("PASS %s TS 6 :%s", curr_uplink->pass, me.numeric);
271     }
272     else
273     {
274     slog (LG_ERROR, "Invalid numeric (SID) %s", me.numeric);
275     }
276     if (ret == 1)
277     return 1;
278    
279     me.bursting = true;
280    
281     sts ("CAPAB :QS EX IE KLN UNKLN ENCAP TB SERVICES EUID");
282     sts ("SERVER %s 1 :%s", me.name, me.desc);
283     sts ("SVINFO %d 3 0 :%ld", ircd->uses_uid ? 6 : 5, NOW);
284    
285     return 0;
286     }
287    
288     /* introduce a client */
289     static void
290     charybdis_introduce_nick (user_t *u)
291     {
292     if (ircd->uses_uid && use_euid)
293     sts (":%s EUID %s 1 %ld +%s%sS %s %s 0 %s * * :%s", me.numeric, u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", u->user, u->host, u->uid, u->gecos);
294     else if (ircd->uses_uid)
295     sts (":%s UID %s 1 %ld +%s%sS %s %s 0 %s :%s", me.numeric, u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", u->user, u->host, u->uid, u->gecos);
296     else
297     sts ("NICK %s 1 %ld +%s%sS %s %s %s :%s", u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", u->user, u->host, me.name, u->gecos);
298     }
299    
300     /* invite a user to a channel */
301     static void
302     charybdis_invite_sts (user_t *sender, user_t *target, channel_t *channel)
303     {
304     sts (":%s INVITE %s %s", CLIENT_NAME (sender), CLIENT_NAME (target), channel->name);
305     }
306    
307     static void
308     charybdis_quit_sts (user_t *u, const char *reason)
309     {
310     if (!me.connected)
311     return;
312    
313     sts (":%s QUIT :%s", CLIENT_NAME (u), reason);
314     }
315    
316     /* WALLOPS wrapper */
317     static void
318     charybdis_wallops_sts (const char *text)
319     {
320     sts (":%s WALLOPS :%s", ME, text);
321     }
322    
323     /* join a channel */
324     static void
325     charybdis_join_sts (channel_t *c, user_t *u, bool isnew, char *modes)
326     {
327     if (isnew)
328     sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, modes, CLIENT_NAME (u));
329     else
330     sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (u));
331     }
332    
333     static void
334     charybdis_chan_lowerts (channel_t *c, user_t *u)
335     {
336     slog (LG_DEBUG, "charybdis_chan_lowerts(): lowering TS for %s to %ld", c->name, (long) c->ts);
337     sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, channel_modes (c, true), CLIENT_NAME (u));
338     if (ircd->uses_uid)
339     chanban_clear (c);
340     }
341    
342     /* kicks a user from a channel */
343     static void
344     charybdis_kick (char *from, char *channel, char *to, char *reason)
345     {
346     channel_t *chan = channel_find (channel);
347     user_t *user = user_find (to);
348     user_t *from_p = user_find (from);
349    
350     if (!chan || !user)
351     return;
352    
353     if (chan->ts != 0 || chanuser_find (chan, from_p))
354     sts (":%s KICK %s %s :%s", CLIENT_NAME (from_p), channel, CLIENT_NAME (user), reason);
355     else
356     sts (":%s KICK %s %s :%s", ME, channel, CLIENT_NAME (user), reason);
357    
358     chanuser_delete (chan, user);
359     }
360    
361     /* PRIVMSG wrapper */
362     static void
363     charybdis_msg (const char *from, const char *target, const char *fmt, ...)
364     {
365     va_list ap;
366     char buf[BUFSIZE];
367     user_t *u = user_find (from);
368     user_t *t = user_find (target);
369    
370     if (!u)
371     return;
372    
373     va_start (ap, fmt);
374     vsnprintf (buf, BUFSIZE, fmt, ap);
375     va_end (ap);
376    
377     /* If this is to a channel, it's the snoop channel so chanserv
378     * is on it -- jilles
379     *
380     * Well, now it's operserv, but yes it's safe to assume that
381     * the source would be able to send to whatever target it is
382     * sending to. --nenolod
383     */
384     sts (":%s PRIVMSG %s :%s", CLIENT_NAME (u), t ? CLIENT_NAME (t) : target, buf);
385     }
386    
387     /* NOTICE wrapper */
388     static void
389     charybdis_notice_user_sts (user_t *from, user_t *target, const char *text)
390     {
391     sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, CLIENT_NAME (target), text);
392     }
393    
394     static void
395     charybdis_notice_global_sts (user_t *from, const char *mask, const char *text)
396     {
397     node_t *n;
398     tld_t *tld;
399    
400     if (!strcmp (mask, "*"))
401     {
402     LIST_FOREACH (n, tldlist.head)
403     {
404     tld = static_cast<tld_t *> (n->data);
405     sts (":%s NOTICE %s*%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, tld->name, text);
406     }
407     }
408     else
409     sts (":%s NOTICE %s%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, mask, text);
410     }
411    
412     static void
413     charybdis_notice_channel_sts (user_t *from, channel_t *target, const char *text)
414     {
415     if (from == NULL || chanuser_find (target, from))
416     sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, target->name, text);
417     else
418     sts (":%s NOTICE %s :[%s:%s] %s", ME, target->name, from->nick, target->name, text);
419     }
420    
421     static void
422     charybdis_wallchops (user_t *sender, channel_t *channel, const char *message)
423     {
424     if (chanuser_find (channel, sender))
425     sts (":%s NOTICE @%s :%s", CLIENT_NAME (sender), channel->name, message);
426     else /* do not join for this, everyone would see -- jilles */
427     generic_wallchops (sender, channel, message);
428     }
429    
430     /* numeric wrapper */
431     static void
432     charybdis_numeric_sts (char *from, int numeric, char *target, char *fmt, ...)
433     {
434     va_list ap;
435     char buf[BUFSIZE];
436     user_t *t = user_find (target);
437    
438     va_start (ap, fmt);
439     vsnprintf (buf, BUFSIZE, fmt, ap);
440     va_end (ap);
441    
442     sts (":%s %d %s %s", ME, numeric, CLIENT_NAME (t), buf);
443     }
444    
445     /* KILL wrapper */
446     static void
447     charybdis_skill (char *from, char *nick, char *fmt, ...)
448     {
449     va_list ap;
450     char buf[BUFSIZE];
451     user_t *killer = user_find (from);
452     user_t *victim = user_find (nick);
453    
454     va_start (ap, fmt);
455     vsnprintf (buf, BUFSIZE, fmt, ap);
456     va_end (ap);
457    
458     sts (":%s KILL %s :%s!%s!%s (%s)", killer ? CLIENT_NAME (killer) : ME, victim ? CLIENT_NAME (victim) : nick, from, from, from, buf);
459     }
460    
461     /* PART wrapper */
462     static void
463     charybdis_part_sts (channel_t *c, user_t *u)
464     {
465     sts (":%s PART %s", CLIENT_NAME (u), c->name);
466     }
467    
468     /* server-to-server KLINE wrapper */
469     static void
470     charybdis_kline_sts (char *server, char *user, char *host, long duration, char *reason)
471     {
472     if (!me.connected)
473     return;
474    
475     sts (":%s KLINE %s %ld %s %s :%s", CLIENT_NAME (opersvs.me->me), server, duration, user, host, reason);
476     }
477    
478     /* server-to-server UNKLINE wrapper */
479     static void
480     charybdis_unkline_sts (char *server, char *user, char *host)
481     {
482     if (!me.connected)
483     return;
484    
485     sts (":%s UNKLINE %s %s %s", CLIENT_NAME (opersvs.me->me), server, user, host);
486     }
487    
488     /* topic wrapper */
489     static void
490     charybdis_topic_sts (channel_t *c, const char *setter, time_t ts, time_t prevts, const char *topic)
491     {
492     int joined = 0;
493    
494     if (!me.connected || !c)
495     return;
496    
497     /* If possible, try to use TB
498     * Note that because TOPIC does not contain topicTS, it may be
499     * off a few seconds on other servers, hence the 60 seconds here.
500     * -- jilles */
501     if (use_tb && *topic != '\0')
502     {
503     /* Restoring old topic */
504     if (ts < prevts || prevts == 0)
505     {
506     if (prevts != 0 && ts + 60 > prevts)
507     ts = prevts - 60;
508     sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
509     c->topicts = ts;
510     return;
511     }
512     /* Tweaking a topic */
513     else if (ts == prevts)
514     {
515     ts -= 60;
516     sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
517     c->topicts = ts;
518     return;
519     }
520     }
521     /* We have to be on channel to change topic.
522     * We cannot nicely change topic from the server:
523     * :server.name TOPIC doesn't propagate and TB requires
524     * us to specify an older topicts.
525     * -- jilles
526     */
527     if (!chanuser_find (c, chansvs.me->me))
528     {
529     sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (chansvs.me->me));
530     joined = 1;
531     }
532     sts (":%s TOPIC %s :%s", CLIENT_NAME (chansvs.me->me), c->name, topic);
533     if (joined)
534     sts (":%s PART %s :Topic set", CLIENT_NAME (chansvs.me->me), c->name);
535     c->topicts = NOW;
536     }
537    
538     /* mode wrapper */
539     static void
540     charybdis_mode_sts (char *sender, channel_t *target, char *modes)
541     {
542     user_t *u = user_find (sender);
543    
544     if (!me.connected || !u)
545     return;
546    
547     if (ircd->uses_uid)
548     sts (":%s TMODE %ld %s %s", CLIENT_NAME (u), target->ts, target->name, modes);
549     else
550     sts (":%s MODE %s %s", CLIENT_NAME (u), target->name, modes);
551     }
552    
553     /* ping wrapper */
554     static void
555     charybdis_ping_sts (void)
556     {
557     if (!me.connected)
558     return;
559    
560     sts ("PING :%s", me.name);
561     }
562    
563     /* protocol-specific stuff to do on login */
564     static void
565     charybdis_on_login (char *origin, char *user, char *wantedhost)
566     {
567     user_t *u = user_find (origin);
568    
569     if (!me.connected || !use_rserv_support || !u)
570     return;
571    
572     sts (":%s ENCAP * SU %s %s", ME, CLIENT_NAME (u), user);
573     }
574    
575     /* protocol-specific stuff to do on login */
576     static bool
577     charybdis_on_logout (char *origin, char *user, char *wantedhost)
578     {
579     user_t *u = user_find (origin);
580    
581     if (!me.connected || !use_rserv_support || !u)
582     return false;
583    
584     sts (":%s ENCAP * SU %s", ME, CLIENT_NAME (u));
585     return false;
586     }
587    
588     /* XXX we don't have an appropriate API for this, what about making JUPE
589     * serverside like in P10?
590     * --nenolod
591     */
592     static void
593     charybdis_jupe (const char *server, const char *reason)
594     {
595     if (!me.connected)
596     return;
597    
598     server_delete (server);
599     sts (":%s SQUIT %s :%s", CLIENT_NAME (opersvs.me->me), server, reason);
600     sts (":%s SERVER %s 2 :(H) %s", me.name, server, reason);
601     }
602    
603     static void
604     charybdis_sethost_sts (char *source, char *target, char *host)
605     {
606     user_t *tu = user_find (target);
607    
608     if (!tu)
609     return;
610    
611     if (use_euid)
612     sts (":%s CHGHOST %s :%s", ME, CLIENT_NAME (tu), host);
613     else
614     sts (":%s ENCAP * CHGHOST %s :%s", ME, tu->nick, host);
615     }
616    
617     static void
618     charybdis_fnc_sts (user_t *source, user_t *u, char *newnick, int type)
619     {
620     /* XXX assumes the server will accept this -- jilles */
621     sts (":%s ENCAP %s RSFNC %s %s %lu %lu", ME, u->server->name, CLIENT_NAME (u), newnick, (unsigned long) (NOW - 60), (unsigned long) u->ts);
622     }
623    
624     static void
625     charybdis_svslogin_sts (char *target, char *nick, char *user, char *host, char *login)
626     {
627     user_t *tu = user_find (target);
628     server_t *s;
629    
630     if (tu)
631     s = tu->server;
632     else if (ircd->uses_uid) /* Non-announced UID - must be a SASL client. */
633     s = sid_find (target);
634     else
635     return;
636    
637     sts (":%s ENCAP %s SVSLOGIN %s %s %s %s %s", ME, s->name, target, nick, user, host, login);
638     }
639    
640     static void
641     charybdis_sasl_sts (char *target, char mode, char *data)
642     {
643     server_t *s = sid_find (target);
644    
645     if (s == NULL)
646     return;
647    
648     if (saslsvs.me == NULL)
649     {
650     slog (LG_ERROR, "charybdis_sasl_sts(): saslserv does not exist!");
651     return;
652     }
653    
654     sts (":%s ENCAP %s SASL %s %s %c %s", ME, s->name, saslsvs.me->uid, target, mode, data);
655     }
656    
657     static void
658     charybdis_holdnick_sts (user_t *source, int duration, const char *nick, myuser_t *account)
659     {
660     if (use_euid)
661     sts (":%s ENCAP * NICKDELAY %d %s", ME, duration, nick);
662     else
663     {
664     if (duration == 0)
665     return; /* can't do this safely */
666     sts (":%s ENCAP * RESV %d %s 0 :Reserved by %s for nickname owner (%s)", CLIENT_NAME (source), duration > 300 ? 300 : duration, nick, source->nick, account != NULL ? account->name : nick);
667     }
668     }
669    
670     static void
671     m_topic (sourceinfo_t *si, int parc, char *parv[])
672     {
673     channel_t *c = channel_find (parv[0]);
674    
675     if (c == NULL)
676     return;
677    
678     handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
679     }
680    
681     static void
682     m_tb (sourceinfo_t *si, int parc, char *parv[])
683     {
684     channel_t *c = channel_find (parv[0]);
685     time_t ts = atol (parv[1]);
686    
687     if (c == NULL)
688     return;
689    
690     if (c->topic != NULL && c->topicts <= ts)
691     {
692     slog (LG_DEBUG, "m_tb(): ignoring newer topic on %s", c->name);
693     return;
694     }
695    
696     handle_topic_from (si, c, parc > 3 ? parv[2] : si->s->name, ts, parv[parc - 1]);
697     }
698    
699     static void
700     m_ping (sourceinfo_t *si, int parc, char *parv[])
701     {
702     /* reply to PING's */
703     sts (":%s PONG %s %s", ME, me.name, parv[0]);
704     }
705    
706     static void
707     m_pong (sourceinfo_t *si, int parc, char *parv[])
708     {
709     server_t *s;
710    
711     /* someone replied to our PING */
712     if (!parv[0])
713     return;
714     s = server_find (parv[0]);
715     if (s == NULL)
716     return;
717     handle_eob (s);
718    
719     if (irccasecmp (me.actual, parv[0]))
720     return;
721    
722     me.uplinkpong = NOW;
723    
724     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
725     if (me.bursting)
726     {
727     #ifdef HAVE_GETTIMEOFDAY
728     e_time (burstime, &burstime);
729    
730     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
731    
732     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
733     #else
734     slog (LG_INFO, "m_pong(): finished synching with uplink");
735     wallops ("Finished synching to network.");
736     #endif
737    
738     me.bursting = false;
739     }
740     }
741    
742     static void
743     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
744     {
745     if (parc != 2)
746     return;
747    
748     handle_message (si, parv[0], false, parv[1]);
749     }
750    
751     static void
752     m_notice (sourceinfo_t *si, int parc, char *parv[])
753     {
754     if (parc != 2)
755     return;
756    
757     handle_message (si, parv[0], true, parv[1]);
758     }
759    
760     static void
761     m_sjoin (sourceinfo_t *si, int parc, char *parv[])
762     {
763     /* -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur */
764    
765     channel_t *c;
766     bool keep_new_modes = true;
767     unsigned int userc;
768     char *userv[256];
769     unsigned int i;
770     time_t ts;
771     char *p;
772    
773     /* :origin SJOIN ts chan modestr [key or limits] :users */
774     c = channel_find (parv[1]);
775     ts = atol (parv[0]);
776    
777     if (!c)
778     {
779     slog (LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
780     c = channel_add (parv[1], ts, si->s);
781     }
782    
783     if (ts == 0 || c->ts == 0)
784     {
785     if (c->ts != 0)
786     slog (LG_INFO, "m_sjoin(): server %s changing TS on %s from %ld to 0", si->s->name, c->name, (long) c->ts);
787     c->ts = 0;
788     hook_call_event ("channel_tschange", c);
789     }
790     else if (ts < c->ts)
791     {
792     chanuser_t *cu;
793     node_t *n;
794    
795     /* the TS changed. a TS change requires the following things
796     * to be done to the channel: reset all modes to nothing, remove
797     * all status modes on known users on the channel (including ours),
798     * and set the new TS.
799     *
800     * if the source does TS6, also remove all bans
801     * note that JOIN does not do this
802     */
803    
804     clear_simple_modes (c);
805     if (si->s->sid != NULL)
806     chanban_clear (c);
807    
808     LIST_FOREACH (n, c->members.head)
809     {
810     cu = (chanuser_t *) n->data;
811     if (cu->user->server == me.me)
812     {
813     /* it's a service, reop */
814     sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
815     sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
816     cu->modes = CMODE_OP;
817     }
818     else
819     cu->modes = 0;
820     }
821    
822     slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
823    
824     c->ts = ts;
825     hook_call_event ("channel_tschange", c);
826     }
827     else if (ts > c->ts)
828     keep_new_modes = false;
829    
830     if (keep_new_modes)
831     channel_mode (NULL, c, parc - 3, parv + 2);
832    
833     userc = sjtoken (parv[parc - 1], ' ', userv);
834    
835     if (keep_new_modes)
836     for (i = 0; i < userc; i++)
837     chanuser_add (c, userv[i]);
838     else
839     for (i = 0; i < userc; i++)
840     {
841     p = userv[i];
842     while (*p == '@' || *p == '%' || *p == '+')
843     p++;
844     /* XXX for TS5 we should mark them deopped
845     * if they were opped and drop modes from them
846     * -- jilles */
847     chanuser_add (c, p);
848     }
849    
850     if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
851     channel_delete (c);
852     }
853    
854     static void
855     m_join (sourceinfo_t *si, int parc, char *parv[])
856     {
857     /* -> :1JJAAAAAB JOIN 1127474195 #test +tn */
858     bool keep_new_modes = true;
859     node_t *n, *tn;
860     channel_t *c;
861     chanuser_t *cu;
862     time_t ts;
863    
864     /* JOIN 0 is really a part from all channels */
865     /* be sure to allow joins to TS 0 channels -- jilles */
866     if (parv[0][0] == '0' && parc <= 2)
867     {
868     LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
869     {
870     cu = (chanuser_t *) n->data;
871     chanuser_delete (cu->chan, si->su);
872     }
873     return;
874     }
875    
876     /* :user JOIN ts chan modestr [key or limits] */
877     c = channel_find (parv[1]);
878     ts = atol (parv[0]);
879    
880     if (!c)
881     {
882     slog (LG_DEBUG, "m_join(): new channel: %s", parv[1]);
883     c = channel_add (parv[1], ts, si->su->server);
884     }
885    
886     if (ts == 0 || c->ts == 0)
887     {
888     if (c->ts != 0)
889     slog (LG_INFO, "m_join(): server %s changing TS on %s from %ld to 0", si->su->server->name, c->name, (long) c->ts);
890     c->ts = 0;
891     hook_call_event ("channel_tschange", c);
892     }
893     else if (ts < c->ts)
894     {
895     /* the TS changed. a TS change requires the following things
896     * to be done to the channel: reset all modes to nothing, remove
897     * all status modes on known users on the channel (including ours),
898     * and set the new TS.
899     */
900     clear_simple_modes (c);
901    
902     LIST_FOREACH (n, c->members.head)
903     {
904     cu = (chanuser_t *) n->data;
905     if (cu->user->server == me.me)
906     {
907     /* it's a service, reop */
908     sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
909     sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
910     cu->modes = CMODE_OP;
911     }
912     else
913     cu->modes = 0;
914     }
915     slog (LG_DEBUG, "m_join(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
916     c->ts = ts;
917     hook_call_event ("channel_tschange", c);
918     }
919     else if (ts > c->ts)
920     keep_new_modes = false;
921    
922     if (keep_new_modes)
923     channel_mode (NULL, c, parc - 2, parv + 2);
924    
925     chanuser_add (c, CLIENT_NAME (si->su));
926     }
927    
928     static void
929     m_bmask (sourceinfo_t *si, int parc, char *parv[])
930     {
931     unsigned int ac, i;
932     char *av[256];
933     channel_t *c = channel_find (parv[1]);
934     int type;
935    
936     /* :1JJ BMASK 1127474361 #services b :*!*@*evil* *!*eviluser1@* */
937     if (!c)
938     {
939     slog (LG_DEBUG, "m_bmask(): got bmask for unknown channel");
940     return;
941     }
942    
943     if (atol (parv[0]) > c->ts)
944     return;
945    
946     type = *parv[2];
947     if (!strchr (ircd->ban_like_modes, type))
948     {
949     slog (LG_DEBUG, "m_bmask(): got unknown type '%c'", type);
950     return;
951     }
952    
953     ac = sjtoken (parv[parc - 1], ' ', av);
954    
955     for (i = 0; i < ac; i++)
956     chanban_add (c, av[i], type);
957     }
958    
959     static void
960     m_part (sourceinfo_t *si, int parc, char *parv[])
961     {
962     int chanc;
963     char *chanv[256];
964     int i;
965    
966     chanc = sjtoken (parv[0], ',', chanv);
967     for (i = 0; i < chanc; i++)
968     {
969     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
970    
971     chanuser_delete (channel_find (chanv[i]), si->su);
972     }
973     }
974    
975     static void
976     m_nick (sourceinfo_t *si, int parc, char *parv[])
977     {
978     server_t *s;
979     user_t *u;
980    
981     /* got the right number of args for an introduction? */
982     if (parc == 8)
983     {
984     s = server_find (parv[6]);
985     if (!s)
986     {
987     slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
988     return;
989     }
990    
991     slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
992    
993     u = user_add (parv[0], parv[4], parv[5], NULL, NULL, NULL, parv[7], s, atoi (parv[2]));
994    
995     user_mode (u, parv[3]);
996    
997     /* If server is not yet EOB we will do this later.
998     * This avoids useless "please identify" -- jilles */
999     if (s->flags & SF_EOB)
1000     handle_nickchange (user_find (parv[0]));
1001     }
1002    
1003     /* if it's only 2 then it's a nickname change */
1004     else if (parc == 2)
1005     {
1006     if (!si->su)
1007     {
1008     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
1009     return;
1010     }
1011    
1012     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
1013    
1014     user_changenick (si->su, parv[0], atoi (parv[1]));
1015    
1016     /* It could happen that our PING arrived late and the
1017     * server didn't acknowledge EOB yet even though it is
1018     * EOB; don't send double notices in that case -- jilles */
1019     if (si->su->server->flags & SF_EOB)
1020     handle_nickchange (si->su);
1021     }
1022     else
1023     {
1024     int i;
1025     slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
1026    
1027     for (i = 0; i < parc; i++)
1028     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
1029     }
1030     }
1031    
1032     static void
1033     m_uid (sourceinfo_t *si, int parc, char *parv[])
1034     {
1035     server_t *s;
1036     user_t *u;
1037    
1038     /* got the right number of args for an introduction? */
1039     if (parc == 9)
1040     {
1041     s = si->s;
1042     slog (LG_DEBUG, "m_uid(): new user on `%s': %s", s->name, parv[0]);
1043    
1044     u = user_add (parv[0], parv[4], parv[5], NULL, parv[6], parv[7], parv[8], s, atoi (parv[2]));
1045    
1046     user_mode (u, parv[3]);
1047    
1048     /* If server is not yet EOB we will do this later.
1049     * This avoids useless "please identify" -- jilles
1050     */
1051     if (s->flags & SF_EOB)
1052     handle_nickchange (user_find (parv[0]));
1053     }
1054     else
1055     {
1056     int i;
1057     slog (LG_DEBUG, "m_uid(): got UID with wrong number of params");
1058    
1059     for (i = 0; i < parc; i++)
1060     slog (LG_DEBUG, "m_uid(): parv[%d] = %s", i, parv[i]);
1061     }
1062     }
1063    
1064     static void
1065     m_euid (sourceinfo_t *si, int parc, char *parv[])
1066     {
1067     server_t *s;
1068     user_t *u;
1069    
1070     /* got the right number of args for an introduction? */
1071     if (parc >= 11)
1072     {
1073     s = si->s;
1074     slog (LG_DEBUG, "m_euid(): new user on `%s': %s", s->name, parv[0]);
1075    
1076     u = user_add (parv[0], /* nick */
1077     parv[4], /* user */
1078     *parv[8] != '*' ? parv[8] : parv[5], /* hostname */
1079     parv[5], /* hostname (visible) */
1080     parv[6], /* ip */
1081     parv[7], /* uid */
1082     parv[parc - 1], /* gecos */
1083     s, /* object parent (server) */
1084     atoi (parv[2])); /* hopcount */
1085    
1086     user_mode (u, parv[3]);
1087     if (*parv[9] != '*')
1088     handle_burstlogin (u, parv[9]);
1089    
1090     /* server_eob() cannot know if a user was introduced
1091     * with NICK/UID or EUID and handle_nickchange() must
1092     * be called exactly once for each new user -- jilles */
1093     if (s->flags & SF_EOB)
1094     handle_nickchange (u);
1095     }
1096     else
1097     {
1098     int i;
1099     slog (LG_DEBUG, "m_euid(): got EUID with wrong number of params");
1100    
1101     for (i = 0; i < parc; i++)
1102     slog (LG_DEBUG, "m_euid(): parv[%d] = %s", i, parv[i]);
1103     }
1104     }
1105    
1106     static void
1107     m_quit (sourceinfo_t *si, int parc, char *parv[])
1108     {
1109     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
1110    
1111     /* user_delete() takes care of removing channels and so forth */
1112     user_delete (si->su);
1113     }
1114    
1115     static void
1116     m_mode (sourceinfo_t *si, int parc, char *parv[])
1117     {
1118     if (*parv[0] == '#')
1119     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
1120     else
1121     user_mode (user_find (parv[0]), parv[1]);
1122     }
1123    
1124     static void
1125     m_tmode (sourceinfo_t *si, int parc, char *parv[])
1126     {
1127     channel_t *c;
1128    
1129     /* -> :1JJAAAAAB TMODE 1127511579 #new +o 2JJAAAAAB */
1130     c = channel_find (parv[1]);
1131     if (c == NULL)
1132     {
1133     slog (LG_DEBUG, "m_tmode(): nonexistent channel %s", parv[1]);
1134     return;
1135     }
1136    
1137     if (atol (parv[0]) > c->ts)
1138     return;
1139    
1140     channel_mode (NULL, c, parc - 2, &parv[2]);
1141     }
1142    
1143     static void
1144     m_kick (sourceinfo_t *si, int parc, char *parv[])
1145     {
1146     user_t *u = user_find (parv[1]);
1147     channel_t *c = channel_find (parv[0]);
1148    
1149     /* -> :rakaur KICK #shrike rintaun :test */
1150     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
1151    
1152     if (!u)
1153     {
1154     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
1155     return;
1156     }
1157    
1158     if (!c)
1159     {
1160     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
1161     return;
1162     }
1163    
1164     if (!chanuser_find (c, u))
1165     {
1166     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
1167     return;
1168     }
1169    
1170     chanuser_delete (c, u);
1171    
1172     /* if they kicked us, let's rejoin */
1173     if (is_internal_client (u))
1174     {
1175     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
1176     join (parv[0], u->nick);
1177     }
1178     }
1179    
1180     static void
1181     m_kill (sourceinfo_t *si, int parc, char *parv[])
1182     {
1183     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
1184     }
1185    
1186     static void
1187     m_squit (sourceinfo_t *si, int parc, char *parv[])
1188     {
1189     slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
1190     server_delete (parv[0]);
1191     }
1192    
1193     static void
1194     m_server (sourceinfo_t *si, int parc, char *parv[])
1195     {
1196     server_t *s;
1197    
1198     slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
1199     s = handle_server (si, parv[0], si->s || !ircd->uses_uid ? NULL : ts6sid, atoi (parv[1]), parv[2]);
1200    
1201     if (s != NULL && s->uplink != me.me)
1202     {
1203     /* elicit PONG for EOB detection; pinging uplink is
1204     * already done elsewhere -- jilles
1205     */
1206     sts (":%s PING %s %s", ME, me.name, s->name);
1207     }
1208     }
1209    
1210     static void
1211     m_sid (sourceinfo_t *si, int parc, char *parv[])
1212     {
1213     /* -> :1JJ SID file. 2 00F :telnet server */
1214     server_t *s;
1215    
1216     slog (LG_DEBUG, "m_sid(): new server: %s", parv[0]);
1217     s = handle_server (si, parv[0], parv[2], atoi (parv[1]), parv[3]);
1218    
1219     if (s != NULL && s->uplink != me.me)
1220     {
1221     /* elicit PONG for EOB detection; pinging uplink is
1222     * already done elsewhere -- jilles
1223     */
1224     sts (":%s PING %s %s", ME, me.name, s->sid);
1225     }
1226     }
1227    
1228     static void
1229     m_stats (sourceinfo_t *si, int parc, char *parv[])
1230     {
1231     handle_stats (si->su, parv[0][0]);
1232     }
1233    
1234     static void
1235     m_admin (sourceinfo_t *si, int parc, char *parv[])
1236     {
1237     handle_admin (si->su);
1238     }
1239    
1240     static void
1241     m_version (sourceinfo_t *si, int parc, char *parv[])
1242     {
1243     handle_version (si->su);
1244     }
1245    
1246     static void
1247     m_info (sourceinfo_t *si, int parc, char *parv[])
1248     {
1249     handle_info (si->su);
1250     }
1251    
1252     static void
1253     m_whois (sourceinfo_t *si, int parc, char *parv[])
1254     {
1255     handle_whois (si->su, parv[1]);
1256     }
1257    
1258     static void
1259     m_trace (sourceinfo_t *si, int parc, char *parv[])
1260     {
1261     handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
1262     }
1263    
1264     static void
1265     m_away (sourceinfo_t *si, int parc, char *parv[])
1266     {
1267     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1268     }
1269    
1270     static void
1271     m_pass (sourceinfo_t *si, int parc, char *parv[])
1272     {
1273     /* TS5: PASS mypassword :TS
1274     * TS6: PASS mypassword TS 6 :sid */
1275     if (strcmp (curr_uplink->pass, parv[0]))
1276     {
1277     slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
1278     runflags |= RF_SHUTDOWN;
1279     }
1280    
1281     if (ircd->uses_uid && parc > 3 && atoi (parv[2]) >= 6)
1282     strlcpy (ts6sid, parv[3], sizeof (ts6sid));
1283     else
1284     {
1285     if (ircd->uses_uid)
1286     {
1287     slog (LG_INFO, "m_pass(): uplink does not support TS6, falling back to TS5");
1288     ircd->uses_uid = false;
1289     }
1290     ts6sid[0] = '\0';
1291     }
1292     }
1293    
1294     static void
1295     m_error (sourceinfo_t *si, int parc, char *parv[])
1296     {
1297     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1298     }
1299    
1300     static void
1301     m_encap (sourceinfo_t *si, int parc, char *parv[])
1302     {
1303     user_t *u;
1304    
1305     if (match (parv[0], me.name))
1306     return;
1307     if (!irccasecmp (parv[1], "LOGIN"))
1308     {
1309     /* :jilles ENCAP * LOGIN jilles */
1310     if (!use_rserv_support)
1311     return;
1312     if (parc < 3)
1313     return;
1314     u = si->su;
1315     if (u == NULL)
1316     return;
1317     /* We used to throw out LOGINs outside of a burst,
1318     * but we can't do that anymore since it's used for
1319     * SASL users.
1320     * --gxti
1321     */
1322     handle_burstlogin (u, parv[2]);
1323     }
1324     else if (!irccasecmp (parv[1], "REALHOST"))
1325     {
1326     /* :1JJAAAAAC ENCAP * REALHOST localhost.stack.nl */
1327     if (parc < 3)
1328     return;
1329     u = si->su;
1330     if (u == NULL)
1331     return;
1332     strlcpy (u->host, parv[2], HOSTLEN);
1333     }
1334     else if (!irccasecmp (parv[1], "CHGHOST"))
1335     {
1336     if (parc < 4)
1337     return;
1338     u = user_find (parv[2]);
1339     if (u == NULL)
1340     return;
1341     strlcpy (u->vhost, parv[3], HOSTLEN);
1342     slog (LG_DEBUG, "m_encap(): chghost %s -> %s", u->nick, u->vhost);
1343     }
1344     else if (!irccasecmp (parv[1], "SASL"))
1345     {
1346     /* :08C ENCAP * SASL 08CAAAAAE * S d29vTklOSkFTAGRhdGEgaW4gZmlyc3QgbGluZQ== */
1347     sasl_message_t smsg;
1348    
1349     if (parc < 6)
1350     return;
1351    
1352     smsg.uid = parv[2];
1353     smsg.mode = *parv[4];
1354     smsg.buf = parv[5];
1355     hook_call_event ("sasl_input", &smsg);
1356     }
1357     }
1358    
1359     static void
1360     m_signon (sourceinfo_t *si, int parc, char *parv[])
1361     {
1362     user_t *u;
1363     myuser_t *olduser, *newuser;
1364    
1365     if ((u = user_find (parv[0])) == NULL)
1366     return;
1367    
1368     /* NICK */
1369     user_changenick (u, parv[0], atoi (parv[3]));
1370    
1371     handle_nickchange (u); /* If they're logging out, this will bug them about identifying. Or something. */
1372    
1373     /* USER */
1374     strlcpy (u->user, parv[1], USERLEN);
1375    
1376     /* HOST */
1377     strlcpy (u->vhost, parv[2], HOSTLEN);
1378    
1379     /* LOGIN */
1380     if (*parv[4] == '*') /* explicitly unchanged */
1381     return;
1382     if (u->myuser == NULL && !strcmp (parv[4], "0")) /* both unset */
1383     return;
1384    
1385     olduser = u->myuser;
1386     newuser = myuser_find (parv[4]);
1387     if (olduser == newuser)
1388     return;
1389    
1390     if (olduser)
1391     {
1392     myuser_t::login_vector::iterator it, it_end;
1393     olduser->lastlogin = NOW;
1394     olduser->logins.erase (u);
1395     u->myuser = NULL;
1396     }
1397    
1398     if (newuser)
1399     {
1400     if (is_soper (newuser))
1401     snoop ("SOPER: \2%s\2 as \2%s\2", u->nick, newuser->name);
1402    
1403     myuser_notice (nicksvs.nick, newuser, "%s!%s@%s has just authenticated as you (%s)", u->nick, u->user, u->vhost, newuser->name);
1404    
1405     u->myuser = newuser;
1406     newuser->logins.insert (u);
1407     }
1408     }
1409    
1410     static void
1411     m_capab (sourceinfo_t *si, int parc, char *parv[])
1412     {
1413     char *p;
1414    
1415     use_euid = false;
1416     use_rserv_support = false;
1417     use_tb = false;
1418     for (p = strtok (parv[0], " "); p != NULL; p = strtok (NULL, " "))
1419     {
1420     if (!irccasecmp (p, "EUID"))
1421     {
1422     slog (LG_DEBUG, "m_capab(): uplink supports EUID, enabling support.");
1423     use_euid = true;
1424     }
1425     if (!irccasecmp (p, "SERVICES"))
1426     {
1427     slog (LG_DEBUG, "m_capab(): uplink has rserv extensions, enabling support.");
1428     use_rserv_support = true;
1429     }
1430     if (!irccasecmp (p, "TB"))
1431     {
1432     slog (LG_DEBUG, "m_capab(): uplink does topic bursting, using if appropriate.");
1433     use_tb = true;
1434     }
1435     }
1436    
1437     /* Now we know whether or not we should enable services support,
1438     * so burst the clients.
1439     * --nenolod
1440     */
1441     services_init ();
1442     }
1443    
1444     static void
1445     m_chghost (sourceinfo_t *si, int parc, char *parv[])
1446     {
1447     user_t *u = user_find (parv[0]);
1448    
1449     if (!u)
1450     return;
1451    
1452     strlcpy (u->vhost, parv[1], HOSTLEN);
1453     }
1454    
1455     static void
1456     m_motd (sourceinfo_t *si, int parc, char *parv[])
1457     {
1458     handle_motd (si->su);
1459     }
1460    
1461     /* Server ended their burst: warn all their users if necessary -- jilles */
1462     static void
1463     server_eob (server_t *s)
1464     {
1465     node_t *n;
1466    
1467     LIST_FOREACH (n, s->userlist.head)
1468     {
1469     handle_nickchange ((user_t *) n->data);
1470     }
1471     }
1472    
1473     static server_t *
1474     sid_find (char *name)
1475     {
1476     char sid[4];
1477     strlcpy (sid, name, 4);
1478     return server_find (sid);
1479     }
1480    
1481     void
1482     _modinit (module_t *m)
1483     {
1484     /* Symbol relocation voodoo. */
1485     server_login = &charybdis_server_login;
1486     introduce_nick = &charybdis_introduce_nick;
1487     quit_sts = &charybdis_quit_sts;
1488     wallops_sts = &charybdis_wallops_sts;
1489     join_sts = &charybdis_join_sts;
1490     chan_lowerts = &charybdis_chan_lowerts;
1491     kick = &charybdis_kick;
1492     msg = &charybdis_msg;
1493     notice_user_sts = &charybdis_notice_user_sts;
1494     notice_global_sts = &charybdis_notice_global_sts;
1495     notice_channel_sts = &charybdis_notice_channel_sts;
1496     wallchops = &charybdis_wallchops;
1497     numeric_sts = &charybdis_numeric_sts;
1498     skill = &charybdis_skill;
1499     part_sts = &charybdis_part_sts;
1500     kline_sts = &charybdis_kline_sts;
1501     unkline_sts = &charybdis_unkline_sts;
1502     topic_sts = &charybdis_topic_sts;
1503     mode_sts = &charybdis_mode_sts;
1504     ping_sts = &charybdis_ping_sts;
1505     ircd_on_login = &charybdis_on_login;
1506     ircd_on_logout = &charybdis_on_logout;
1507     jupe = &charybdis_jupe;
1508     sethost_sts = &charybdis_sethost_sts;
1509     fnc_sts = &charybdis_fnc_sts;
1510     invite_sts = &charybdis_invite_sts;
1511     holdnick_sts = &charybdis_holdnick_sts;
1512     svslogin_sts = &charybdis_svslogin_sts;
1513     sasl_sts = &charybdis_sasl_sts;
1514     next_matching_ban = &charybdis_next_matching_ban;
1515    
1516     mode_list = charybdis_mode_list;
1517     ignore_mode_list = charybdis_ignore_mode_list;
1518     status_mode_list = charybdis_status_mode_list;
1519     prefix_mode_list = charybdis_prefix_mode_list;
1520    
1521     ircd = &Charybdis;
1522    
1523     pcommand_add ("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
1524     pcommand_add ("PONG", m_pong, 1, MSRC_SERVER);
1525     pcommand_add ("PRIVMSG", m_privmsg, 2, MSRC_USER);
1526     pcommand_add ("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
1527     pcommand_add ("SJOIN", m_sjoin, 4, MSRC_SERVER);
1528     pcommand_add ("PART", m_part, 1, MSRC_USER);
1529     pcommand_add ("NICK", m_nick, 2, MSRC_USER | MSRC_SERVER);
1530     pcommand_add ("QUIT", m_quit, 1, MSRC_USER);
1531     pcommand_add ("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
1532     pcommand_add ("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
1533     pcommand_add ("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
1534     pcommand_add ("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
1535     pcommand_add ("SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER);
1536     pcommand_add ("STATS", m_stats, 2, MSRC_USER);
1537     pcommand_add ("ADMIN", m_admin, 1, MSRC_USER);
1538     pcommand_add ("VERSION", m_version, 1, MSRC_USER);
1539     pcommand_add ("INFO", m_info, 1, MSRC_USER);
1540     pcommand_add ("WHOIS", m_whois, 2, MSRC_USER);
1541     pcommand_add ("TRACE", m_trace, 1, MSRC_USER);
1542     pcommand_add ("AWAY", m_away, 0, MSRC_USER);
1543     pcommand_add ("JOIN", m_join, 1, MSRC_USER);
1544     pcommand_add ("PASS", m_pass, 1, MSRC_UNREG);
1545     pcommand_add ("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
1546     pcommand_add ("TOPIC", m_topic, 2, MSRC_USER);
1547     pcommand_add ("TB", m_tb, 3, MSRC_SERVER);
1548     pcommand_add ("ENCAP", m_encap, 2, MSRC_USER | MSRC_SERVER);
1549     pcommand_add ("CAPAB", m_capab, 1, MSRC_UNREG);
1550     pcommand_add ("UID", m_uid, 9, MSRC_SERVER);
1551     pcommand_add ("BMASK", m_bmask, 4, MSRC_SERVER);
1552     pcommand_add ("TMODE", m_tmode, 3, MSRC_USER | MSRC_SERVER);
1553     pcommand_add ("SID", m_sid, 4, MSRC_SERVER);
1554     pcommand_add ("CHGHOST", m_chghost, 2, MSRC_USER | MSRC_SERVER);
1555     pcommand_add ("MOTD", m_motd, 1, MSRC_USER);
1556     pcommand_add ("SIGNON", m_signon, 5, MSRC_USER);
1557     pcommand_add ("EUID", m_euid, 11, MSRC_SERVER);
1558    
1559     hook_add_event ("server_eob");
1560     hook_add_hook ("server_eob", (void (*)(void *)) server_eob);
1561    
1562     m->mflags = MODTYPE_CORE;
1563    
1564     pmodule_loaded = true;
1565     }