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

File Contents

# User Rev Content
1 pippijn 1.7 /**
2     * asuka.C: This file contains protocol support for P10 ircd's.
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 pippijn 1.4 * Copyright © 2005-2006 William Pitcock, et al.
10 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
11 pippijn 1.1 *
12 pippijn 1.8 * $Id: asuka.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $
13 pippijn 1.1 */
14    
15 pippijn 1.8 #include <boost/foreach.hpp>
16    
17 pippijn 1.1 #include "atheme.h"
18 pippijn 1.8 #include <util/time.h>
19     #include <libermyth.h>
20 pippijn 1.6 #include "servers.h"
21 pippijn 1.1 #include <account/myuser.h>
22     #include "uplink.h"
23     #include "pmodule.h"
24     #include "protocol/asuka.h"
25    
26 pippijn 1.8 static char const rcsid[] = "$Id: asuka.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $";
27 pippijn 1.1
28     /* *INDENT-OFF* */
29    
30 pippijn 1.4 namespace protocol
31 pippijn 1.1 {
32 pippijn 1.4 static ircd_t Asuka = {
33     "Asuka 1.2.1 and later", /* IRCd name */
34     "$", /* TLD Prefix, used by Global. */
35     true, /* Whether or not we use IRCNet/TS6 UID */
36     false, /* Whether or not we use RCOMMAND */
37     false, /* Whether or not we support channel owners. */
38     false, /* Whether or not we support channel protection. */
39     false, /* Whether or not we support halfops. */
40     true, /* Whether or not we use P10 */
41     true, /* Whether or not we use vhosts. */
42     0, /* Oper-only cmodes */
43     0, /* Integer flag for owner channel flag. */
44     0, /* Integer flag for protect channel flag. */
45     0, /* Integer flag for halfops. */
46     "+", /* Mode we set for owner. */
47     "+", /* Mode we set for protect. */
48     "+", /* Mode we set for halfops. */
49     PROTOCOL_ASUKA, /* Protocol type */
50     0, /* Permanent cmodes */
51     "b", /* Ban-like cmodes */
52     0, /* Except mchar */
53     0, /* Invex mchar */
54     IRCD_CIDR_BANS /* Flags */
55     };
56    
57     struct asuka_handler : handler
58     {
59     asuka_handler ();
60     virtual ~asuka_handler ();
61    
62     virtual unsigned int server_login (void);
63     virtual void introduce_nick (user_t *u);
64     virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
65     virtual void quit_sts (user_t *u, char const * const reason);
66     virtual void wallops_sts (char const * const text);
67     virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
68     virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
69     virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
70     virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
71     virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
72     virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
73     virtual void wallchops (user_t *sender, channel_t *channel, char const * const message);
74     virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
75     virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
76     virtual void part_sts (channel_t *c, user_t *u);
77     virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
78     virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
79     virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
80     virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
81     virtual void ping_sts (void);
82     virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
83     virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
84     virtual void jupe (char const * const server, char const * const reason);
85     };
86    
87     static cmode_t asuka_mode_list[] = {
88     { 'i', CMODE_INVITE },
89     { 'm', CMODE_MOD },
90     { 'n', CMODE_NOEXT },
91     { 'p', CMODE_PRIV },
92     { 's', CMODE_SEC },
93     { 't', CMODE_TOPIC },
94     { 'c', CMODE_NOCOLOR },
95     { 'C', CMODE_NOCTCP },
96     { 'D', CMODE_DELAYED },
97     { 'u', CMODE_NOQUIT },
98     { 'N', CMODE_NONOTICE },
99     { '\0', 0 }
100     };
101    
102 pippijn 1.5 static extmode_t asuka_ignore_mode_list[] = {
103 pippijn 1.4 { '\0', 0 }
104     };
105    
106     static cmode_t asuka_status_mode_list[] = {
107     { 'o', CMODE_OP },
108     { 'v', CMODE_VOICE },
109     { '\0', 0 }
110     };
111    
112     static cmode_t asuka_prefix_mode_list[] = {
113     { '@', CMODE_OP },
114     { '+', CMODE_VOICE },
115     { '\0', 0 }
116     };
117    
118     static void check_hidehost(user_t *u);
119    
120     /* *INDENT-ON* */
121    
122     /* login to our uplink */
123     unsigned int
124     asuka_handler::server_login (void)
125     {
126     int ret;
127    
128     ret = sts ("PASS :%s", curr_uplink->pass);
129     if (ret == 1)
130     return 1;
131    
132     me.bursting = true;
133    
134     /* SERVER irc.undernet.org 1 933022556 947908144 J10 AA]]] :[127.0.0.1] A Undernet Server */
135     sts ("SERVER %s 1 %ld %ld J10 %s]]] +s :%s", me.name, me.start, NOW, me.numeric, me.desc);
136    
137     services_init ();
138    
139     sts ("%s EB", me.numeric);
140    
141     return 0;
142     }
143    
144     /* introduce a client */
145     void
146     asuka_handler::introduce_nick (user_t *u)
147     {
148     sts ("%s N %s 1 %ld %s %s +%s%sk ]]]]]] %s :%s", me.numeric, u->nick, u->ts, u->user, u->host, "io", chansvs.fantasy ? "" : "d", u->uid, u->gecos);
149     }
150    
151     /* invite a user to a channel */
152     void
153     asuka_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
154     {
155     /* target is a nick, weird eh? -- jilles */
156     sts ("%s I %s %s", sender->uid, target->nick, channel->name);
157     }
158    
159     void
160     asuka_handler::quit_sts (user_t *u, char const * const reason)
161     {
162     if (!me.connected)
163     return;
164    
165     sts ("%s Q :%s", u->uid, reason);
166     }
167    
168     /* WALLOPS wrapper */
169     void
170     asuka_handler::wallops_sts (char const * const text)
171     {
172     sts ("%s WA :%s", me.numeric, text);
173     }
174    
175     /* join a channel */
176     void
177     asuka_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
178     {
179     /* If the channel doesn't exist, we need to create it. */
180     if (isnew)
181     {
182     sts ("%s C %s %ld", u->uid, c->name, c->ts);
183     if (modes[0] && modes[1])
184     sts ("%s M %s %s", u->uid, c->name, modes);
185     }
186     else
187     {
188     sts ("%s J %s %ld", u->uid, c->name, c->ts);
189     sts ("%s M %s +o %s", me.numeric, c->name, u->uid);
190     }
191     }
192    
193     /* kicks a user from a channel */
194     void
195     asuka_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
196     {
197     channel_t *chan = channel_find (channel);
198     user_t *fptr = user_find_named (from);
199     user_t *user = user_find_named (to);
200    
201     if (!chan || !user || !fptr)
202     return;
203    
204     if (chanuser_find (chan, fptr))
205     sts ("%s K %s %s :%s", fptr->uid, channel, user->uid, reason);
206     else
207     sts ("%s K %s %s :%s", me.numeric, channel, user->uid, reason);
208    
209     chanuser_delete (chan, user);
210     }
211    
212     /* PRIVMSG wrapper */
213     void
214     asuka_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
215     {
216     va_list ap;
217     user_t *u = user_find_named (from);
218     char buf[BUFSIZE];
219    
220     if (!u)
221     return;
222    
223     va_start (ap, fmt);
224     vsnprintf (buf, BUFSIZE, fmt, ap);
225     va_end (ap);
226    
227     sts ("%s P %s :%s", u->uid, target, buf);
228     }
229    
230     /* NOTICE wrapper */
231     void
232     asuka_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
233     {
234     sts ("%s O %s :%s", from ? from->uid : me.numeric, target->uid, text);
235     }
236    
237     void
238     asuka_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
239     {
240     if (!strcmp (mask, "*"))
241 pippijn 1.8 foreach (tld_t *tld, tld_t::list)
242     sts ("%s O %s*%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, tld->name, text);
243 pippijn 1.4 else
244     sts ("%s O %s%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, mask, text);
245     }
246    
247     void
248     asuka_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
249     {
250     if (target->modes & CMODE_NONOTICE)
251     {
252     /* asuka sucks */
253     /* remove that stupid +N mode before it blocks our notice
254     * -- jilles */
255     sts ("%s M %s -N", from ? from->uid : me.numeric, target->name);
256     target->modes &= ~CMODE_NONOTICE;
257     }
258     if (from == NULL || chanuser_find (target, from))
259     sts ("%s O %s :%s", from ? from->uid : me.numeric, target->name, text);
260     else
261     sts ("%s O %s :[%s:%s] %s", me.numeric, target->name, from->nick, target->name, text);
262     }
263    
264     void
265     asuka_handler::wallchops (user_t *sender, channel_t *channel, char const * const message)
266     {
267     if (channel->modes & CMODE_NONOTICE)
268     {
269     /* asuka sucks */
270     /* remove that stupid +N mode before it blocks our notice
271     * -- jilles */
272     sts ("%s M %s -N", sender->uid, channel->name);
273     channel->modes &= ~CMODE_NONOTICE;
274     }
275     sts ("%s WC %s :%s", sender->uid, channel->name, message);
276     }
277    
278     void
279     asuka_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
280     {
281     va_list ap;
282     char buf[BUFSIZE];
283     user_t *source_p, *target_p;
284    
285     source_p = user_find_named (from);
286     target_p = user_find_named (target);
287    
288     if (!target_p)
289     return;
290    
291     va_start (ap, fmt);
292     vsnprintf (buf, BUFSIZE, fmt, ap);
293     va_end (ap);
294    
295     sts ("%s %d %s %s", source_p ? source_p->uid : me.numeric, numeric, target_p->uid, buf);
296     }
297    
298     /* KILL wrapper */
299     void
300     asuka_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
301     {
302     va_list ap;
303     char buf[BUFSIZE];
304     user_t *fptr = user_find_named (from);
305     user_t *tptr = user_find_named (nick);
306    
307     if (!tptr)
308     return;
309    
310     va_start (ap, fmt);
311     vsnprintf (buf, BUFSIZE, fmt, ap);
312     va_end (ap);
313    
314     sts ("%s D %s :%s!%s!%s (%s)", fptr ? fptr->uid : me.numeric, tptr->uid, from, from, from, buf);
315     }
316    
317     /* PART wrapper */
318     void
319     asuka_handler::part_sts (channel_t *c, user_t *u)
320     {
321     sts ("%s L %s", u->uid, c->name);
322     }
323    
324     /* server-to-server KLINE wrapper */
325     void
326     asuka_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
327     {
328     if (!me.connected)
329     return;
330    
331     /* hold permanent akills for four weeks -- jilles */
332     sts ("%s GL * +%s@%s %ld :%s", me.numeric, user, host, duration > 0 ? duration : 2419200, reason);
333     }
334    
335     /* server-to-server UNKLINE wrapper */
336     void
337     asuka_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
338     {
339     if (!me.connected)
340     return;
341    
342     sts ("%s GL * -%s@%s", me.numeric, user, host);
343     }
344    
345     /* topic wrapper */
346     void
347     asuka_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
348     {
349     if (!me.connected || !c)
350     return;
351    
352     if (ts > prevts || prevts == 0)
353 pippijn 1.1 sts ("%s T %s %ld %ld :%s", chansvs.me->me->uid, c->name, c->ts, ts, topic);
354 pippijn 1.4 else
355 pippijn 1.1 {
356 pippijn 1.4 ts = NOW;
357     if (ts < prevts)
358     ts = prevts + 1;
359     sts ("%s T %s %ld %ld :%s", chansvs.me->me->uid, c->name, c->ts, ts, topic);
360     c->topicts = ts;
361 pippijn 1.1 }
362 pippijn 1.4 }
363    
364     /* mode wrapper */
365     void
366     asuka_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
367     {
368     user_t *fptr = user_find_named (sender);
369    
370     if (!fptr)
371     return;
372    
373     if (chanuser_find (target, fptr))
374     sts ("%s M %s %s", fptr->uid, target->name, modes);
375     else
376     sts ("%s M %s %s", me.numeric, target->name, modes);
377     }
378    
379     /* ping wrapper */
380     void
381     asuka_handler::ping_sts (void)
382     {
383     if (!me.connected)
384 pippijn 1.1 return;
385 pippijn 1.4
386     sts ("%s G !%ld %s %ld", me.numeric, NOW, me.name, NOW);
387     }
388    
389     /* protocol-specific stuff to do on login */
390     void
391     asuka_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
392     {
393     user_t *u = user_find_named (origin);
394    
395     if (!u)
396     return;
397    
398     sts ("%s AC %s %s", me.numeric, u->uid, u->myuser->name);
399     check_hidehost (u);
400     }
401    
402     /* P10 does not support logout, so kill the user
403     * we can't keep track of which logins are stale and which aren't -- jilles */
404     bool
405     asuka_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
406     {
407     user_t *u = user_find_named (origin);
408    
409     if (!me.connected)
410     return false;
411    
412     if (u != NULL)
413     {
414     skill (me.name, u->nick, "Forcing logout %s -> %s", u->nick, user);
415     user_delete (u);
416     return true;
417 pippijn 1.1 }
418 pippijn 1.4 else
419     return false;
420     }
421    
422     void
423     asuka_handler::jupe (char const * const server, char const * const reason)
424     {
425     server_t *s;
426    
427     if (!me.connected)
428     return;
429    
430     /* hold it for a day (arbitrary) -- jilles */
431     /* get rid of local deactivation too */
432     s = server_find (server);
433     if (s != NULL && s->uplink != NULL)
434     sts ("%s JU %s +%s %d %ld :%s", me.numeric, s->uplink->sid, server, 86400, NOW, reason);
435     sts ("%s JU * +%s %d %ld :%s", me.numeric, server, 86400, NOW, reason);
436     }
437    
438     static void
439     m_topic (sourceinfo_t *si, int parc, char *parv[])
440     {
441     channel_t *c = channel_find (parv[0]);
442     char *source;
443     time_t ts = 0;
444    
445     if (!c)
446 pippijn 1.1 return;
447 pippijn 1.4
448     if (si->s != NULL)
449     source = si->s->name;
450     else
451     source = si->su->nick;
452    
453     if (parc > 2)
454     ts = atoi (parv[parc - 2]);
455     if (ts == 0)
456     ts = NOW;
457     else if (c->topic != NULL && ts < c->topicts)
458 pippijn 1.1 return;
459 pippijn 1.4 handle_topic_from (si, c, source, ts, parv[parc - 1]);
460     }
461    
462     /* AB G !1119920789.573932 services.atheme.org 1119920789.573932 */
463     static void
464     m_ping (sourceinfo_t *si, int parc, char *parv[])
465     {
466     /* reply to PING's */
467     sts ("%s Z %s %s %s", me.numeric, parv[0], parv[1], parv[2]);
468     }
469    
470     static void
471     m_pong (sourceinfo_t *si, int parc, char *parv[])
472     {
473     me.uplinkpong = NOW;
474    
475     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
476     if (me.bursting)
477     {
478     #ifdef HAVE_GETTIMEOFDAY
479     e_time (burstime, &burstime);
480    
481     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
482    
483     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
484     #else
485     slog (LG_INFO, "m_pong(): finished synching with uplink");
486     wallops ("Finished synching to network.");
487     #endif
488    
489     me.bursting = false;
490     }
491     }
492    
493     static void
494     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
495     {
496     if (parc != 2)
497 pippijn 1.1 return;
498 pippijn 1.4
499     handle_message (si, parv[0], false, parv[1]);
500     }
501    
502     static void
503     m_notice (sourceinfo_t *si, int parc, char *parv[])
504     {
505     if (parc != 2)
506 pippijn 1.1 return;
507 pippijn 1.4
508     handle_message (si, parv[0], true, parv[1]);
509     }
510    
511     static void
512     m_create (sourceinfo_t *si, int parc, char *parv[])
513     {
514     char buf[BUFSIZE];
515     int chanc;
516     char *chanv[256];
517     int i;
518    
519     chanc = sjtoken (parv[0], ',', chanv);
520    
521     for (i = 0; i < chanc; i++)
522     {
523     channel_t *c = channel_add (chanv[i], atoi (parv[1]), si->su->server);
524    
525     /* Tell the core to check mode locks now,
526     * otherwise it may only happen after the next
527     * mode change.
528     * P10 does not allow any redundant modes
529     * so this will not look ugly. -- jilles */
530     channel_mode (NULL, c, "+");
531    
532     buf[0] = '@';
533     buf[1] = '\0';
534    
535     strlcat (buf, si->su->uid, BUFSIZE);
536    
537     chanuser_add (c, buf);
538     }
539     }
540    
541     static void
542     m_join (sourceinfo_t *si, int parc, char *parv[])
543     {
544     int chanc;
545     char *chanv[256];
546     int i;
547     node_t *n, *tn;
548     chanuser_t *cu;
549    
550     /* JOIN 0 is really a part from all channels */
551     if (!strcmp (parv[0], "0"))
552     {
553     LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
554     {
555     cu = (chanuser_t *) n->data;
556     chanuser_delete (cu->chan, si->su);
557     }
558     return;
559     }
560     if (parc < 2)
561 pippijn 1.1 return;
562 pippijn 1.4
563     chanc = sjtoken (parv[0], ',', chanv);
564    
565     for (i = 0; i < chanc; i++)
566     {
567     channel_t *c = channel_find (chanv[i]);
568    
569     if (!c)
570     {
571     c = channel_add (chanv[i], atoi (parv[1]), si->su->server);
572     channel_mode (NULL, c, "+");
573     }
574    
575     chanuser_add (c, si->su->uid);
576     }
577     }
578    
579     static void
580     m_burst (sourceinfo_t *si, int parc, char *parv[])
581     {
582     channel_t *c;
583     unsigned int modec;
584     char *modev[16];
585     unsigned int userc;
586     char *userv[256];
587     unsigned int i;
588     int j;
589     char prefix[16];
590     char newnick[16 + NICKLEN];
591     char *p;
592     time_t ts;
593    
594     /* S BURST <channel> <ts> [parameters]
595     * parameters can be:
596     * +<simple mode>
597     * %<bans separated with spaces>
598     * <nicks>
599     */
600     ts = atoi (parv[1]);
601    
602     c = channel_find (parv[0]);
603    
604     if (c == NULL)
605     {
606     slog (LG_DEBUG, "m_burst(): new channel: %s", parv[0]);
607     c = channel_add (parv[0], ts, si->s);
608     }
609     else if (ts < c->ts)
610     {
611     chanuser_t *cu;
612     node_t *n;
613    
614     clear_simple_modes (c);
615     chanban_clear (c);
616     handle_topic_from (si, c, "", 0, "");
617     LIST_FOREACH (n, c->members.head)
618     {
619     cu = (chanuser_t *) n->data;
620     if (cu->user->server == me.me)
621     {
622     /* it's a service, reop */
623     sts ("%s M %s +o %s", me.numeric, c->name, CLIENT_NAME (cu->user));
624     cu->modes = CMODE_OP;
625     }
626     else
627     cu->modes = 0;
628     }
629    
630     slog (LG_DEBUG, "m_burst(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
631     c->ts = ts;
632     c->callback.tschange (c);
633     }
634     if (parc < 3 || parv[2][0] != '+')
635     {
636     /* Tell the core to check mode locks now,
637     * otherwise it may only happen after the next
638     * mode change. -- jilles */
639     channel_mode (NULL, c, "+");
640     }
641    
642     j = 2;
643     while (j < parc)
644     {
645     if (parv[j][0] == '+')
646     {
647     modec = 0;
648     modev[modec++] = parv[j++];
649     if (strchr (modev[0], 'k') && j < parc)
650     modev[modec++] = parv[j++];
651     if (strchr (modev[0], 'l') && j < parc)
652     modev[modec++] = parv[j++];
653     channel_mode (NULL, c, modec, modev);
654     }
655     else if (parv[j][0] == '%')
656     {
657     userc = sjtoken (parv[j++] + 1, ' ', userv);
658     for (i = 0; i < userc; i++)
659     chanban_add (c, userv[i], 'b');
660     }
661     else
662     {
663     userc = sjtoken (parv[j++], ',', userv);
664    
665     prefix[0] = '\0';
666     for (i = 0; i < userc; i++)
667     {
668     p = strchr (userv[i], ':');
669     if (p != NULL)
670     {
671     *p = '\0';
672     prefix[0] = '\0';
673     prefix[1] = '\0';
674     prefix[2] = '\0';
675     p++;
676     while (*p)
677     {
678     if (*p == 'o')
679     prefix[prefix[0] ? 1 : 0] = '@';
680     else if (*p == 'v')
681     prefix[prefix[0] ? 1 : 0] = '+';
682     p++;
683     }
684     }
685     strlcpy (newnick, prefix, sizeof newnick);
686     strlcat (newnick, userv[i], sizeof newnick);
687     chanuser_add (c, newnick);
688     }
689     }
690     }
691    
692     if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
693     channel_delete (c);
694     }
695    
696     static void
697     m_part (sourceinfo_t *si, int parc, char *parv[])
698     {
699     int chanc;
700     char *chanv[256];
701     int i;
702    
703     chanc = sjtoken (parv[0], ',', chanv);
704     for (i = 0; i < chanc; i++)
705     {
706     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
707    
708     chanuser_delete (channel_find (chanv[i]), si->su);
709     }
710     }
711    
712     static void
713     m_nick (sourceinfo_t *si, int parc, char *parv[])
714     {
715     user_t *u;
716     struct in_addr ip;
717     char ipstring[64];
718     char *p;
719     int i;
720    
721     /* got the right number of args for an introduction? */
722     if (parc >= 8)
723     {
724     /* -> AB N jilles 1 1137687480 jilles jaguar.test +oiwgrx jilles B]AAAB ABAAE :Jilles Tjoelker */
725     /* -> AB N test4 1 1137690148 jilles jaguar.test +iw B]AAAB ABAAG :Jilles Tjoelker */
726     slog (LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
727    
728     ipstring[0] = '\0';
729     if (strlen (parv[parc - 3]) == 6)
730     {
731     ip.s_addr = ntohl (base64touint (parv[parc - 3]));
732     if (!inet_ntop (AF_INET, &ip, ipstring, sizeof ipstring))
733     ipstring[0] = '\0';
734     }
735     u = user_add (parv[0], parv[3], parv[4], NULL, ipstring, parv[parc - 2], parv[parc - 1], si->s, atoi (parv[2]));
736    
737     if (parv[5][0] == '+')
738     {
739     user_mode (u, parv[5]);
740     i = 1;
741     if (strchr (parv[5], 'r'))
742     {
743     handle_burstlogin (u, parv[5 + i]);
744     /* killed to force logout? */
745     if (user_find (parv[parc - 2]) == NULL)
746     return;
747     i++;
748     }
749     if (strchr (parv[5], 'h'))
750     {
751     p = strchr (parv[5 + i], '@');
752     if (p == NULL)
753     strlcpy (u->vhost, parv[5 + i], sizeof u->vhost);
754     else
755     {
756     strlcpy (u->vhost, p + 1, sizeof u->vhost);
757     strlcpy (u->user, parv[5 + i], sizeof u->user);
758     p = strchr (u->user, '@');
759     if (p != NULL)
760     *p = '\0';
761     }
762     i++;
763     }
764     if (strchr (parv[5], 'x'))
765     {
766     u->flags |= UF_HIDEHOSTREQ;
767     /* this must be after setting the account name */
768     check_hidehost (u);
769     }
770     }
771    
772     handle_nickchange (u);
773     }
774     /* if it's only 2 then it's a nickname change */
775     else if (parc == 2)
776     {
777     if (!si->su)
778     {
779     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
780     return;
781     }
782    
783     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
784    
785     user_changenick (si->su, parv[0], atoi (parv[1]));
786    
787     handle_nickchange (si->su);
788     }
789     else
790     {
791     slog (LG_DEBUG, "m_nick(): got NICK with wrong (%d) number of params", parc);
792    
793     for (i = 0; i < parc; i++)
794     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
795     }
796     }
797    
798     static void
799     m_quit (sourceinfo_t *si, int parc, char *parv[])
800     {
801     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
802    
803     /* user_delete() takes care of removing channels and so forth */
804     user_delete (si->su);
805     }
806    
807     static void
808     m_mode (sourceinfo_t *si, int parc, char *parv[])
809     {
810     user_t *u;
811     char *p;
812    
813     if (*parv[0] == '#')
814     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
815     else
816     {
817     /* Yes this is a nick and not a UID -- jilles */
818     u = user_find_named (parv[0]);
819     if (u == NULL)
820     {
821     slog (LG_DEBUG, "m_mode(): user mode for unknown user %s", parv[0]);
822     return;
823     }
824     user_mode (u, parv[1]);
825     if (strchr (parv[1], 'x'))
826     {
827     u->flags |= UF_HIDEHOSTREQ;
828     check_hidehost (u);
829     }
830     if (strchr (parv[1], 'h'))
831     {
832     if (parc > 2)
833     {
834     /* assume +h */
835     p = strchr (parv[2], '@');
836     if (p == NULL)
837     strlcpy (u->vhost, parv[2], sizeof u->vhost);
838     else
839     {
840     strlcpy (u->vhost, p + 1, sizeof u->vhost);
841     strlcpy (u->user, parv[2], sizeof u->user);
842     p = strchr (u->user, '@');
843     if (p != NULL)
844     *p = '\0';
845     }
846     slog (LG_DEBUG, "m_mode(): user %s setting vhost %s@%s", u->nick, u->user, u->vhost);
847     }
848     else
849     {
850     /* must be -h */
851     /* XXX we don't know the original ident */
852     slog (LG_DEBUG, "m_mode(): user %s turning off vhost", u->nick);
853     strlcpy (u->vhost, u->host, sizeof u->vhost);
854     /* revert to +x vhost if applicable */
855     check_hidehost (u);
856     }
857     }
858     }
859     }
860    
861     static void
862     m_clearmode (sourceinfo_t *si, int parc, char *parv[])
863     {
864     channel_t *chan;
865     char *p, c;
866     node_t *n;
867     chanuser_t *cu;
868     int i;
869    
870     /* -> ABAAA CM # b */
871     /* Note: this is an IRCop command, do not enforce mode locks. */
872     chan = channel_find (parv[0]);
873     if (chan == NULL)
874     {
875     slog (LG_DEBUG, "m_clearmode(): unknown channel %s", parv[0]);
876     return;
877     }
878     p = parv[1];
879     while ((c = *p++))
880     {
881     if (c == 'b')
882     chanban_clear (chan);
883     else if (c == 'k')
884     {
885     if (chan->key)
886     sfree (chan->key);
887     chan->key = NULL;
888     }
889     else if (c == 'l')
890     chan->limit = 0;
891     else if (c == 'o')
892     {
893     LIST_FOREACH (n, chan->members.head)
894     {
895     cu = (chanuser_t *) n->data;
896     if (cu->user->server == me.me)
897     {
898     /* it's a service, reop */
899     sts ("%s M %s +o %s", me.numeric, chan->name, cu->user->uid);
900     }
901     else
902     cu->modes &= ~CMODE_OP;
903     }
904     }
905     else if (c == 'v')
906     {
907     LIST_FOREACH (n, chan->members.head)
908     {
909     cu = (chanuser_t *) n->data;
910     cu->modes &= ~CMODE_VOICE;
911     }
912     }
913     else
914     for (i = 0; mode_list[i].mode != '\0'; i++)
915     {
916     if (c == mode_list[i].mode)
917     chan->modes &= ~mode_list[i].value;
918     }
919     }
920     }
921    
922     static void
923     m_kick (sourceinfo_t *si, int parc, char *parv[])
924     {
925     user_t *u = user_find (parv[1]);
926     channel_t *c = channel_find (parv[0]);
927    
928     /* -> :rakaur KICK #shrike rintaun :test */
929     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
930    
931     if (!u)
932     {
933     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
934     return;
935     }
936    
937     if (!c)
938     {
939     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
940     return;
941     }
942    
943     if (!chanuser_find (c, u))
944     {
945     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
946     return;
947     }
948    
949     chanuser_delete (c, u);
950    
951     /* if they kicked us, let's rejoin */
952     if (is_internal_client (u))
953     {
954     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
955     join (parv[0], u->nick);
956     }
957     }
958    
959     static void
960     m_kill (sourceinfo_t *si, int parc, char *parv[])
961     {
962     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
963     }
964    
965     static void
966     m_squit (sourceinfo_t *si, int parc, char *parv[])
967     {
968     slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
969     server_delete (parv[0]);
970     }
971    
972     /* SERVER ircu.devel.atheme.org 1 1119902586 1119908830 J10 ABAP] + :lets lol */
973     static void
974     m_server (sourceinfo_t *si, int parc, char *parv[])
975     {
976     server_t *s;
977    
978     /* We dont care about the max connections. */
979     parv[5][2] = '\0';
980    
981     slog (LG_DEBUG, "m_server(): new server: %s, id %s, %s", parv[0], parv[5], parv[4][0] == 'P' ? "eob" : "bursting");
982     s = handle_server (si, parv[0], parv[5], atoi (parv[1]), parv[7]);
983    
984     /* SF_EOB may only be set when we have all users on the server.
985     * so store the fact that they are EOB in another flag.
986     * handle_eob() will set SF_EOB when the uplink has finished bursting.
987     * -- jilles */
988     if (s != NULL && parv[4][0] == 'P')
989     s->flags |= SF_EOB2;
990     }
991    
992     static void
993     m_stats (sourceinfo_t *si, int parc, char *parv[])
994     {
995     handle_stats (si->su, parv[0][0]);
996     }
997    
998     static void
999     m_admin (sourceinfo_t *si, int parc, char *parv[])
1000     {
1001     handle_admin (si->su);
1002     }
1003    
1004     static void
1005     m_version (sourceinfo_t *si, int parc, char *parv[])
1006     {
1007     handle_version (si->su);
1008     }
1009    
1010     static void
1011     m_info (sourceinfo_t *si, int parc, char *parv[])
1012     {
1013     handle_info (si->su);
1014     }
1015    
1016     static void
1017     m_motd (sourceinfo_t *si, int parc, char *parv[])
1018     {
1019     handle_motd (si->su);
1020     }
1021    
1022     static void
1023     m_whois (sourceinfo_t *si, int parc, char *parv[])
1024     {
1025     handle_whois (si->su, parv[1]);
1026     }
1027    
1028     static void
1029     m_trace (sourceinfo_t *si, int parc, char *parv[])
1030     {
1031     handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
1032     }
1033    
1034     static void
1035     m_away (sourceinfo_t *si, int parc, char *parv[])
1036     {
1037     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1038     }
1039    
1040     static void
1041     m_pass (sourceinfo_t *si, int parc, char *parv[])
1042     {
1043     if (strcmp (curr_uplink->pass, parv[0]))
1044     {
1045     slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
1046     runflags |= RF_SHUTDOWN;
1047     }
1048     }
1049    
1050     static void
1051     m_error (sourceinfo_t *si, int parc, char *parv[])
1052     {
1053     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1054     }
1055    
1056     static void
1057     m_eos (sourceinfo_t *si, int parc, char *parv[])
1058     {
1059     handle_eob (si->s);
1060    
1061     /* acknowledge a local END_OF_BURST */
1062     if (si->s->uplink == me.me)
1063     sts ("%s EA", me.numeric);
1064     }
1065    
1066     static void
1067     check_hidehost (user_t *u)
1068     {
1069     static bool warned = false;
1070    
1071     /* do they qualify? */
1072     if (!(u->flags & UF_HIDEHOSTREQ) || u->myuser == NULL || (u->myuser->flags & MU_WAITAUTH))
1073 pippijn 1.1 return;
1074 pippijn 1.4 /* don't use this if they have some other kind of vhost */
1075     if (strcmp (u->host, u->vhost))
1076     {
1077     slog (LG_DEBUG, "check_hidehost(): +x overruled by other vhost for %s", u->nick);
1078     return;
1079     }
1080     if (me.hidehostsuffix == NULL)
1081     {
1082     if (!warned)
1083     {
1084     wallops ("Misconfiguration: serverinfo::hidehostsuffix not set");
1085     warned = true;
1086     }
1087     return;
1088     }
1089     snprintf (u->vhost, sizeof u->vhost, "%s.%s", u->myuser->name, me.hidehostsuffix);
1090     slog (LG_DEBUG, "check_hidehost(): %s -> %s", u->nick, u->vhost);
1091     }
1092    
1093     static pcommand_t const pcommands[] = {
1094     { "G", m_ping, 1, MSRC_USER | MSRC_SERVER },
1095     { "Z", m_pong, 1, MSRC_SERVER },
1096     { "P", m_privmsg, 2, MSRC_USER },
1097     { "O", m_notice, 2, MSRC_USER | MSRC_SERVER },
1098     { "NOTICE", m_notice, 2, MSRC_UNREG },
1099     { "C", m_create, 1, MSRC_USER },
1100     { "J", m_join, 1, MSRC_USER },
1101     { "EB", m_eos, 0, MSRC_SERVER },
1102     { "B", m_burst, 2, MSRC_SERVER },
1103     { "L", m_part, 1, MSRC_USER },
1104     { "N", m_nick, 2, MSRC_USER | MSRC_SERVER },
1105     { "Q", m_quit, 1, MSRC_USER },
1106     { "M", m_mode, 2, MSRC_USER | MSRC_SERVER },
1107     { "OM", m_mode, 2, MSRC_USER }, /* OPMODE, treat as MODE */
1108     { "CM", m_clearmode, 2, MSRC_USER },
1109     { "K", m_kick, 2, MSRC_USER | MSRC_SERVER },
1110     { "D", m_kill, 1, MSRC_USER | MSRC_SERVER },
1111     { "SQ", m_squit, 1, MSRC_USER | MSRC_SERVER },
1112     { "S", m_server, 8, MSRC_SERVER },
1113     { "SERVER", m_server, 8, MSRC_UNREG },
1114     { "R", m_stats, 2, MSRC_USER },
1115     { "AD", m_admin, 1, MSRC_USER },
1116     { "V", m_version, 1, MSRC_USER },
1117     { "F", m_info, 1, MSRC_USER },
1118     { "W", m_whois, 2, MSRC_USER },
1119     { "TR", m_trace, 1, MSRC_USER },
1120     { "A", m_away, 0, MSRC_USER },
1121     { "PASS", m_pass, 1, MSRC_UNREG },
1122     { "Y", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1123     { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1124     { "T", m_topic, 2, MSRC_USER | MSRC_SERVER },
1125     { "MO", m_motd, 1, MSRC_USER },
1126     };
1127    
1128     asuka_handler::asuka_handler ()
1129     {
1130     parse = &p10_parse;
1131    
1132     mode_list = asuka_mode_list;
1133     ignore_mode_list = asuka_ignore_mode_list;
1134     status_mode_list = asuka_status_mode_list;
1135     prefix_mode_list = asuka_prefix_mode_list;
1136    
1137     ircd = &Asuka;
1138    
1139     pcommand_add (&pcommands);
1140     }
1141    
1142     asuka_handler::~asuka_handler ()
1143     {
1144     parse = NULL;
1145    
1146     mode_list = NULL;
1147     ignore_mode_list = NULL;
1148     status_mode_list = NULL;
1149     prefix_mode_list = NULL;
1150    
1151     ircd = NULL;
1152    
1153     pcommand_delete (&pcommands);
1154     }
1155     } // namespace protocol
1156    
1157     #define FACREG_TYPE protocol::asuka_handler
1158     #define FACREG_TYPE_NAME "asuka"
1159     #define FACREG_INTERFACE_TYPE protocol::handler
1160     #include <ermyth/factory_reg.h>