ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/charybdis.C
Revision: 1.4
Committed: Tue Aug 28 17:08:11 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.3: +1532 -1521 lines
Log Message:
- changed name
- updated the example config to the new system
- added more documentation
- enhanced documentation generators
- added a link to the pdf to the website
- added an RSS feed generator
- transitioned hooks to c++ callbacks
- did various merges with upstream along the way
- added const where appropriate
- removed the old block allocator
- fixed most memory leaks
- transitioned some dictionaries to std::map
- transitioned some lists to std::vector
- made some free functions members where appropriate
- renamed string to dynstr and added a static string ststr
- use NOW instead of time (NULL) if possible
- completely reworked database backends, crypto handlers and protocol handlers
  to use an object factory
- removed the old module system. ermyth does not do any dynamic loading anymore
- fixed most of the build system
- reworked how protocol commands work

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