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

File Contents

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