ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/asuka.C
Revision: 1.2
Committed: Sat Jul 21 01:29:09 2007 UTC (19 years, 3 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +2 -2 lines
Log Message:
- moved to new documentation system
- fixed small build error

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