ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/charybdis.C
Revision: 1.10
Committed: Sat Sep 22 14:27:28 2007 UTC (19 years ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +8 -12 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

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