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