ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/charybdis.C
Revision: 1.8
Committed: Sun Sep 9 20:05:52 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.7: +3 -2 lines
Log Message:
- changed configurations to the c++ stdlib
- more #defines to enum
- removed getopt.h and link.h from the system as they were unused
- reworked logstreams
- added an itoa with old syntax
- made klines objects
- moved some global variables into appropriate classes
- fixed boost.foreach's compiler workaround #if's
- allow other files to add exceptions with ADD_EXCEPTION
- changed mynick_t to c++ object
- moved servers.h out of atheme.h
- corrected PING from inspircd 1.2

File Contents

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