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

File Contents

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