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

File Contents

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