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