ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/hyperion.C
Revision: 1.3
Committed: Sat Jul 21 13:23:21 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.2: +2 -2 lines
Log Message:
- added rcsid to some files
- more documentation tweaks
- made most protocol commands local to phandler.C
- added ircd metadata (inspircd only for now)
- added inspircd swhois support

File Contents

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