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