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