ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/inspircd11.C
Revision: 1.10
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.9: +6 -2 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /**
2 * inspircd11.C: This file contains protocol support for spanning tree 1.1 branch inspircd.
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 Atheme Development Group
10 * Rights to this code are documented in doc/pod/license.pod.
11 *
12 * $Id: inspircd11.C,v 1.9 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/mychan.h>
22 #include <account/myuser.h>
23 #include "uplink.h"
24 #include "pmodule.h"
25 #include "protocol/inspircd.h"
26
27 static char const rcsid[] = "$Id: inspircd11.C,v 1.9 2007-09-16 18:54:44 pippijn Exp $";
28
29 /* *INDENT-OFF* */
30
31 namespace protocol
32 {
33 static ircd_t InspIRCd = {
34 "InspIRCd 1.1.x", /* IRCd name */
35 "$", /* TLD Prefix, used by Global. */
36 false, /* Whether or not we use IRCNet/TS6 UID */
37 false, /* Whether or not we use RCOMMAND */
38 false, /* Whether or not we support channel owners. */
39 false, /* Whether or not we support channel protection. */
40 true, /* Whether or not we support halfops. */
41 false, /* Whether or not we use P10 */
42 true, /* Whether or not we use vHosts. */
43 CMODE_OPERONLY, /* Oper-only cmodes */
44 CMODE_OWNER, /* Integer flag for owner channel flag. */
45 CMODE_PROTECT, /* Integer flag for protect channel flag. */
46 CMODE_HALFOP, /* Integer flag for halfops. */
47 "+q", /* Mode we set for owner. */
48 "+a", /* Mode we set for protect. */
49 "+h", /* Mode we set for halfops. */
50 PROTOCOL_INSPIRCD, /* Protocol type */
51 0, /* Permanent cmodes */
52 "beIg", /* Ban-like cmodes */
53 'e', /* Except mchar */
54 'I', /* Invex mchar */
55 IRCD_CIDR_BANS /* Flags */
56 };
57
58 struct inspircd11_handler : handler
59 {
60 inspircd11_handler ();
61 virtual ~inspircd11_handler ();
62
63 virtual unsigned int server_login (void);
64 virtual void introduce_nick (user_t *u);
65 virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
66 virtual void quit_sts (user_t *u, char const * const reason);
67 virtual void wallops_sts (char const * const text);
68 virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
69 virtual void chan_lowerts (channel_t *c, user_t *u);
70 virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
71 virtual void mdchange (char const * const target, char const * const key, char const * const value);
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 numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
77 virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
78 virtual void part_sts (channel_t *c, user_t *u);
79 virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
80 virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
81 virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
82 virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
83 virtual void ping_sts (void);
84 virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
85 virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
86 virtual void jupe (char const * const server, char const * const reason);
87 virtual void sethost_sts (char const * const source, char const * const target, char const * const host);
88 virtual void fnc_sts (user_t *source, user_t *u, char const * const newnick, int type);
89 virtual void holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account);
90 };
91
92 static cmode_t inspircd11_mode_list[] = {
93 { 'i', CMODE_INVITE },
94 { 'm', CMODE_MOD },
95 { 'n', CMODE_NOEXT },
96 { 'p', CMODE_PRIV },
97 { 's', CMODE_SEC },
98 { 't', CMODE_TOPIC },
99 { 'c', CMODE_NOCOLOR },
100 { 'M', CMODE_MODREG },
101 { 'R', CMODE_REGONLY },
102 { 'O', CMODE_OPERONLY },
103 { 'S', CMODE_STRIP },
104 { 'K', CMODE_NOKNOCK },
105 { 'V', CMODE_NOINVITE },
106 { 'C', CMODE_NOCTCP },
107 { 'N', CMODE_STICKY },
108 { 'G', CMODE_CENSOR },
109 { 'P', CMODE_NOCAPS },
110 { 'z', CMODE_SSLONLY },
111 { 'T', CMODE_NONOTICE },
112 { 'u', CMODE_HIDING },
113 { 'Q', CMODE_PEACE },
114 { '\0', 0 }
115 };
116
117 static bool check_flood(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
118 static bool check_nickflood (char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
119 static bool check_jointhrottle(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
120 static bool check_forward(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
121 static bool check_rejoindelay(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
122
123 static extmode_t inspircd11_ignore_mode_list[] = {
124 { 'f', check_flood },
125 { 'F', check_nickflood },
126 { 'j', check_jointhrottle },
127 { 'L', check_forward },
128 { 'J', check_rejoindelay },
129 { '\0', 0 }
130 };
131
132 static cmode_t inspircd11_status_mode_list[] = {
133 { 'q', CMODE_OWNER },
134 { 'a', CMODE_PROTECT },
135 { 'o', CMODE_OP },
136 { 'h', CMODE_HALFOP },
137 { 'v', CMODE_VOICE },
138 { '\0', 0 }
139 };
140
141 static cmode_t inspircd11_prefix_mode_list[] = {
142 { '~', CMODE_OWNER },
143 { '&', CMODE_PROTECT },
144 { '@', CMODE_OP },
145 { '%', CMODE_HALFOP },
146 { '+', CMODE_VOICE },
147 { '\0', 0 }
148 };
149
150 /* CAPABilities */
151 static bool has_servicesmod = false;
152 static bool has_globopsmod = false;
153 static bool has_svshold = false;
154 static int has_protocol = 0;
155
156 #define PROTOCOL_SNONOTICE 1102 /* has SNONOTICE and OPERNOTICE commands */
157 #define PROTOCOL_NEWTS 1104 /* does not send "confirming" FMODEs on TS changes and uses clear/ignore semantics also for FMODE */
158
159 /* *INDENT-ON* */
160
161 static bool
162 check_flood (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
163 {
164 /* +F doesn't support *, so don't bother checking for it -- w00t */
165 return check_jointhrottle (value, c, mc, u, mu);
166 }
167
168 static bool
169 check_nickflood (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
170 {
171 return *value == '*' ? check_jointhrottle(value + 1, c, mc, u, mu) : check_jointhrottle(value, c, mc, u, mu);
172 }
173
174 static bool
175 check_jointhrottle (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
176 {
177 char const *p;
178 char const *arg2;
179
180 p = value, arg2 = NULL;
181 while (*p != '\0')
182 {
183 if (*p == ':')
184 {
185 if (arg2 != NULL)
186 return false;
187 arg2 = p + 1;
188 }
189 else if (!isdigit (*p))
190 return false;
191 p++;
192 }
193 if (arg2 == NULL)
194 return false;
195 if (p - arg2 > 10 || arg2 - value - 1 > 10 || !atoi (value) || !atoi (arg2))
196 return false;
197 return true;
198 }
199
200 static bool
201 check_forward (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
202 {
203 channel_t *target_c;
204 mychan_t *target_mc;
205
206 if (*value != '#' || strlen (value) > 50)
207 return false;
208 if (u == NULL && mu == NULL)
209 return true;
210 target_c = channel_find (value);
211 target_mc = mychan_t::find (value);
212 if (target_c == NULL && target_mc == NULL)
213 return false;
214 return true;
215 }
216
217 static bool
218 check_rejoindelay (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
219 {
220 char const *ch = value;
221
222 while (*ch)
223 {
224 if (!isdigit (*ch))
225 return false;
226 ch++;
227 }
228
229 if (atoi (value) <= 0)
230 {
231 return false;
232 }
233 else
234 {
235 return true;
236 }
237 }
238
239
240 /* login to our uplink */
241 unsigned int
242 inspircd11_handler::server_login (void)
243 {
244 int ret;
245
246 /* XXX This is a horrible hack, but I don't know how else */
247 sleep (3);
248
249 ret = sts ("SERVER %s %s 0 :%s", me.name, curr_uplink->pass, me.desc);
250 if (ret == 1)
251 return 1;
252
253 me.bursting = true;
254 sts ("BURST");
255 /* XXX: Being able to get this data as a char* would be nice - Brain */
256 sts (":%s VERSION :" PACKAGE_NAME "-%s. %s %s%s%s%s%s%s%s%s%s%s",
257 me.name,
258 version,
259 me.name,
260 match_mapping ? "A" : "",
261 log_debug_enabled ()? "d" : "",
262 me.auth ? "e" : "",
263 config_options.flood_msgs ? "F" : "",
264 config_options.leave_chans ? "l" : "",
265 config_options.join_chans ? "j" : "",
266 chansvs.changets ? "t" : "",
267 !match_mapping ? "R" : "",
268 config_options.raw ? "r" : "",
269 runflags & RF_LIVE ? "n" : "");
270 services_init ();
271 return 0;
272 }
273
274 /* introduce a client */
275 void
276 inspircd11_handler::introduce_nick (user_t *u)
277 {
278 /* :services-dev.chatspike.net NICK 1133994664 OperServ chatspike.net chatspike.net services +oii 0.0.0.0 :Operator Server */
279 sts (":%s NICK %ld %s %s %s %s +%s 0.0.0.0 :%s", me.name, u->ts, u->nick, u->host, u->host, u->user, "io", u->gecos);
280 sts (":%s OPERTYPE Services", u->nick);
281 }
282
283 void
284 inspircd11_handler::quit_sts (user_t *u, char const * const reason)
285 {
286 if (!me.connected)
287 return;
288
289 sts (":%s QUIT :%s", u->nick, reason);
290 }
291
292 /* WALLOPS wrapper */
293 void
294 inspircd11_handler::wallops_sts (char const * const text)
295 {
296 char const *sendernick = NULL;
297 user_t *u;
298 node_t *n;
299
300 if (has_protocol >= PROTOCOL_SNONOTICE)
301 {
302 /* XXX */
303 if (has_globopsmod)
304 sts (":%s SNONOTICE g :%s", me.name, text);
305 else
306 sts (":%s OPERNOTICE :%s", me.name, text);
307 return;
308 }
309
310 if (me.me == NULL)
311 return;
312
313 if (is_internal_client (user_find_named (opersvs.nick)))
314 sendernick = opersvs.nick;
315 else
316 {
317 LIST_FOREACH (n, me.me->userlist.head)
318 {
319 u = (user_t *) n->data;
320
321 sendernick = u->nick;
322 break;
323 }
324 }
325
326 if (sendernick == NULL)
327 {
328 /*
329 * this means we have no pseudoclients -- under present inspircd, servers cannot globops, and
330 * thus, we will need to bail -- slog, and let them know. --w00t
331 */
332 slog (LG_ERROR, "wallops_sts(): InspIRCD requires at least one pseudoclient module to be loaded to send wallops.");
333 return;
334 }
335
336 sts (":%s GLOBOPS :%s", sendernick, text);
337 }
338
339 /* join a channel */
340 void
341 inspircd11_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
342 {
343 if (isnew)
344 {
345 sts (":%s FJOIN %s %ld :@,%s", me.name, c->name, c->ts, u->nick);
346 if (modes[0] && modes[1])
347 sts (":%s FMODE %s %ld %s", me.name, c->name, c->ts, modes);
348 }
349 else
350 {
351 sts (":%s FJOIN %s %ld :@,%s", me.name, c->name, c->ts, u->nick);
352 }
353 }
354
355 void
356 inspircd11_handler::chan_lowerts (channel_t *c, user_t *u)
357 {
358 slog (LG_DEBUG, "inspircd11_handler::chan_lowerts(): lowering TS for %s to %ld", c->name, (long) c->ts);
359
360 sts (":%s FJOIN %s %ld :@,%s", me.name, c->name, c->ts, u->nick);
361 sts (":%s FMODE %s %ld %s", me.name, c->name, c->ts, channel_modes (c, true));
362 }
363
364 /* kicks a user from a channel */
365 void
366 inspircd11_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
367 {
368 channel_t *chan = channel_find (channel);
369 user_t *user = user_find (to);
370
371 if (!chan || !user)
372 return;
373
374 sts (":%s KICK %s %s :%s", from, channel, to, reason);
375
376 chanuser_delete (chan, user);
377 }
378
379 // changes ircd metadata
380 void
381 inspircd11_handler::mdchange (char const * const target, char const * const key, char const * const value)
382 {
383 sts (":%s METADATA %s %s :%s", me.name, target, key, value);
384 }
385
386 /* PRIVMSG wrapper */
387 void
388 inspircd11_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
389 {
390 va_list ap;
391 char buf[BUFSIZE];
392
393 va_start (ap, fmt);
394 vsnprintf (buf, BUFSIZE, fmt, ap);
395 va_end (ap);
396
397 sts (":%s PRIVMSG %s :%s", from, target, buf);
398 }
399
400 /* NOTICE wrapper */
401 void
402 inspircd11_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
403 {
404 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
405 }
406
407 void
408 inspircd11_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
409 {
410 sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
411 }
412
413 void
414 inspircd11_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
415 {
416 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
417 }
418
419 void
420 inspircd11_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
421 {
422 va_list ap;
423 char buf[BUFSIZE];
424
425 va_start (ap, fmt);
426 vsnprintf (buf, BUFSIZE, fmt, ap);
427 va_end (ap);
428
429 sts (":%s PUSH %s ::%s %d %s %s", me.name, target, from, numeric, target, buf);
430 }
431
432 /* KILL wrapper */
433 void
434 inspircd11_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
435 {
436 va_list ap;
437 char buf[BUFSIZE];
438
439 va_start (ap, fmt);
440 vsnprintf (buf, BUFSIZE, fmt, ap);
441 va_end (ap);
442
443 sts (":%s KILL %s :[%s] Killed (%s (%s))", from, nick, me.name, from, buf);
444 }
445
446 /* PART wrapper */
447 void
448 inspircd11_handler::part_sts (channel_t *c, user_t *u)
449 {
450 sts (":%s PART %s :Leaving", u->nick, c->name);
451 }
452
453 /* server-to-server KLINE wrapper */
454 void
455 inspircd11_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
456 {
457 if (!me.connected)
458 return;
459
460 /* :services-dev.chatspike.net ADDLINE G test@test.com Brain 1133994664 0 :You are banned from this network */
461 sts (":%s ADDLINE G %s@%s %s %ld %ld :%s", me.name, user, host, opersvs.nick, NOW, duration, reason);
462 }
463
464 /* server-to-server UNKLINE wrapper */
465 void
466 inspircd11_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
467 {
468 if (!me.connected)
469 return;
470
471 /* I know this looks wrong, but it's really not. Trust me. --w00t */
472 sts (":%s GLINE %s@%s", opersvs.nick, user, host);
473 }
474
475 /* topic wrapper */
476 void
477 inspircd11_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
478 {
479 if (!me.connected || !c)
480 return;
481
482 /* If possible, try to use FTOPIC
483 * Note that because TOPIC does not contain topicTS, it may be
484 * off a few seconds on other servers, hence the 60 seconds here.
485 * -- jilles */
486 /* Restoring old topic */
487 if (ts > prevts + 60 || prevts == 0)
488 {
489 sts (":%s FTOPIC %s %ld %s :%s", chansvs.nick, c->name, ts, setter, topic);
490 return;
491 }
492 /* Tweaking a topic */
493 else if (ts == prevts)
494 {
495 ts += 60;
496 sts (":%s FTOPIC %s %ld %s :%s", chansvs.nick, c->name, ts, setter, topic);
497 c->topicts = ts;
498 return;
499 }
500 sts (":%s TOPIC %s :%s", chansvs.nick, c->name, topic);
501 c->topicts = NOW;
502 }
503
504 /* mode wrapper */
505 void
506 inspircd11_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
507 {
508 if (!me.connected)
509 return;
510
511 if (has_protocol >= PROTOCOL_NEWTS)
512 {
513 /* FMODE from user is ok, use it */
514 sts (":%s FMODE %s %ld %s", sender, target->name, target->ts, modes);
515 }
516 else
517 {
518 sts (":%s MODE %s %s", sender, target->name, modes);
519 }
520 }
521
522 /* ping wrapper */
523 void
524 inspircd11_handler::ping_sts (void)
525 {
526 if (!me.connected)
527 return;
528
529 sts (":%s PING :%s", me.name, curr_uplink->name);
530 }
531
532 /* protocol-specific stuff to do on login */
533 void
534 inspircd11_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
535 {
536 if (!me.connected)
537 return;
538
539 sts (":%s METADATA %s accountname :%s", me.name, origin, user);
540 }
541
542 /* protocol-specific stuff to do on logout */
543 bool
544 inspircd11_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
545 {
546 if (!me.connected)
547 return false;
548
549 sts (":%s METADATA %s accountname :", me.name, origin);
550 return false;
551 }
552
553 void
554 inspircd11_handler::jupe (char const * const server, char const * const reason)
555 {
556 server_t *s;
557 if (!me.connected)
558 return;
559
560 s = server_find (server);
561 if (s != NULL)
562 {
563 /* We need to wait for the RSQUIT to be processed -- jilles */
564 sts (":%s RSQUIT :%s", opersvs.nick, server);
565 s->flags |= SF_JUPE_PENDING;
566 }
567 else
568 {
569 /* Remove any previous jupe first */
570 sts (":%s SQUIT %s :%s", me.name, server, reason);
571 sts (":%s SERVER %s * 1 :%s", me.name, server, reason);
572 }
573 }
574
575 void
576 inspircd11_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
577 {
578 if (!me.connected)
579 return;
580
581 notice (source, target, "Setting your host to \2%s\2.", host);
582 sts (":%s CHGHOST %s %s", source, target, host);
583 }
584
585 void
586 inspircd11_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
587 {
588 /* svsnick can only be sent by a server */
589 sts (":%s SVSNICK %s %s %lu", me.name, u->nick, newnick, (unsigned long) (NOW - 60));
590 }
591
592
593 /* invite a user to a channel */
594 void
595 inspircd11_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
596 {
597 sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
598 }
599
600 void
601 inspircd11_handler::holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account)
602 {
603 if (duration == 0)
604 /* remove SVSHOLD */
605 sts (":%s SVSHOLD %s", source->nick, nick);
606 else
607 sts (":%s SVSHOLD %s %ds :Registered nickname.", source->nick, nick, duration);
608 }
609
610 static void
611 m_topic (sourceinfo_t *si, int parc, char *parv[])
612 {
613 channel_t *c = channel_find (parv[0]);
614
615 if (!c)
616 return;
617
618 handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
619 }
620
621 static void
622 m_ftopic (sourceinfo_t *si, int parc, char *parv[])
623 {
624 channel_t *c = channel_find (parv[0]);
625 time_t ts = atol (parv[1]);
626
627 if (!c)
628 return;
629
630 if (c->topic != NULL && c->topicts >= ts)
631 {
632 slog (LG_DEBUG, "m_ftopic(): ignoring older topic on %s", c->name);
633 return;
634 }
635
636 handle_topic_from (si, c, parv[2], ts, parv[3]);
637 }
638
639 static void
640 m_ping (sourceinfo_t *si, int parc, char *parv[])
641 {
642 /* reply to PING's */
643 sts (":%s PONG %s", me.name, parv[0]);
644 }
645
646 static void
647 m_pong (sourceinfo_t *si, int parc, char *parv[])
648 {
649 handle_eob (si->s);
650
651 if (irccasecmp (me.actual, si->s->name))
652 return;
653 me.uplinkpong = NOW;
654
655 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
656 if (me.bursting)
657 {
658 #ifdef HAVE_GETTIMEOFDAY
659 e_time (burstime, &burstime);
660
661 slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
662
663 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
664 #else
665 slog (LG_INFO, "m_pong(): finished synching with uplink");
666 wallops ("Finished synching to network.");
667 #endif
668
669 me.bursting = false;
670 }
671 }
672
673 static void
674 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
675 {
676 if (parc != 2)
677 return;
678
679 handle_message (si, parv[0], false, parv[1]);
680 }
681
682 static void
683 m_notice (sourceinfo_t *si, int parc, char *parv[])
684 {
685 if (parc != 2)
686 return;
687
688 handle_message (si, parv[0], true, parv[1]);
689 }
690
691 static void
692 m_fjoin (sourceinfo_t *si, int parc, char *parv[])
693 {
694 /* FJOIN #flaps 1234 :@,fanny +%,arse ,tits ,breasts &~,poontang */
695 channel_t *c;
696 unsigned int userc;
697 unsigned int i;
698 unsigned int j;
699 unsigned int nlen;
700 bool prefix = true;
701 bool keep_new_modes = true;
702 char *userv[256];
703 char prefixandnick[51];
704 time_t ts;
705
706 c = channel_find (parv[0]);
707 ts = atol (parv[1]);
708
709 if (!c)
710 {
711 slog (LG_DEBUG, "m_fjoin(): new channel: %s", parv[0]);
712 c = channel_add (parv[0], ts, si->s);
713 return_if_fail (c != NULL);
714 /* Tell the core to check mode locks now,
715 * otherwise it may only happen after the next
716 * mode change.
717 * Inspircd does not allow any redundant modes
718 * so this will not look ugly. -- jilles */
719 /* As discussed with Brain, if this is in a burst,
720 * an FMODE with the simple modes will follow so we
721 * can skip this. -- jilles */
722 if (!me.bursting)
723 channel_mode (NULL, c, "+");
724 }
725
726 if (ts < c->ts)
727 {
728 chanuser_t *cu;
729 node_t *n;
730
731 /* the TS changed. a TS change requires us to do
732 * bugger all except update the TS, because in InspIRCd
733 * remote servers enforce the TS change - Brain
734 *
735 * This is no longer the case with 1.1, we need to bounce their modes
736 * as well as lowering the channel ts. Do both. -- w00t
737 */
738
739 if (has_protocol >= PROTOCOL_NEWTS)
740 {
741 clear_simple_modes (c);
742 chanban_clear (c);
743 }
744 LIST_FOREACH (n, c->members.head)
745 {
746 cu = (chanuser_t *) n->data;
747 if (cu->user->server == me.me)
748 {
749 /* it's a service, reop */
750 sts (":%s FMODE %s %ld +o %s", me.name, c->name, ts, cu->user->nick);
751 cu->modes = CMODE_OP;
752 }
753 else
754 cu->modes = 0;
755 }
756
757 c->ts = ts;
758 c->callback.tschange (c);
759 }
760 else if (ts > c->ts)
761 {
762 keep_new_modes = false; /* ignore statuses */
763 }
764
765 /*
766 * ok, here's the difference from 1.0 -> 1.1:
767 * 1.0 sent p[3] and up as individual users, prefixed with their 'highest' prefix, @, % or +
768 * in 1.1, this is more complex. All prefixes are sent, with the additional caveat that modules
769 * can add their own prefixes (dangerous!) - therefore, don't just chanuser_add(), split the prefix
770 * out and ignore unknown prefixes (probably the safest option). --w00t
771 */
772 userc = sjtoken (parv[parc - 1], ' ', userv);
773
774 /* loop over all the users in this fjoin */
775 for (i = 0; i < userc; i++)
776 {
777 nlen = 0;
778 prefix = true;
779
780 slog (LG_DEBUG, "m_fjoin(): processing user: %s", userv[i]);
781
782 /*
783 * ok, now look at the chars in the nick.. we have something like "@%,w00t", but need @%w00t.. and
784 * we also want to ignore unknown prefixes.. loop through the chars
785 */
786 for (; *userv[i]; userv[i]++)
787 {
788 /* does this char match a known prefix? */
789 for (j = 0; prefix_mode_list[j].mode; j++)
790 {
791 /* yup. add it to the 'final' combination (@%w00t) */
792 if (*userv[i] == prefix_mode_list[j].mode)
793 {
794 prefixandnick[nlen++] = *userv[i];
795 continue;
796 }
797 }
798
799 /* it's not a known prefix char, have we reached the end of the prefixes? */
800 if (*userv[i] == ',')
801 {
802 /* yup, skip over the comma */
803 userv[i]++;
804
805 /* if we're ignoring status (keep_new_modes is false) then just add them to chan here.. */
806 if (keep_new_modes == false)
807 {
808 /* This ignores the @%, and just adds 'w00t' to the chan */
809 chanuser_add (c, userv[i]);
810 }
811 else
812 {
813 /* else, we do care about their prefixes.. add '@%w00t' to the chan */
814 strlcpy (prefixandnick + nlen, userv[i], sizeof (prefixandnick) - nlen);
815 chanuser_add (c, prefixandnick);
816 }
817
818 /* added them.. break out of this loop, which will move us to the next user */
819 break;
820 }
821 else
822 {
823 /* unknown prefix char */
824 }
825 }
826
827 /* go to the next user */
828 }
829
830 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
831 channel_delete (c);
832 }
833
834 static void
835 m_part (sourceinfo_t *si, int parc, char *parv[])
836 {
837 int chanc;
838 char *chanv[256];
839 int i;
840
841 chanc = sjtoken (parv[0], ',', chanv);
842 for (i = 0; i < chanc; i++)
843 {
844 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
845
846 chanuser_delete (channel_find (chanv[i]), si->su);
847 }
848 }
849
850 static void
851 m_nick (sourceinfo_t *si, int parc, char *parv[])
852 {
853 user_t *u;
854
855 /* :services-dev.chatspike.net NICK 1133994664 DevNull chatspike.net chatspike.net services +i 0.0.0.0 :/dev/null -- message sink */
856 if (parc == 8)
857 {
858 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[1]);
859
860 /* char *nick, char *user, char *host, char *vhost, char *ip, char *uid, char *gecos, server_t *server, unsigned int ts */
861 u = user_add (parv[1], parv[4], parv[2], parv[3], parv[6], NULL, parv[7], si->s, atol (parv[0]));
862 user_mode (u, parv[5]);
863
864 /* If server is not yet EOB we will do this later.
865 * This avoids useless "please identify" -- jilles */
866 if (si->s->flags & SF_EOB)
867 handle_nickchange (u);
868 }
869 /* if it's only 1 then it's a nickname change */
870 else if (parc == 1)
871 {
872 if (!si->su)
873 {
874 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
875 return;
876 }
877
878 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
879
880 /* No TS here for some reason, hmm */
881 user_changenick (si->su, parv[0], si->su->ts);
882
883 /* It could happen that our PING arrived late and the
884 * server didn't acknowledge EOB yet even though it is
885 * EOB; don't send double notices in that case -- jilles */
886 if (si->su->server->flags & SF_EOB)
887 handle_nickchange (si->su);
888 }
889 else
890 {
891 int i;
892 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
893
894 for (i = 0; i < parc; i++)
895 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
896 }
897 }
898
899 static void
900 m_quit (sourceinfo_t *si, int parc, char *parv[])
901 {
902 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
903
904 /* user_delete() takes care of removing channels and so forth */
905 user_delete (si->su);
906 }
907
908 static void
909 m_saquit (sourceinfo_t *si, int parc, char *parv[])
910 {
911 user_t *u = user_find (parv[0]);
912
913 slog (LG_DEBUG, "m_saquit(): user leaving: %s", parv[0]);
914
915 /* user_delete() takes care of removing channels and so forth */
916 user_delete (u);
917 }
918
919 static void
920 m_mode (sourceinfo_t *si, int parc, char *parv[])
921 {
922 if (*parv[0] == '#')
923 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
924 else
925 user_mode (user_find (parv[0]), parv[1]);
926 }
927
928 static void
929 m_fmode (sourceinfo_t *si, int parc, char *parv[])
930 {
931 channel_t *c;
932 bool onlydeop;
933 time_t ts;
934 char const *p;
935
936 /* :server.moo FMODE #blarp tshere +ntsklLg keymoo 1337 secks */
937 if (*parv[0] == '#')
938 {
939 c = channel_find (parv[0]);
940 if (c == NULL)
941 {
942 slog (LG_DEBUG, "m_fmode(): nonexistant channel: %s", parv[0]);
943 return;
944 }
945 ts = atoi (parv[1]);
946 if (ts == c->ts && has_protocol < PROTOCOL_NEWTS)
947 {
948 onlydeop = true;
949 p = parv[2];
950 while (*p != '\0')
951 {
952 if (!strchr ("-qaohv", *p))
953 onlydeop = false;
954 p++;
955 }
956 if (onlydeop && si->s != NULL)
957 {
958 /* ignore redundant deops generated
959 * if we lower the TS of a channel
960 * scenario: user with autoop privs recreates
961 * channel
962 * XXX could this ignore other stuff too?
963 * -- jilles */
964 slog (LG_DEBUG, "m_fmode(): ignoring %s %s: incoming TS %ld is equal to our TS %ld, and only deops", parv[0], parv[2], ts, c->ts);
965 return;
966 }
967 }
968 else if (ts > c->ts)
969 {
970 if (has_protocol < PROTOCOL_NEWTS)
971 slog (LG_DEBUG, "m_fmode(): accepting but should bounce %s %s: incoming TS %ld is newer than our TS %ld", parv[0], parv[2], ts, c->ts);
972 else
973 return;
974 }
975 else if (ts < c->ts)
976 slog (LG_DEBUG, "m_fmode(): %s %s: incoming TS %ld is older than our TS %ld, possible desync", parv[0], parv[2], ts, c->ts);
977 channel_mode (NULL, c, parc - 2, &parv[2]);
978 }
979 else
980 user_mode (user_find (parv[0]), parv[2]);
981 }
982
983 static void
984 m_kick (sourceinfo_t *si, int parc, char *parv[])
985 {
986 user_t *u = user_find (parv[1]);
987 channel_t *c = channel_find (parv[0]);
988
989 /* -> :rakaur KICK #shrike rintaun :test */
990 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
991
992 if (!u)
993 {
994 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
995 return;
996 }
997
998 if (!c)
999 {
1000 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
1001 return;
1002 }
1003
1004 if (!chanuser_find (c, u))
1005 {
1006 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
1007 return;
1008 }
1009
1010 chanuser_delete (c, u);
1011
1012 /* if they kicked us, let's rejoin */
1013 if (is_internal_client (u))
1014 {
1015 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
1016 join (parv[0], u->nick);
1017 }
1018 }
1019
1020 static void
1021 m_kill (sourceinfo_t *si, int parc, char *parv[])
1022 {
1023 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
1024 }
1025
1026 static void
1027 m_squit (sourceinfo_t *si, int parc, char *parv[])
1028 {
1029 slog (LG_DEBUG, "m_squit(): server leaving: %s", parv[0]);
1030 server_delete (parv[0]);
1031 }
1032
1033 static void
1034 m_server (sourceinfo_t *si, int parc, char *parv[])
1035 {
1036 server_t *s;
1037
1038 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
1039 s = handle_server (si, parv[0], NULL, atoi (parv[2]), parv[3]);
1040
1041 if (s != NULL && s->uplink != me.me)
1042 {
1043 /* elicit PONG for EOB detection; pinging uplink is
1044 * already done elsewhere -- jilles
1045 */
1046 sts (":%s PING %s %s", me.name, me.name, s->name);
1047 }
1048 }
1049
1050 static void
1051 m_stats (sourceinfo_t *si, int parc, char *parv[])
1052 {
1053 handle_stats (si->su, parv[0][0]);
1054 }
1055
1056 static void
1057 m_motd (sourceinfo_t *si, int parc, char *parv[])
1058 {
1059 handle_motd (si->su);
1060 }
1061
1062 static void
1063 m_admin (sourceinfo_t *si, int parc, char *parv[])
1064 {
1065 handle_admin (si->su);
1066 }
1067
1068 static void
1069 m_away (sourceinfo_t *si, int parc, char *parv[])
1070 {
1071 handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1072 }
1073
1074 static void
1075 m_join (sourceinfo_t *si, int parc, char *parv[])
1076 {
1077 channel_t *c;
1078
1079 c = channel_find (parv[0]);
1080 if (!c)
1081 {
1082 slog (LG_DEBUG, "m_join(): new channel: %s (modes lost)", parv[0]);
1083 c = channel_add (parv[0], parc > 1 ? atol (parv[1]) : NOW, si->su->server);
1084 return_if_fail (c != NULL);
1085 channel_mode (NULL, c, "+");
1086 }
1087 chanuser_add (c, si->su->nick);
1088 }
1089
1090 static void
1091 m_sajoin (sourceinfo_t *si, int parc, char *parv[])
1092 {
1093 si->su = user_find (parv[0]);
1094 if (si->su == NULL)
1095 return;
1096 m_join (si, 1, &parv[1]);
1097 }
1098
1099 static void
1100 m_sapart (sourceinfo_t *si, int parc, char *parv[])
1101 {
1102 si->su = user_find (parv[0]);
1103 if (si->su == NULL)
1104 return;
1105 m_part (si, 1, &parv[1]);
1106 }
1107
1108 static void
1109 m_sanick (sourceinfo_t *si, int parc, char *parv[])
1110 {
1111 si->su = user_find (parv[0]);
1112 if (si->su == NULL)
1113 return;
1114 m_nick (si, 1, &parv[1]);
1115 }
1116
1117 static void
1118 m_samode (sourceinfo_t *si, int parc, char *parv[])
1119 {
1120 /* note that SAMODE is not checked in any way before propagation,
1121 * and only works on channels, not users */
1122 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
1123 }
1124
1125 static void
1126 m_error (sourceinfo_t *si, int parc, char *parv[])
1127 {
1128 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1129 }
1130
1131 static void
1132 m_idle (sourceinfo_t *si, int parc, char *parv[])
1133 {
1134 if (parc == 1 && si->su != NULL)
1135 {
1136 sts (":%s IDLE %s %ld 0", parv[0], si->su->nick, NOW);
1137 }
1138 else
1139 {
1140 slog (LG_INFO, "m_idle(): Received an IDLE response but we didn't WHOIS anybody!");
1141 }
1142 }
1143
1144 static void
1145 m_opertype (sourceinfo_t *si, int parc, char *parv[])
1146 {
1147 /*
1148 * Hope this works.. InspIRCd OPERTYPE means user is an oper, mark them so
1149 * Later, we may want to look at saving their OPERTYPE for informational
1150 * purposes, or not. --w00t
1151 */
1152 user_mode (si->su, "+o");
1153 }
1154
1155 static void
1156 m_fhost (sourceinfo_t *si, int parc, char *parv[])
1157 {
1158 strlcpy (si->su->vhost, parv[0], HOSTLEN);
1159 }
1160
1161 /*
1162 * :<source server> METADATA <channel|user> <key> :<value>
1163 * The sole piece of metadata we're interested in is 'accountname', set by Services,
1164 * and kept by ircd.
1165 *
1166 * :services.barafranca METADATA w00t accountname :w00t
1167 */
1168
1169 static void
1170 m_metadata (sourceinfo_t *si, int parc, char *parv[])
1171 {
1172 user_t *u;
1173
1174 if (!irccasecmp (parv[1], "accountname"))
1175 {
1176 /* find user */
1177 u = user_find (parv[0]);
1178
1179 if (u == NULL)
1180 return;
1181
1182 handle_burstlogin (u, parv[2]);
1183 }
1184 }
1185
1186 /*
1187 * rsquit:
1188 * remote/request squit
1189 * when squitting a remote server, inspircd sends RSQUIT along the tree until it reaches the server that has
1190 * the server to be squit as a local connection, which should then close it's connection and send SQUIT back
1191 * to the rest of the network.
1192 */
1193 static void
1194 m_rsquit (sourceinfo_t *si, int parc, char *parv[])
1195 {
1196 sts (":%s SQUIT %s :Jupe removed by %s", me.name, parv[0], si->su->nick);
1197 }
1198
1199 static void
1200 m_capab (sourceinfo_t *si, int parc, char *parv[])
1201 {
1202 int i, varc;
1203 char *varv[256];
1204
1205 if (strcasecmp (parv[0], "START") == 0)
1206 {
1207 /* reset all our previously recieved CAPAB stuff */
1208 has_servicesmod = false;
1209 has_globopsmod = false;
1210 has_svshold = false;
1211 has_protocol = 0;
1212 }
1213 else if (strcasecmp (parv[0], "CAPABILITIES") == 0 && parc > 1)
1214 {
1215 varc = sjtoken (parv[1], ' ', varv);
1216 for (i = 0; i < varc; i++)
1217 {
1218 if (!strncmp (varv[i], "PROTOCOL=", 9))
1219 has_protocol = atoi (varv[i] + 9);
1220 /* XXX check/store HALFOP/CHANMAX/IDENTMAX */
1221 }
1222 }
1223 else if (strcasecmp (parv[0], "MODULES") == 0 && parc > 1)
1224 {
1225 if (strstr (parv[1], "m_services_account.so"))
1226 has_servicesmod = true;
1227 if (strstr (parv[1], "m_globops.so"))
1228 has_globopsmod = true;
1229 if (strstr (parv[1], "m_svshold.so"))
1230 has_svshold = true;
1231 if (strstr (parv[1], "m_chanprotect.so"))
1232 {
1233 ircd->uses_owner = true;
1234 ircd->uses_protect = true;
1235 }
1236 }
1237 else if (strcasecmp (parv[0], "END") == 0)
1238 {
1239 if (has_globopsmod == false && has_protocol < PROTOCOL_SNONOTICE)
1240 {
1241 slog (LG_ERROR, "m_capab(): you didn't load m_globops into inspircd. " PACKAGE_NAME " support requires this module. exiting.");
1242 exit (EXIT_FAILURE);
1243 }
1244
1245 if (has_servicesmod == false)
1246 {
1247 slog (LG_ERROR, "m_capab(): you didn't load m_services_account into inspircd. " PACKAGE_NAME " support requires this module. exiting.");
1248 exit (EXIT_FAILURE);
1249 }
1250
1251 if (has_svshold == false)
1252 {
1253 slog (LG_INFO, "m_capab(): you didn't load m_svshold into inspircd. nickname enforcers will not work.");
1254 }
1255 }
1256 else
1257 {
1258 slog (LG_DEBUG, "m_capab(): unknown CAPAB type %s - out of date protocol module?", parv[0]);
1259 }
1260 }
1261
1262 /* Server ended their burst: warn all their users if necessary -- jilles */
1263 static void
1264 server_eob (server_t *s)
1265 {
1266 node_t *n;
1267
1268 LIST_FOREACH (n, s->userlist.head)
1269 {
1270 handle_nickchange ((user_t *) n->data);
1271 }
1272 }
1273
1274 static pcommand_t const pcommands[] = {
1275 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
1276 { "PONG", m_pong, 1, MSRC_SERVER },
1277 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
1278 { "NOTICE", m_notice, 2, MSRC_USER | MSRC_SERVER },
1279 { "FJOIN", m_fjoin, 3, MSRC_SERVER },
1280 { "PART", m_part, 1, MSRC_USER },
1281 { "NICK", m_nick, 1, MSRC_USER | MSRC_SERVER },
1282 { "QUIT", m_quit, 1, MSRC_USER },
1283 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
1284 { "FMODE", m_fmode, 3, MSRC_USER | MSRC_SERVER },
1285 { "SAMODE", m_samode, 2, MSRC_USER },
1286 { "SAJOIN", m_sajoin, 2, MSRC_USER },
1287 { "SAPART", m_sapart, 2, MSRC_USER },
1288 { "SANICK", m_sanick, 2, MSRC_USER },
1289 { "SAQUIT", m_saquit, 1, MSRC_USER },
1290 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
1291 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
1292 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
1293 { "RSQUIT", m_rsquit, 1, MSRC_USER },
1294 { "SERVER", m_server, 4, MSRC_UNREG | MSRC_SERVER },
1295 { "STATS", m_stats, 2, MSRC_USER },
1296 { "MOTD", m_motd, 1, MSRC_USER },
1297 { "ADMIN", m_admin, 1, MSRC_USER },
1298 { "FTOPIC", m_ftopic, 4, MSRC_SERVER },
1299 { "JOIN", m_join, 1, MSRC_USER },
1300 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1301 { "TOPIC", m_topic, 2, MSRC_USER },
1302 { "FHOST", m_fhost, 1, MSRC_USER },
1303 { "IDLE", m_idle, 1, MSRC_USER },
1304 { "AWAY", m_away, 0, MSRC_USER },
1305 { "OPERTYPE", m_opertype, 1, MSRC_USER },
1306 { "METADATA", m_metadata, 3, MSRC_SERVER },
1307 { "CAPAB", m_capab, 1, MSRC_UNREG | MSRC_SERVER },
1308 };
1309
1310 inspircd11_handler::inspircd11_handler ()
1311 {
1312 mode_list = inspircd11_mode_list;
1313 ignore_mode_list = inspircd11_ignore_mode_list;
1314 status_mode_list = inspircd11_status_mode_list;
1315 prefix_mode_list = inspircd11_prefix_mode_list;
1316
1317 ircd = &InspIRCd;
1318
1319 pcommand_add (&pcommands);
1320
1321 server_t::callback.eob.attach (server_eob);
1322 }
1323
1324 inspircd11_handler::~inspircd11_handler ()
1325 {
1326 server_t::callback.eob.detach (server_eob);
1327
1328 pcommand_delete (&pcommands);
1329
1330 ircd = NULL;
1331
1332 prefix_mode_list = NULL;
1333 status_mode_list = NULL;
1334 ignore_mode_list = NULL;
1335 mode_list = NULL;
1336 }
1337 } // namespace protocol
1338
1339 #define FACREG_TYPE protocol::inspircd11_handler
1340 #define FACREG_TYPE_NAME "inspircd11"
1341 #define FACREG_INTERFACE_TYPE protocol::handler
1342 #include <ermyth/factory_reg.h>