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