ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/hyperion.C
Revision: 1.9
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.8: +6 -2 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /**
2 * hyperion.C: This file contains protocol support for hyperion-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 © 2005-2007 William Pitcock, et al.
10 * Rights to this code are documented in doc/pod/license.pod.
11 *
12 * $Id: hyperion.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $
13 */
14
15 /* option: use SVSLOGIN/SIGNON to remember users even if they're
16 * not logged in to their current nick, etc
17 */
18 #define USE_SVSLOGIN
19
20 #include <boost/foreach.hpp>
21
22 #include "atheme.h"
23 #include <util/time.h>
24 #include <libermyth.h>
25 #include "servers.h"
26 #include <account/mychan.h>
27 #include <account/mynick.h>
28 #include <account/myuser.h>
29 #include "uplink.h"
30 #include "pmodule.h"
31 #include "protocol/hyperion.h"
32
33 static char const rcsid[] = "$Id: hyperion.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $";
34
35 /* *INDENT-OFF* */
36
37 namespace protocol
38 {
39 static ircd_t Hyperion = {
40 "Hyperion 1.0", /* IRCd name */
41 "$", /* TLD Prefix, used by Global. */
42 false, /* Whether or not we use IRCNet/TS6 UID */
43 false, /* Whether or not we use RCOMMAND */
44 false, /* Whether or not we support channel owners. */
45 false, /* Whether or not we support channel protection. */
46 false, /* Whether or not we support halfops. */
47 false, /* Whether or not we use P10 */
48 true, /* Whether or not we use vHosts. */
49 CMODE_EXLIMIT | CMODE_PERM | CMODE_JUPED, /* Oper-only cmodes */
50 0, /* Integer flag for owner channel flag. */
51 0, /* Integer flag for protect channel flag. */
52 0, /* Integer flag for halfops. */
53 "+", /* Mode we set for owner. */
54 "+", /* Mode we set for protect. */
55 "+", /* Mode we set for halfops. */
56 PROTOCOL_HYPERION, /* Protocol type */
57 CMODE_PERM | CMODE_JUPED, /* Permanent cmodes */
58 "beIqd", /* Ban-like cmodes */
59 'e', /* Except mchar */
60 'I', /* Invex mchar */
61 0 /* Flags */
62 };
63
64 struct hyperion_handler : handler
65 {
66 hyperion_handler ();
67 virtual ~hyperion_handler ();
68
69 virtual unsigned int server_login (void);
70 virtual void introduce_nick (user_t *u);
71 virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
72 virtual void quit_sts (user_t *u, char const * const reason);
73 virtual void wallops_sts (char const * const text);
74 virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
75 virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
76 virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
77 virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
78 virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
79 virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
80 virtual void wallchops (user_t *sender, channel_t *channel, char const * const message);
81 virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
82 virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
83 virtual void part_sts (channel_t *c, user_t *u);
84 virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
85 virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
86 virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
87 virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
88 virtual void ping_sts (void);
89 virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
90 virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
91 virtual void jupe (char const * const server, char const * const reason);
92 virtual void sethost_sts (char const * const source, char const * const target, char const * const host);
93 virtual void fnc_sts (user_t *source, user_t *u, char const * const newnick, int type);
94 };
95
96 static cmode_t hyperion_mode_list[] = {
97 { 'i', CMODE_INVITE },
98 { 'm', CMODE_MOD },
99 { 'n', CMODE_NOEXT },
100 { 's', CMODE_SEC },
101 { 't', CMODE_TOPIC },
102 { 'c', CMODE_NOCOLOR},
103 { 'r', CMODE_REGONLY},
104 { 'R', CMODE_MODREG },
105 { 'z', CMODE_OPMOD },
106 { 'g', CMODE_FINVITE},
107 { 'L', CMODE_EXLIMIT},
108 { 'P', CMODE_PERM },
109 { 'j', CMODE_JUPED },
110 { 'Q', CMODE_DISFWD },
111 { '\0', 0 }
112 };
113
114 static bool check_forward(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
115 static bool check_jointhrottle(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
116
117 static extmode_t hyperion_ignore_mode_list[] = {
118 /*{ 'D', 0 },*/
119 { 'f', check_forward },
120 { 'J', check_jointhrottle },
121 { '\0', 0 }
122 };
123
124 static cmode_t hyperion_status_mode_list[] = {
125 { 'o', CMODE_OP },
126 { 'v', CMODE_VOICE },
127 { '\0', 0 }
128 };
129
130 static cmode_t hyperion_prefix_mode_list[] = {
131 { '@', CMODE_OP },
132 { '+', CMODE_VOICE },
133 { '\0', 0 }
134 };
135
136 static bool use_svslogin = false;
137
138 /* *INDENT-ON* */
139
140 static bool
141 check_forward (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
142 {
143 channel_t *target_c;
144 mychan_t *target_mc;
145
146 if (*value != '#' || strlen (value) > 30)
147 return false;
148 if (u == NULL && mu == NULL)
149 return true;
150 target_c = channel_find (value);
151 target_mc = mychan_t::find (value);
152 if (target_c == NULL && target_mc == NULL)
153 return false;
154 return true;
155 }
156
157 static bool
158 check_jointhrottle (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
159 {
160 char const *p = value;
161 char const *arg2 = NULL;
162
163 while (*p != '\0')
164 {
165 if (*p == ',')
166 {
167 if (arg2 != NULL)
168 return false;
169 arg2 = p + 1;
170 }
171 else if (!isdigit (*p))
172 return false;
173 p++;
174 }
175 if (arg2 == NULL)
176 return false;
177 if (p - arg2 > 5 || arg2 - value - 1 > 5 || !atoi (value) || !atoi (arg2))
178 return false;
179 return true;
180 }
181
182 node_t *
183 hyperion_next_matching_ban (channel_t *c, user_t *u, int type, node_t *first)
184 {
185 chanban_t *cb;
186 node_t *n;
187 char hostbuf[NICKLEN + USERLEN + HOSTLEN];
188 char realbuf[NICKLEN + USERLEN + HOSTLEN];
189 char ipbuf[NICKLEN + USERLEN + HOSTLEN];
190
191 snprintf (hostbuf, sizeof hostbuf, "%s!%s@%s", u->nick, u->user, u->vhost);
192 snprintf (realbuf, sizeof realbuf, "%s!%s@%s", u->nick, u->user, u->host);
193 /* will be nick!user@ if ip unknown, doesn't matter */
194 snprintf (ipbuf, sizeof ipbuf, "%s!%s@%s", u->nick, u->user, u->ip);
195 LIST_FOREACH (n, first)
196 {
197 cb = static_cast<chanban_t *> (n->data);
198
199 if (cb->type == type && (!match (cb->mask, hostbuf) || !match (cb->mask, realbuf) || !match (cb->mask, ipbuf)))
200 return n;
201 if (cb->type == 'd' && type == 'b' && !match (cb->mask, u->gecos))
202 return n;
203 }
204 return NULL;
205 }
206
207 /* login to our uplink */
208 unsigned int
209 hyperion_handler::server_login (void)
210 {
211 int ret;
212
213 ret = sts ("PASS %s :TS", curr_uplink->pass);
214 if (ret == 1)
215 return 1;
216
217 me.bursting = true;
218
219 sts ("CAPAB :QS EX DE CHW IE QU DNCR SRV SIGNON");
220 sts ("SERVER %s 1 :%s", me.name, me.desc);
221 sts ("SVINFO 5 3 0 :%ld", NOW);
222
223 services_init ();
224
225 return 0;
226 }
227
228 /* introduce a nick.
229 * Hyperion sucks because it has to do things differently.
230 * Thus we have to make all clients opered, because there is no real way to get
231 * around the problem.
232 *
233 * Protocol information ripped from theia/dancer-services (Hybserv2 TS of some sort).
234 * -- nenolod
235 */
236 void
237 hyperion_handler::introduce_nick (user_t *u)
238 {
239 char const * const privs = "6@BFmMopPRUX";
240 sts ("NICK %s 1 %ld +ei%s %s %s %s 0.0.0.0 :%s", u->nick, u->ts, privs, u->user, u->host, me.name, u->gecos);
241 sts (":%s OPER %s +%s", me.name, u->nick, privs);
242 }
243
244 /* invite a user to a channel */
245 void
246 hyperion_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
247 {
248 sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
249 }
250
251 void
252 hyperion_handler::quit_sts (user_t *u, char const * const reason)
253 {
254 if (!me.connected)
255 return;
256
257 sts (":%s QUIT :%s", u->nick, reason);
258 }
259
260 /* WALLOPS wrapper */
261 void
262 hyperion_handler::wallops_sts (char const * const text)
263 {
264 /* Generate +s server notice -- jilles */
265 sts (":%s WALLOPS 1-0 :%s", me.name, text);
266 }
267
268 /* join a channel */
269 void
270 hyperion_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
271 {
272 if (isnew)
273 sts (":%s SJOIN %ld %s %s :@%s", me.name, c->ts, c->name, modes, u->nick);
274 else
275 sts (":%s SJOIN %ld %s + :@%s", me.name, c->ts, c->name, u->nick);
276 }
277
278 /* kicks a user from a channel */
279 void
280 hyperion_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
281 {
282 channel_t *chan = channel_find (channel);
283 user_t *user = user_find (to);
284
285 if (!chan || !user)
286 return;
287
288 sts (":%s KICK %s %s :%s", from, channel, to, reason);
289
290 chanuser_delete (chan, user);
291 }
292
293 /* PRIVMSG wrapper */
294 void
295 hyperion_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
296 {
297 va_list ap;
298 char buf[BUFSIZE];
299
300 va_start (ap, fmt);
301 vsnprintf (buf, BUFSIZE, fmt, ap);
302 va_end (ap);
303
304 sts (":%s PRIVMSG %s :%s", from, target, buf);
305 }
306
307 /* NOTICE wrapper */
308 void
309 hyperion_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
310 {
311 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
312 }
313
314 void
315 hyperion_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
316 {
317 sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
318 }
319
320 void
321 hyperion_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
322 {
323 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
324 }
325
326 void
327 hyperion_handler::wallchops (user_t *sender, channel_t *channel, char const * const message)
328 {
329 /* +p does not grant the ability to send to @#channel (!) */
330 if (chanuser_find (channel, sender))
331 sts (":%s NOTICE @%s :%s", CLIENT_NAME (sender), channel->name, message);
332 else /* do not join for this, everyone would see -- jilles */
333 handler::wallchops (sender, channel, message);
334 }
335
336 /* numeric wrapper */
337 void
338 hyperion_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
339 {
340 va_list ap;
341 char buf[BUFSIZE];
342
343 va_start (ap, fmt);
344 vsnprintf (buf, BUFSIZE, fmt, ap);
345 va_end (ap);
346
347 sts (":%s %d %s %s", from, numeric, target, buf);
348 }
349
350 /* KILL wrapper */
351 void
352 hyperion_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
353 {
354 va_list ap;
355 char buf[BUFSIZE];
356
357 va_start (ap, fmt);
358 vsnprintf (buf, BUFSIZE, fmt, ap);
359 va_end (ap);
360
361 #if 0
362 sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
363 #else
364 /* Use COLLIDE to cut down on server notices.
365 * The current version of hyperion does not do COLLIDE reasons,
366 * so fake it.
367 */
368 sts (":%s NOTICE %s :*** Disconnecting you (%s (%s))", me.name, nick, from, buf);
369 sts (":%s COLLIDE %s :(%s)", me.name, nick, buf);
370 #endif
371 }
372
373 /* PART wrapper */
374 void
375 hyperion_handler::part_sts (channel_t *c, user_t *u)
376 {
377 sts (":%s PART %s", u->nick, c->name);
378 }
379
380 /* server-to-server KLINE wrapper */
381 void
382 hyperion_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
383 {
384 if (!me.connected)
385 return;
386
387 if (duration)
388 sts (":%s KLINE %s %ld %s@%s :%s", me.name, me.name, duration > 60 ? (duration / 60) : 1, user, host, reason);
389 else
390 sts (":%s KLINE %s %s@%s :%s", me.name, me.name, user, host, reason);
391 }
392
393 /* server-to-server UNKLINE wrapper */
394 void
395 hyperion_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
396 {
397 if (!me.connected)
398 return;
399
400 sts (":%s UNKLINE %s@%s %ld", opersvs.nick, user, host, NOW);
401 }
402
403 /* topic wrapper */
404 void
405 hyperion_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
406 {
407 if (!me.connected || !c)
408 return;
409
410 /* Send 0 channelts so this will always be accepted */
411 sts (":%s STOPIC %s %s %ld 0 :%s", chansvs.nick, c->name, setter, ts, topic);
412 }
413
414 /* mode wrapper */
415 void
416 hyperion_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
417 {
418 if (!me.connected)
419 return;
420
421 sts (":%s MODE %s %s", sender, target->name, modes);
422 }
423
424 /* ping wrapper */
425 void
426 hyperion_handler::ping_sts (void)
427 {
428 if (!me.connected)
429 return;
430
431 sts ("PING :%s", me.name);
432 }
433
434 /* protocol-specific stuff to do on login */
435 void
436 hyperion_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
437 {
438 user_t *u;
439
440 if (!me.connected)
441 return;
442
443 u = user_find (origin);
444 if (!u)
445 return;
446 if (use_svslogin)
447 sts (":%s SVSLOGIN %s %s %s %s %s %s", me.name, u->server->name, origin, user, origin, u->user, wantedhost ? wantedhost : u->vhost);
448 /* we'll get a SIGNON confirming the changes later, no need
449 * to change the fields yet */
450
451 /* set +e if they're identified to the nick they are using */
452 if (should_reg_umode (u))
453 sts (":%s MODE %s +e", me.name, origin);
454 }
455
456 /* protocol-specific stuff to do on login */
457 bool
458 hyperion_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
459 {
460 user_t *u;
461
462 if (!me.connected)
463 return false;
464
465 u = user_find (origin);
466 if (!u)
467 return false;
468 if (use_svslogin)
469 sts (":%s SVSLOGIN %s %s %s %s %s %s", me.name, u->server->name, origin, "0", origin, u->user, wantedhost ? u->host : u->vhost);
470
471 if (!nicksvs.no_nick_ownership)
472 sts (":%s MODE %s -e", me.name, origin);
473
474 return false;
475 }
476
477 void
478 hyperion_handler::jupe (char const * const server, char const * const reason)
479 {
480 if (!me.connected)
481 return;
482
483 server_delete (server);
484 sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
485 sts (":%s SERVER %s 2 :%s", me.name, server, reason);
486 }
487
488 void
489 hyperion_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
490 {
491 if (!me.connected)
492 return;
493
494 sts (":%s SETHOST %s :%s", source, target, host);
495 }
496
497 void
498 hyperion_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
499 {
500 /* XXX this should be combined with the SVSLOGIN to set login id
501 * and SETHOST, if any -- jilles */
502 sts (":%s SVSLOGIN %s %s %s %s %s %s", me.name, u->server->name, u->nick, u->myuser ? u->myuser->name : "0", newnick, u->user, u->vhost);
503 }
504
505 static void
506 m_topic (sourceinfo_t *si, int parc, char *parv[])
507 {
508 channel_t *c = channel_find (parv[0]);
509
510 if (!c || !si->su)
511 return;
512
513 handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
514 }
515
516 static void
517 m_stopic (sourceinfo_t *si, int parc, char *parv[])
518 {
519 channel_t *c = channel_find (parv[0]);
520 time_t channelts;
521 time_t topicts;
522
523 if (c == NULL)
524 return;
525
526 /* Our uplink is trying to change the topic during burst,
527 * and we have already set a topic. Assume our change won.
528 * -- jilles */
529 if (si->s != NULL && si->s->uplink == me.me && !(si->s->flags & SF_EOB) && c->topic != NULL)
530 return;
531
532 /* hyperion will propagate an STOPIC even if it's not applied
533 * locally :( */
534 topicts = atol (parv[2]);
535 channelts = atol (parv[3]);
536 if (c->topic == NULL || ((strcmp (c->topic, parv[1]) && channelts < c->ts) || (channelts == c->ts && topicts > c->topicts)))
537 handle_topic_from (si, c, parv[1], topicts, parv[parc - 1]);
538 }
539
540 static void
541 m_ping (sourceinfo_t *si, int parc, char *parv[])
542 {
543 /* reply to PING's */
544 sts (":%s PONG %s %s", me.name, me.name, parv[0]);
545 }
546
547 static void
548 m_pong (sourceinfo_t *si, int parc, char *parv[])
549 {
550 server_t *s;
551
552 /* someone replied to our PING */
553 if (!parv[0])
554 return;
555 s = server_find (parv[0]);
556 if (s == NULL)
557 return;
558 handle_eob (s);
559
560 if (irccasecmp (me.actual, parv[0]))
561 return;
562
563 me.uplinkpong = NOW;
564
565 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
566 if (me.bursting)
567 {
568 #ifdef HAVE_GETTIMEOFDAY
569 e_time (burstime, &burstime);
570
571 slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
572
573 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
574 #else
575 slog (LG_INFO, "m_pong(): finished synching with uplink");
576 wallops ("Finished synching to network.");
577 #endif
578
579 me.bursting = false;
580 }
581 }
582
583 static void
584 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
585 {
586 if (parc != 2)
587 return;
588
589 handle_message (si, parv[0], false, parv[1]);
590 }
591
592 static void
593 m_notice (sourceinfo_t *si, int parc, char *parv[])
594 {
595 if (parc != 2)
596 return;
597
598 handle_message (si, parv[0], true, parv[1]);
599 }
600
601 static void
602 m_sjoin (sourceinfo_t *si, int parc, char *parv[])
603 {
604 /* -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur */
605
606 channel_t *c;
607 unsigned int userc;
608 char *userv[256];
609 unsigned int i;
610 time_t ts;
611
612 /* :origin SJOIN ts chan modestr [key or limits] :users */
613 c = channel_find (parv[1]);
614 ts = atol (parv[0]);
615
616 if (!c)
617 {
618 slog (LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
619 c = channel_add (parv[1], ts, si->s);
620 }
621
622 if (ts < c->ts)
623 {
624 chanuser_t *cu;
625 node_t *n;
626
627 /* the TS changed. a TS change requires the following things
628 * to be done to the channel: reset all modes to nothing, remove
629 * all status modes on known users on the channel (including ours),
630 * and set the new TS.
631 */
632
633 clear_simple_modes (c);
634
635 LIST_FOREACH (n, c->members.head)
636 {
637 cu = (chanuser_t *) n->data;
638 if (cu->user->server == me.me)
639 {
640 /* it's a service, reop */
641 sts (":%s MODE %s +o %s", cu->user->nick, c->name, cu->user->nick);
642 cu->modes = CMODE_OP;
643 }
644 else
645 cu->modes = 0;
646 }
647
648 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
649
650 c->ts = ts;
651 c->callback.tschange (c);
652 }
653
654 channel_mode (NULL, c, parc - 3, parv + 2);
655
656 userc = sjtoken (parv[parc - 1], ' ', userv);
657
658 for (i = 0; i < userc; i++)
659 chanuser_add (c, userv[i]);
660
661 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
662 channel_delete (c);
663 }
664
665 static void
666 m_part (sourceinfo_t *si, int parc, char *parv[])
667 {
668 int chanc;
669 char *chanv[256];
670 int i;
671
672 chanc = sjtoken (parv[0], ',', chanv);
673 for (i = 0; i < chanc; i++)
674 {
675 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
676
677 chanuser_delete (channel_find (chanv[i]), si->su);
678 }
679 }
680
681 static void
682 m_nick (sourceinfo_t *si, int parc, char *parv[])
683 {
684 server_t *s;
685 user_t *u;
686 bool realchange;
687
688 /* got the right number of args for an introduction? */
689 if (parc == 9)
690 {
691 s = server_find (parv[6]);
692 if (!s)
693 {
694 slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
695 return;
696 }
697
698 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
699
700 u = user_add (parv[0], parv[4], parv[5], NULL, parv[7], NULL, parv[8], s, atoi (parv[2]));
701
702 user_mode (u, parv[3]);
703
704 /* umode +e: identified to current nick */
705 /* As hyperion clears +e on nick changes, this is safe. */
706 if (!use_svslogin && strchr (parv[3], 'e'))
707 handle_burstlogin (u, NULL);
708
709 /* if the server supports SIGNON, we will get an SNICK
710 * for this user, potentially with a login name
711 */
712 if (!use_svslogin)
713 handle_nickchange (u);
714 }
715
716 /* if it's only 2 then it's a nickname change */
717 else if (parc == 2)
718 {
719 if (!si->su)
720 {
721 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
722 return;
723 }
724
725 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
726
727 realchange = irccasecmp (si->su->nick, parv[0]);
728
729 user_changenick (si->su, parv[0], atoi (parv[1]));
730
731 /* fix up +e if necessary -- jilles */
732 if (realchange && should_reg_umode (si->su))
733 /* changed nick to registered one, reset +e */
734 sts (":%s MODE %s +e", me.name, parv[0]);
735
736 handle_nickchange (si->su);
737 }
738 else
739 {
740 int i;
741 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
742
743 for (i = 0; i < parc; i++)
744 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
745 }
746 }
747
748 static void
749 m_quit (sourceinfo_t *si, int parc, char *parv[])
750 {
751 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
752
753 /* user_delete() takes care of removing channels and so forth */
754 user_delete (si->su);
755 }
756
757 static void
758 m_mode (sourceinfo_t *si, int parc, char *parv[])
759 {
760 if (*parv[0] == '#')
761 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
762 else
763 user_mode (user_find (parv[0]), parv[1]);
764 }
765
766 static void
767 m_kick (sourceinfo_t *si, int parc, char *parv[])
768 {
769 user_t *u = user_find (parv[1]);
770 channel_t *c = channel_find (parv[0]);
771
772 /* -> :rakaur KICK #shrike rintaun :test */
773 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
774
775 if (!u)
776 {
777 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
778 return;
779 }
780
781 if (!c)
782 {
783 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
784 return;
785 }
786
787 if (!chanuser_find (c, u))
788 {
789 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
790 return;
791 }
792
793 chanuser_delete (c, u);
794
795 /* if they kicked us, let's rejoin */
796 if (is_internal_client (u))
797 {
798 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
799 join (parv[0], u->nick);
800 }
801 }
802
803 static void
804 m_kill (sourceinfo_t *si, int parc, char *parv[])
805 {
806 /* serves for both KILL and COLLIDE
807 * COLLIDE only originates from servers and may not have
808 * a reason field, but the net effect is identical
809 * -- jilles */
810 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
811 }
812
813 static void
814 m_squit (sourceinfo_t *si, int parc, char *parv[])
815 {
816 slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
817 server_delete (parv[0]);
818 }
819
820 static void
821 m_server (sourceinfo_t *si, int parc, char *parv[])
822 {
823 server_t *s;
824
825 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
826 s = handle_server (si, parv[0], NULL, atoi (parv[1]), parv[2]);
827
828 if (s != NULL && s->uplink != me.me)
829 {
830 /* elicit PONG for EOB detection; pinging uplink is
831 * already done elsewhere -- jilles
832 */
833 sts (":%s PING %s %s", me.name, me.name, s->name);
834 }
835 }
836
837 static void
838 m_stats (sourceinfo_t *si, int parc, char *parv[])
839 {
840 handle_stats (si->su, parv[0][0]);
841 }
842
843 static void
844 m_admin (sourceinfo_t *si, int parc, char *parv[])
845 {
846 handle_admin (si->su);
847 }
848
849 static void
850 m_version (sourceinfo_t *si, int parc, char *parv[])
851 {
852 handle_version (si->su);
853 }
854
855 static void
856 m_info (sourceinfo_t *si, int parc, char *parv[])
857 {
858 handle_info (si->su);
859 }
860
861 static void
862 m_whois (sourceinfo_t *si, int parc, char *parv[])
863 {
864 handle_whois (si->su, parv[1]);
865 }
866
867 static void
868 m_trace (sourceinfo_t *si, int parc, char *parv[])
869 {
870 handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
871 }
872
873 static void
874 m_away (sourceinfo_t *si, int parc, char *parv[])
875 {
876 handle_away (si->su, parc >= 1 ? parv[0] : NULL);
877 }
878
879 static void
880 m_join (sourceinfo_t *si, int parc, char *parv[])
881 {
882 chanuser_t *cu;
883 node_t *n, *tn;
884
885 /* JOIN 0 is really a part from all channels */
886 if (parv[0][0] == '0')
887 {
888 LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
889 {
890 cu = (chanuser_t *) n->data;
891 chanuser_delete (cu->chan, si->su);
892 }
893 }
894 }
895
896 static void
897 m_pass (sourceinfo_t *si, int parc, char *parv[])
898 {
899 if (strcmp (curr_uplink->pass, parv[0]))
900 {
901 slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
902 runflags |= RF_SHUTDOWN;
903 }
904 }
905
906 static void
907 m_error (sourceinfo_t *si, int parc, char *parv[])
908 {
909 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
910 }
911
912 static void
913 m_capab (sourceinfo_t *si, int parc, char *parv[])
914 {
915 char *p;
916
917 #ifdef USE_SVSLOGIN
918 for (p = strtok (parv[0], " "); p != NULL; p = strtok (NULL, " "))
919 {
920 if (!irccasecmp (p, "SIGNON"))
921 {
922 slog (LG_DEBUG, "m_capab(): uplink can do SIGNON, enabling support.");
923 use_svslogin = true;
924 }
925 }
926 #endif
927 }
928
929 static void
930 m_snick (sourceinfo_t *si, int parc, char *parv[])
931 {
932 user_t *u;
933
934 /* SNICK <nick> <orignick> <spoofhost> <firsttime> <dnshost> <servlogin> */
935 u = user_find (parv[0]);
936
937 if (!u)
938 return;
939
940 if (strcmp (u->host, parv[2])) /* User is not using spoofhost, assume no I:line spoof */
941 {
942 strlcpy (u->vhost, parv[4], HOSTLEN);
943 }
944
945 if (use_svslogin)
946 {
947 if (parc >= 6)
948 if (strcmp (parv[5], "0"))
949 handle_burstlogin (u, parv[5]);
950
951 handle_nickchange (u);
952 }
953 }
954
955 static void
956 m_sethost (sourceinfo_t *si, int parc, char *parv[])
957 {
958 user_t *u;
959
960 /* SETHOST <nick> <newhost> */
961 u = user_find (parv[0]);
962
963 if (!u)
964 return;
965
966 strlcpy (u->vhost, parv[1], HOSTLEN);
967 }
968
969 static void
970 m_setident (sourceinfo_t *si, int parc, char *parv[])
971 {
972 user_t *u;
973
974 /* SETHOST <nick> <newident> */
975 u = user_find (parv[0]);
976
977 if (!u)
978 return;
979
980 strlcpy (u->user, parv[1], USERLEN);
981 }
982
983 static void
984 m_setname (sourceinfo_t *si, int parc, char *parv[])
985 {
986 user_t *u;
987
988 /* SETNAME <nick> <newreal> */
989 u = user_find (parv[0]);
990
991 if (!u)
992 return;
993
994 strlcpy (u->gecos, parv[1], GECOSLEN);
995 }
996
997 /*
998 * Make broadcasted changes to a user's login id, nick, user, and hostname.
999 *
1000 * parv[0] = login id
1001 * parv[1] = new nick
1002 * parv[2] = new ident
1003 * parv[3] = new hostname
1004 * parv[4] = ts
1005 */
1006 static void
1007 m_signon (sourceinfo_t *si, int parc, char *parv[])
1008 {
1009 char *nick_parv[2];
1010
1011 slog (LG_DEBUG, "m_signon(): signon %s -> %s!%s@%s (login %s)", si->su->nick, parv[1], parv[2], parv[3], parv[0]);
1012
1013 strlcpy (si->su->user, parv[2], USERLEN);
1014 strlcpy (si->su->vhost, parv[3], HOSTLEN);
1015 nick_parv[0] = parv[1];
1016 nick_parv[1] = parv[4];
1017 if (strcmp (si->su->nick, parv[1]))
1018 m_nick (si, 2, nick_parv);
1019 /* don't use login id, assume everyone signs in via the services */
1020 }
1021
1022 static void
1023 m_motd (sourceinfo_t *si, int parc, char *parv[])
1024 {
1025 handle_motd (si->su);
1026 }
1027
1028 static void
1029 nick_group (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
1030 {
1031 user_t *u;
1032
1033 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
1034 u = si->su;
1035 else
1036 u = user_find_named (mn->nick);
1037
1038 if (u != NULL && should_reg_umode (u))
1039 sts (":%s MODE %s +e", me.name, u->nick);
1040 }
1041
1042 static void
1043 nick_ungroup (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
1044 {
1045 user_t *u;
1046
1047 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
1048 u = si->su;
1049 else
1050 u = user_find_named (mn->nick);
1051
1052 if (u != NULL && !nicksvs.no_nick_ownership)
1053 sts (":%s MODE %s -e", me.name, u->nick);
1054 }
1055
1056 static pcommand_t const pcommands[] = {
1057 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
1058 { "PONG", m_pong, 1, MSRC_SERVER },
1059 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
1060 { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
1061 { "SJOIN", m_sjoin, 4, MSRC_SERVER },
1062 { "PART", m_part, 1, MSRC_USER },
1063 { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
1064 { "QUIT", m_quit, 1, MSRC_USER },
1065 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
1066 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
1067 { "REMOVE", m_kick, 2, MSRC_USER | MSRC_SERVER }, /* same net result */
1068 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
1069 { "COLLIDE", m_kill, 1, MSRC_SERVER }, /* same net result */
1070 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
1071 { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
1072 { "STATS", m_stats, 2, MSRC_USER },
1073 { "ADMIN", m_admin, 1, MSRC_USER },
1074 { "VERSION", m_version, 1, MSRC_USER },
1075 { "INFO", m_info, 1, MSRC_USER },
1076 { "WHOIS", m_whois, 2, MSRC_USER },
1077 { "TRACE", m_trace, 1, MSRC_USER },
1078 { "AWAY", m_away, 0, MSRC_USER },
1079 { "JOIN", m_join, 1, MSRC_USER },
1080 { "PASS", m_pass, 1, MSRC_UNREG },
1081 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1082 { "TOPIC", m_topic, 2, MSRC_USER },
1083 { "STOPIC", m_stopic, 5, MSRC_USER | MSRC_SERVER },
1084 { "SNICK", m_snick, 5, MSRC_SERVER },
1085 { "SETHOST", m_sethost, 2, MSRC_USER | MSRC_SERVER },
1086 { "SETIDENT", m_setident, 2, MSRC_USER | MSRC_SERVER },
1087 { "SETNAME", m_setname, 2, MSRC_USER | MSRC_SERVER },
1088 { "SIGNON", m_signon, 5, MSRC_USER },
1089 { "CAPAB", m_capab, 1, MSRC_UNREG },
1090 { "MOTD", m_motd, 1, MSRC_USER },
1091 };
1092
1093 hyperion_handler::hyperion_handler ()
1094 {
1095 mode_list = hyperion_mode_list;
1096 ignore_mode_list = hyperion_ignore_mode_list;
1097 status_mode_list = hyperion_status_mode_list;
1098 prefix_mode_list = hyperion_prefix_mode_list;
1099
1100 ircd = &Hyperion;
1101
1102 pcommand_add (&pcommands);
1103
1104 mynick_t::callback.group.attach (nick_group);
1105 mynick_t::callback.ungroup.attach (nick_ungroup);
1106 }
1107
1108 hyperion_handler::~hyperion_handler ()
1109 {
1110 mode_list = NULL;
1111 ignore_mode_list = NULL;
1112 status_mode_list = NULL;
1113 prefix_mode_list = NULL;
1114
1115 ircd = NULL;
1116
1117 mynick_t::callback.group.detach (nick_group);
1118 mynick_t::callback.ungroup.detach (nick_ungroup);
1119
1120 pcommand_delete (&pcommands);
1121 }
1122 } // namespace protocol
1123
1124 #define FACREG_TYPE protocol::hyperion_handler
1125 #define FACREG_TYPE_NAME "hyperion"
1126 #define FACREG_INTERFACE_TYPE protocol::handler
1127 #include <ermyth/factory_reg.h>