ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/asuka.C
Revision: 1.8
Committed: Sat Sep 22 14:27:28 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +8 -12 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

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