ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/hybrid.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 * hybrid.C: This file contains protocol support for hybrid-based ircd.
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: hybrid.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/hybrid.h"
25
26 static char const rcsid[] = "$Id: hybrid.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 Hybrid = {
33 "Hybrid 7.1.2+", /* 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 false, /* Whether or not we use P10 */
41 false, /* 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_RATBOX, /* Protocol type */
50 0, /* Permanent cmodes */
51 "beI", /* Ban-like cmodes */
52 'e', /* Except mchar */
53 'I', /* Invex mchar */
54 IRCD_CIDR_BANS /* Flags */
55 };
56
57 struct hybrid_handler : handler
58 {
59 hybrid_handler ();
60 virtual ~hybrid_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 chan_lowerts (channel_t *c, user_t *u);
69 virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
70 virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
71 virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
72 virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
73 virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
74 virtual void wallchops (user_t *sender, channel_t *channel, char const * const message);
75 virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
76 virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
77 virtual void part_sts (channel_t *c, user_t *u);
78 virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
79 virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
80 virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
81 virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
82 virtual void ping_sts (void);
83 virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
84 virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
85 virtual void jupe (char const * const server, char const * const reason);
86 virtual void sethost_sts (char const * const source, char const * const target, char const * const host);
87 virtual void fnc_sts (user_t *source, user_t *u, char const * const newnick, int type);
88 virtual void holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account);
89 };
90
91 static cmode_t hybrid_mode_list[] = {
92 { 'i', CMODE_INVITE },
93 { 'm', CMODE_MOD },
94 { 'n', CMODE_NOEXT },
95 { 'p', CMODE_PRIV },
96 { 's', CMODE_SEC },
97 { 't', CMODE_TOPIC },
98 { '\0', 0 }
99 };
100
101 static extmode_t hybrid_ignore_mode_list[] = {
102 { '\0', 0 }
103 };
104
105 static cmode_t hybrid_status_mode_list[] = {
106 { 'o', CMODE_OP },
107 { 'v', CMODE_VOICE },
108 { '\0', 0 }
109 };
110
111 static cmode_t hybrid_prefix_mode_list[] = {
112 { '@', CMODE_OP },
113 { '+', CMODE_VOICE },
114 { '\0', 0 }
115 };
116
117 static bool use_rserv_support = false;
118 static bool use_tb = false;
119 static bool use_rsfnc = false;
120
121 static char ts6sid[3 + 1] = "";
122
123 /* *INDENT-ON* */
124
125 /* login to our uplink */
126 unsigned int
127 hybrid_handler::server_login (void)
128 {
129 int ret = 1;
130
131 if (!me.numeric)
132 {
133 ircd->uses_uid = false;
134 ret = sts ("PASS %s :TS", curr_uplink->pass);
135 }
136 else if (strlen (me.numeric) == 3 && isdigit (*me.numeric))
137 {
138 ircd->uses_uid = true;
139 ret = sts ("PASS %s TS 6 :%s", curr_uplink->pass, me.numeric);
140 }
141 else
142 {
143 slog (LG_ERROR, "Invalid numeric (SID) %s", me.numeric);
144 }
145 if (ret == 1)
146 return 1;
147
148 me.bursting = true;
149
150 sts ("CAPAB :QS EX IE KLN UNKLN ENCAP TB SERVICES");
151 sts ("SERVER %s 1 :%s", me.name, me.desc);
152 sts ("SVINFO %d 3 0 :%ld", ircd->uses_uid ? 6 : 5, NOW);
153
154 return 0;
155 }
156
157 /* introduce a client */
158 void
159 hybrid_handler::introduce_nick (user_t *u)
160 {
161 if (ircd->uses_uid)
162 sts (":%s UID %s 1 %ld +%s%s%s %s %s 0 %s :%s", me.numeric, u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", use_rserv_support ? "S" : "", u->user, u->host, u->uid, u->gecos);
163 else
164 sts ("NICK %s 1 %ld +%s%s%s %s %s %s :%s", u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", use_rserv_support ? "S" : "", u->user, u->host, me.name, u->gecos);
165 }
166
167 /* invite a user to a channel */
168 void
169 hybrid_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
170 {
171 /* some older TSora ircds require the sender to be
172 * on the channel, but hyb7/ratbox don't
173 * let's just assume it's not necessary -- jilles */
174 sts (":%s INVITE %s %s", CLIENT_NAME (sender), CLIENT_NAME (target), channel->name);
175 }
176
177 void
178 hybrid_handler::quit_sts (user_t *u, char const * const reason)
179 {
180 if (!me.connected)
181 return;
182
183 sts (":%s QUIT :%s", CLIENT_NAME (u), reason);
184 }
185
186 /* WALLOPS wrapper */
187 void
188 hybrid_handler::wallops_sts (char const * const text)
189 {
190 sts (":%s WALLOPS :%s", ME, text);
191 }
192
193 /* join a channel */
194 void
195 hybrid_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
196 {
197 if (isnew)
198 sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, modes, CLIENT_NAME (u));
199 else
200 sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (u));
201 }
202
203 void
204 hybrid_handler::chan_lowerts (channel_t *c, user_t *u)
205 {
206 slog (LG_DEBUG, "hybrid_chan_lowerts(): lowering TS for %s to %ld", c->name, (long) c->ts);
207 sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, channel_modes (c, true), CLIENT_NAME (u));
208 if (ircd->uses_uid)
209 chanban_clear (c);
210 }
211
212 /* kicks a user from a channel */
213 void
214 hybrid_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
215 {
216 channel_t *chan = channel_find (channel);
217 user_t *user = user_find (to);
218 user_t *from_p = user_find (from);
219
220 if (!chan || !user)
221 return;
222
223 if (chan->ts != 0 || chanuser_find (chan, from_p))
224 sts (":%s KICK %s %s :%s", CLIENT_NAME (from_p), channel, CLIENT_NAME (user), reason);
225 else
226 sts (":%s KICK %s %s :%s", ME, channel, CLIENT_NAME (user), reason);
227
228 chanuser_delete (chan, user);
229 }
230
231 /* PRIVMSG wrapper */
232 void
233 hybrid_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
234 {
235 va_list ap;
236 char buf[BUFSIZE];
237 user_t *u = user_find (from);
238 user_t *t = user_find (target);
239
240 if (!u)
241 return;
242
243 va_start (ap, fmt);
244 vsnprintf (buf, BUFSIZE, fmt, ap);
245 va_end (ap);
246
247 /* If this is to a channel, it's the snoop channel so chanserv
248 * is on it -- jilles
249 *
250 * Well, now it's operserv, but yes it's safe to assume that
251 * the source would be able to send to whatever target it is
252 * sending to. --nenolod
253 */
254 sts (":%s PRIVMSG %s :%s", CLIENT_NAME (u), t ? CLIENT_NAME (t) : target, buf);
255 }
256
257 /* NOTICE wrapper */
258 void
259 hybrid_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
260 {
261 sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, CLIENT_NAME (target), text);
262 }
263
264 void
265 hybrid_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
266 {
267 if (!strcmp (mask, "*"))
268 foreach (tld_t *tld, tld_t::list)
269 sts (":%s NOTICE %s*%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, tld->name, text);
270 else
271 sts (":%s NOTICE %s%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, mask, text);
272 }
273
274 void
275 hybrid_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
276 {
277 if (from == NULL || chanuser_find (target, from))
278 sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, target->name, text);
279 else
280 /* not on channel, let's send it from the server
281 * hyb6 won't accept this, oh well, they'll have to
282 * enable join_chans -- jilles */
283 sts (":%s NOTICE %s :[%s:%s]: %s", ME, target->name, from->nick, target->name, text);
284 }
285
286 void
287 hybrid_handler::wallchops (user_t *sender, channel_t *channel, char const * const message)
288 {
289 if (chanuser_find (channel, sender))
290 sts (":%s NOTICE @%s :%s", CLIENT_NAME (sender), channel->name, message);
291 else /* do not join for this, everyone would see -- jilles */
292 handler::wallchops (sender, channel, message);
293 }
294
295 /* numeric wrapper */
296 void
297 hybrid_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
298 {
299 va_list ap;
300 char buf[BUFSIZE];
301 user_t *t = user_find (target);
302
303 va_start (ap, fmt);
304 vsnprintf (buf, BUFSIZE, fmt, ap);
305 va_end (ap);
306
307 sts (":%s %d %s %s", ME, numeric, CLIENT_NAME (t), buf);
308 }
309
310 /* KILL wrapper */
311 void
312 hybrid_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
313 {
314 va_list ap;
315 char buf[BUFSIZE];
316 user_t *killer = user_find (from);
317 user_t *victim = user_find (nick);
318
319 va_start (ap, fmt);
320 vsnprintf (buf, BUFSIZE, fmt, ap);
321 va_end (ap);
322
323 sts (":%s KILL %s :%s!%s!%s (%s)", killer ? CLIENT_NAME (killer) : ME, victim ? CLIENT_NAME (victim) : nick, from, from, from, buf);
324 }
325
326 /* PART wrapper */
327 void
328 hybrid_handler::part_sts (channel_t *c, user_t *u)
329 {
330 sts (":%s PART %s", CLIENT_NAME (u), c->name);
331 }
332
333 /* server-to-server KLINE wrapper */
334 void
335 hybrid_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
336 {
337 if (!me.connected)
338 return;
339
340 sts (":%s KLINE %s %ld %s %s :%s", CLIENT_NAME (opersvs.me->me), server, duration, user, host, reason);
341 }
342
343 /* server-to-server UNKLINE wrapper */
344 void
345 hybrid_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
346 {
347 if (!me.connected)
348 return;
349
350 sts (":%s UNKLINE %s %s %s", CLIENT_NAME (opersvs.me->me), server, user, host);
351 }
352
353 /* topic wrapper */
354 void
355 hybrid_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
356 {
357 int joined = 0;
358
359 if (!me.connected || !c)
360 return;
361
362 /* If possible, try to use TB
363 * Note that because TOPIC does not contain topicTS, it may be
364 * off a few seconds on other servers, hence the 60 seconds here.
365 * -- jilles */
366 if (use_tb && *topic != '\0')
367 {
368 /* Restoring old topic */
369 if (ts < prevts || prevts == 0)
370 {
371 if (prevts != 0 && ts + 60 > prevts)
372 ts = prevts - 60;
373 sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
374 c->topicts = ts;
375 return;
376 }
377 /* Tweaking a topic */
378 else if (ts == prevts)
379 {
380 ts -= 60;
381 sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
382 c->topicts = ts;
383 return;
384 }
385 }
386 /* We have to be on channel to change topic.
387 * We cannot nicely change topic from the server:
388 * :server.name TOPIC doesn't propagate and TB requires
389 * us to specify an older topicts.
390 * -- jilles
391 */
392 if (!chanuser_find (c, chansvs.me->me))
393 {
394 sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (chansvs.me->me));
395 joined = 1;
396 }
397 sts (":%s TOPIC %s :%s", CLIENT_NAME (chansvs.me->me), c->name, topic);
398 if (joined)
399 sts (":%s PART %s :Topic set", CLIENT_NAME (chansvs.me->me), c->name);
400 c->topicts = NOW;
401 }
402
403 /* mode wrapper */
404 void
405 hybrid_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
406 {
407 user_t *u = user_find (sender);
408
409 if (!me.connected || !u)
410 return;
411
412 if (ircd->uses_uid)
413 sts (":%s TMODE %ld %s %s", CLIENT_NAME (u), target->ts, target->name, modes);
414 else
415 sts (":%s MODE %s %s", CLIENT_NAME (u), target->name, modes);
416 }
417
418 /* ping wrapper */
419 void
420 hybrid_handler::ping_sts (void)
421 {
422 if (!me.connected)
423 return;
424
425 sts ("PING :%s", me.name);
426 }
427
428 /* protocol-specific stuff to do on login */
429 void
430 hybrid_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
431 {
432 user_t *u = user_find (origin);
433
434 if (!me.connected || !use_rserv_support || !u)
435 return;
436
437 sts (":%s ENCAP * SU %s %s", ME, CLIENT_NAME (u), user);
438 }
439
440 /* protocol-specific stuff to do on login */
441 bool
442 hybrid_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
443 {
444 user_t *u = user_find (origin);
445
446 if (!me.connected || !use_rserv_support || !u)
447 return false;
448
449 sts (":%s ENCAP * SU %s", ME, CLIENT_NAME (u));
450 return false;
451 }
452
453 void
454 hybrid_handler::jupe (char const * const server, char const * const reason)
455 {
456 if (!me.connected)
457 return;
458
459 server_delete (server);
460 sts (":%s SQUIT %s :%s", CLIENT_NAME (opersvs.me->me), server, reason);
461 sts (":%s SERVER %s 2 :(H) %s", me.name, server, reason);
462 }
463
464 void
465 hybrid_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
466 {
467 if (use_rsfnc)
468 /* XXX assumes whole net has it -- jilles */
469 sts (":%s ENCAP %s RSFNC %s %s %lu %lu", ME, u->server->name, CLIENT_NAME (u), newnick, (unsigned long) (NOW - 60), (unsigned long) u->ts);
470 else
471 handler::fnc_sts (source, u, newnick, type);
472 }
473
474 void
475 hybrid_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
476 {
477 user_t *tu = user_find (target);
478
479 if (!tu)
480 return;
481
482 notice (source, target, "Setting your host to \2%s\2.", host);
483 sts (":%s CHGHOST %s :%s", ME, tu->nick, host);
484 }
485
486 void
487 hybrid_handler::holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account)
488 {
489 if (duration == 0)
490 return; /* can't do this safely */
491 sts (":%s ENCAP * RESV %d %s 0 :Reserved by %s for nickname owner (%s)", CLIENT_NAME (source), duration > 300 ? 300 : duration, nick, source->nick, account != NULL ? account->name : nick);
492 }
493
494 static void
495 m_topic (sourceinfo_t *si, int parc, char *parv[])
496 {
497 channel_t *c = channel_find (parv[0]);
498
499 if (c == NULL)
500 return;
501
502 handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
503 }
504
505 static void
506 m_tb (sourceinfo_t *si, int parc, char *parv[])
507 {
508 channel_t *c = channel_find (parv[0]);
509 time_t ts = atol (parv[1]);
510
511 if (c == NULL)
512 return;
513
514 if (c->topic != NULL && c->topicts <= ts)
515 {
516 slog (LG_DEBUG, "m_tb(): ignoring newer topic on %s", c->name);
517 return;
518 }
519
520 handle_topic_from (si, c, parc > 3 ? parv[2] : si->s->name, ts, parv[parc - 1]);
521 }
522
523 static void
524 m_ping (sourceinfo_t *si, int parc, char *parv[])
525 {
526 /* reply to PING's */
527 sts (":%s PONG %s %s", ME, me.name, parv[0]);
528 }
529
530 static void
531 m_pong (sourceinfo_t *si, int parc, char *parv[])
532 {
533 server_t *s;
534
535 /* someone replied to our PING */
536 if (!parv[0])
537 return;
538 s = server_find (parv[0]);
539 if (s == NULL)
540 return;
541 handle_eob (s);
542
543 if (irccasecmp (me.actual, parv[0]))
544 return;
545
546 me.uplinkpong = NOW;
547
548 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
549 if (me.bursting)
550 {
551 #ifdef HAVE_GETTIMEOFDAY
552 e_time (burstime, &burstime);
553
554 slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
555
556 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
557 #else
558 slog (LG_INFO, "m_pong(): finished synching with uplink");
559 wallops ("Finished synching to network.");
560 #endif
561
562 me.bursting = false;
563 }
564 }
565
566 static void
567 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
568 {
569 if (parc != 2)
570 return;
571
572 handle_message (si, parv[0], false, parv[1]);
573 }
574
575 static void
576 m_notice (sourceinfo_t *si, int parc, char *parv[])
577 {
578 if (parc != 2)
579 return;
580
581 handle_message (si, parv[0], true, parv[1]);
582 }
583
584 static void
585 m_sjoin (sourceinfo_t *si, int parc, char *parv[])
586 {
587 /* -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur */
588
589 channel_t *c;
590 bool keep_new_modes = true;
591 unsigned int userc;
592 char *userv[256];
593 unsigned int i;
594 time_t ts;
595 char *p;
596
597 /* :origin SJOIN ts chan modestr [key or limits] :users */
598 c = channel_find (parv[1]);
599 ts = atol (parv[0]);
600
601 if (!c)
602 {
603 slog (LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
604 c = channel_add (parv[1], ts, si->s);
605 }
606
607 if (ts == 0 || c->ts == 0)
608 {
609 if (c->ts != 0)
610 slog (LG_INFO, "m_sjoin(): server %s changing TS on %s from %ld to 0", si->s->name, c->name, (long) c->ts);
611 c->ts = 0;
612 c->callback.tschange (c);
613 }
614 else if (ts < c->ts)
615 {
616 chanuser_t *cu;
617 node_t *n;
618
619 /* the TS changed. a TS change requires the following things
620 * to be done to the channel: reset all modes to nothing, remove
621 * all status modes on known users on the channel (including ours),
622 * and set the new TS.
623 *
624 * if the source does TS6, also remove all bans
625 * note that JOIN does not do this
626 */
627
628 clear_simple_modes (c);
629 if (si->s->sid != NULL)
630 chanban_clear (c);
631
632 LIST_FOREACH (n, c->members.head)
633 {
634 cu = (chanuser_t *) n->data;
635 if (cu->user->server == me.me)
636 {
637 /* it's a service, reop */
638 sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
639 sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
640 cu->modes = CMODE_OP;
641 }
642 else
643 cu->modes = 0;
644 }
645
646 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
647
648 c->ts = ts;
649 c->callback.tschange (c);
650 }
651 else if (ts > c->ts)
652 keep_new_modes = false;
653
654 if (keep_new_modes)
655 channel_mode (NULL, c, parc - 3, parv + 2);
656
657 userc = sjtoken (parv[parc - 1], ' ', userv);
658
659 if (keep_new_modes)
660 for (i = 0; i < userc; i++)
661 chanuser_add (c, userv[i]);
662 else
663 for (i = 0; i < userc; i++)
664 {
665 p = userv[i];
666 while (*p == '@' || *p == '%' || *p == '+')
667 p++;
668 /* XXX for TS5 we should mark them deopped
669 * if they were opped and drop modes from them
670 * -- jilles */
671 chanuser_add (c, p);
672 }
673
674 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
675 channel_delete (c);
676 }
677
678 static void
679 m_join (sourceinfo_t *si, int parc, char *parv[])
680 {
681 /* -> :1JJAAAAAB JOIN 1127474195 #test +tn */
682 bool keep_new_modes = true;
683 node_t *n, *tn;
684 channel_t *c;
685 chanuser_t *cu;
686 time_t ts;
687
688 /* JOIN 0 is really a part from all channels */
689 /* be sure to allow joins to TS 0 channels -- jilles */
690 if (parv[0][0] == '0' && parc <= 2)
691 {
692 LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
693 {
694 cu = (chanuser_t *) n->data;
695 chanuser_delete (cu->chan, si->su);
696 }
697 return;
698 }
699
700 /* :user JOIN ts chan modestr [key or limits] */
701 c = channel_find (parv[1]);
702 ts = atol (parv[0]);
703
704 if (!c)
705 {
706 slog (LG_DEBUG, "m_join(): new channel: %s", parv[1]);
707 c = channel_add (parv[1], ts, si->su->server);
708 }
709
710 if (ts == 0 || c->ts == 0)
711 {
712 if (c->ts != 0)
713 slog (LG_INFO, "m_join(): server %s changing TS on %s from %ld to 0", si->su->server->name, c->name, (long) c->ts);
714 c->ts = 0;
715 c->callback.tschange (c);
716 }
717 else if (ts < c->ts)
718 {
719 /* the TS changed. a TS change requires the following things
720 * to be done to the channel: reset all modes to nothing, remove
721 * all status modes on known users on the channel (including ours),
722 * and set the new TS.
723 */
724 clear_simple_modes (c);
725
726 LIST_FOREACH (n, c->members.head)
727 {
728 cu = (chanuser_t *) n->data;
729 if (cu->user->server == me.me)
730 {
731 /* it's a service, reop */
732 sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
733 sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
734 cu->modes = CMODE_OP;
735 }
736 else
737 cu->modes = 0;
738 }
739 slog (LG_DEBUG, "m_join(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
740 c->ts = ts;
741 c->callback.tschange (c);
742 }
743 else if (ts > c->ts)
744 keep_new_modes = false;
745
746 if (keep_new_modes)
747 channel_mode (NULL, c, parc - 2, parv + 2);
748
749 chanuser_add (c, CLIENT_NAME (si->su));
750 }
751
752 static void
753 m_bmask (sourceinfo_t *si, int parc, char *parv[])
754 {
755 unsigned int ac, i;
756 char *av[256];
757 channel_t *c = channel_find (parv[1]);
758 int type;
759
760 /* :1JJ BMASK 1127474361 #services b :*!*@*evil* *!*eviluser1@* */
761 if (!c)
762 {
763 slog (LG_DEBUG, "m_bmask(): got bmask for unknown channel");
764 return;
765 }
766
767 if (atol (parv[0]) > c->ts)
768 return;
769
770 type = *parv[2];
771 if (!strchr (ircd->ban_like_modes, type))
772 {
773 slog (LG_DEBUG, "m_bmask(): got unknown type '%c'", type);
774 return;
775 }
776
777 ac = sjtoken (parv[parc - 1], ' ', av);
778
779 for (i = 0; i < ac; i++)
780 chanban_add (c, av[i], type);
781 }
782
783 static void
784 m_part (sourceinfo_t *si, int parc, char *parv[])
785 {
786 int chanc;
787 char *chanv[256];
788 int i;
789
790 chanc = sjtoken (parv[0], ',', chanv);
791 for (i = 0; i < chanc; i++)
792 {
793 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
794
795 chanuser_delete (channel_find (chanv[i]), si->su);
796 }
797 }
798
799 static void
800 m_nick (sourceinfo_t *si, int parc, char *parv[])
801 {
802 server_t *s;
803 user_t *u;
804
805 /* got the right number of args for an introduction? */
806 if (parc == 8)
807 {
808 s = server_find (parv[6]);
809 if (!s)
810 {
811 slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
812 return;
813 }
814
815 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
816
817 u = user_add (parv[0], parv[4], parv[5], NULL, NULL, NULL, parv[7], s, atoi (parv[2]));
818
819 user_mode (u, parv[3]);
820
821 /* If server is not yet EOB we will do this later.
822 * This avoids useless "please identify" -- jilles */
823 if (s->flags & SF_EOB)
824 handle_nickchange (user_find (parv[0]));
825 }
826
827 /* if it's only 2 then it's a nickname change */
828 else if (parc == 2)
829 {
830 if (!si->su)
831 {
832 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
833 return;
834 }
835
836 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
837
838 user_changenick (si->su, parv[0], atoi (parv[1]));
839
840 /* It could happen that our PING arrived late and the
841 * server didn't acknowledge EOB yet even though it is
842 * EOB; don't send double notices in that case -- jilles */
843 if (si->su->server->flags & SF_EOB)
844 handle_nickchange (si->su);
845 }
846 else
847 {
848 int i;
849 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
850
851 for (i = 0; i < parc; i++)
852 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
853 }
854 }
855
856 static void
857 m_uid (sourceinfo_t *si, int parc, char *parv[])
858 {
859 server_t *s;
860 user_t *u;
861
862 /* got the right number of args for an introduction? */
863 if (parc == 9)
864 {
865 s = si->s;
866 slog (LG_DEBUG, "m_uid(): new user on `%s': %s", s->name, parv[0]);
867
868
869 u = user_add (parv[0], parv[4], parv[5], NULL, parv[6], parv[7], parv[8], s, atoi (parv[2]));
870
871 user_mode (u, parv[3]);
872
873 /* If server is not yet EOB we will do this later.
874 * This avoids useless "please identify" -- jilles
875 */
876 if (s->flags & SF_EOB)
877 handle_nickchange (user_find (parv[0]));
878 }
879 else
880 {
881 int i;
882 slog (LG_DEBUG, "m_uid(): got UID with wrong number of params");
883
884 for (i = 0; i < parc; i++)
885 slog (LG_DEBUG, "m_uid(): parv[%d] = %s", i, parv[i]);
886 }
887 }
888
889 static void
890 m_quit (sourceinfo_t *si, int parc, char *parv[])
891 {
892 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
893
894 /* user_delete() takes care of removing channels and so forth */
895 user_delete (si->su);
896 }
897
898 static void
899 m_mode (sourceinfo_t *si, int parc, char *parv[])
900 {
901 if (*parv[0] == '#')
902 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
903 else
904 user_mode (user_find (parv[0]), parv[1]);
905 }
906
907 static void
908 m_tmode (sourceinfo_t *si, int parc, char *parv[])
909 {
910 channel_t *c;
911
912 /* -> :1JJAAAAAB TMODE 1127511579 #new +o 2JJAAAAAB */
913 c = channel_find (parv[1]);
914 if (c == NULL)
915 {
916 slog (LG_DEBUG, "m_tmode(): nonexistent channel %s", parv[1]);
917 return;
918 }
919
920 if (atol (parv[0]) > c->ts)
921 return;
922
923 channel_mode (NULL, c, parc - 2, &parv[2]);
924 }
925
926 static void
927 m_kick (sourceinfo_t *si, int parc, char *parv[])
928 {
929 user_t *u = user_find (parv[1]);
930 channel_t *c = channel_find (parv[0]);
931
932 /* -> :rakaur KICK #shrike rintaun :test */
933 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
934
935 if (!u)
936 {
937 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
938 return;
939 }
940
941 if (!c)
942 {
943 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
944 return;
945 }
946
947 if (!chanuser_find (c, u))
948 {
949 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
950 return;
951 }
952
953 chanuser_delete (c, u);
954
955 /* if they kicked us, let's rejoin */
956 if (is_internal_client (u))
957 {
958 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
959 join (parv[0], u->nick);
960 }
961 }
962
963 static void
964 m_kill (sourceinfo_t *si, int parc, char *parv[])
965 {
966 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
967 }
968
969 static void
970 m_squit (sourceinfo_t *si, int parc, char *parv[])
971 {
972 slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
973 server_delete (parv[0]);
974 }
975
976 static void
977 m_server (sourceinfo_t *si, int parc, char *parv[])
978 {
979 server_t *s;
980
981 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
982 s = handle_server (si, parv[0], si->s || !ircd->uses_uid ? NULL : ts6sid, atoi (parv[1]), parv[2]);
983
984 if (s != NULL && s->uplink != me.me)
985 {
986 /* elicit PONG for EOB detection; pinging uplink is
987 * already done elsewhere -- jilles
988 */
989 sts (":%s PING %s %s", ME, me.name, s->name);
990 }
991 }
992
993 static void
994 m_sid (sourceinfo_t *si, int parc, char *parv[])
995 {
996 /* -> :1JJ SID file. 2 00F :telnet server */
997 server_t *s;
998
999 slog (LG_DEBUG, "m_sid(): new server: %s", parv[0]);
1000 s = handle_server (si, parv[0], parv[2], atoi (parv[1]), parv[3]);
1001
1002 if (s != NULL && s->uplink != me.me)
1003 {
1004 /* elicit PONG for EOB detection; pinging uplink is
1005 * already done elsewhere -- jilles
1006 */
1007 sts (":%s PING %s %s", ME, me.name, s->sid);
1008 }
1009 }
1010
1011 static void
1012 m_stats (sourceinfo_t *si, int parc, char *parv[])
1013 {
1014 handle_stats (si->su, parv[0][0]);
1015 }
1016
1017 static void
1018 m_admin (sourceinfo_t *si, int parc, char *parv[])
1019 {
1020 handle_admin (si->su);
1021 }
1022
1023 static void
1024 m_version (sourceinfo_t *si, int parc, char *parv[])
1025 {
1026 handle_version (si->su);
1027 }
1028
1029 static void
1030 m_info (sourceinfo_t *si, int parc, char *parv[])
1031 {
1032 handle_info (si->su);
1033 }
1034
1035 static void
1036 m_whois (sourceinfo_t *si, int parc, char *parv[])
1037 {
1038 handle_whois (si->su, parv[1]);
1039 }
1040
1041 static void
1042 m_trace (sourceinfo_t *si, int parc, char *parv[])
1043 {
1044 handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
1045 }
1046
1047 static void
1048 m_away (sourceinfo_t *si, int parc, char *parv[])
1049 {
1050 handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1051 }
1052
1053 static void
1054 m_pass (sourceinfo_t *si, int parc, char *parv[])
1055 {
1056 /* TS5: PASS mypassword :TS
1057 * TS6: PASS mypassword TS 6 :sid */
1058 if (strcmp (curr_uplink->pass, parv[0]))
1059 {
1060 slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
1061 runflags |= RF_SHUTDOWN;
1062 }
1063
1064 if (ircd->uses_uid && parc > 3 && atoi (parv[2]) >= 6)
1065 strlcpy (ts6sid, parv[3], sizeof (ts6sid));
1066 else
1067 {
1068 if (ircd->uses_uid)
1069 {
1070 slog (LG_INFO, "m_pass(): uplink does not support TS6, falling back to TS5");
1071 ircd->uses_uid = false;
1072 }
1073 ts6sid[0] = '\0';
1074 }
1075 }
1076
1077 static void
1078 m_error (sourceinfo_t *si, int parc, char *parv[])
1079 {
1080 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1081 }
1082
1083 static void
1084 m_encap (sourceinfo_t *si, int parc, char *parv[])
1085 {
1086 user_t *u;
1087
1088 if (match (parv[0], me.name))
1089 return;
1090 if (!irccasecmp (parv[1], "LOGIN"))
1091 {
1092 /* :jilles ENCAP * LOGIN jilles */
1093 if (!use_rserv_support)
1094 return;
1095 if (parc < 3)
1096 return;
1097 u = si->su;
1098 if (u == NULL)
1099 return;
1100 if (u->server->flags & SF_EOB)
1101 {
1102 slog (LG_DEBUG, "m_encap(): %s sent ENCAP LOGIN for user %s outside burst", u->server->name, si->su->nick);
1103 return;
1104 }
1105 handle_burstlogin (u, parv[2]);
1106 }
1107 }
1108
1109 static void
1110 m_capab (sourceinfo_t *si, int parc, char *parv[])
1111 {
1112 char *p;
1113
1114 use_rserv_support = false;
1115 use_tb = false;
1116 use_rsfnc = false;
1117 for (p = strtok (parv[0], " "); p != NULL; p = strtok (NULL, " "))
1118 {
1119 if (!irccasecmp (p, "SERVICES"))
1120 {
1121 slog (LG_DEBUG, "m_capab(): uplink has rserv extensions, enabling support.");
1122 use_rserv_support = true;
1123 }
1124 if (!irccasecmp (p, "TB"))
1125 {
1126 slog (LG_DEBUG, "m_capab(): uplink does topic bursting, using if appropriate.");
1127 use_tb = true;
1128 }
1129 if (!irccasecmp (p, "RSFNC"))
1130 {
1131 slog (LG_DEBUG, "m_capab(): uplink does RSFNC, assuming whole net has it.");
1132 use_rsfnc = true;
1133 }
1134 }
1135
1136 /* Now we know whether or not we should enable services support,
1137 * so burst the clients.
1138 * --nenolod
1139 */
1140 services_init ();
1141 }
1142
1143 static void
1144 m_chghost (sourceinfo_t *si, int parc, char *parv[])
1145 {
1146 user_t *u = user_find (parv[0]);
1147
1148 if (!u)
1149 return;
1150
1151 strlcpy (u->vhost, parv[1], HOSTLEN);
1152 }
1153
1154 static void
1155 m_motd (sourceinfo_t *si, int parc, char *parv[])
1156 {
1157 handle_motd (si->su);
1158 }
1159
1160 /* Server ended their burst: warn all their users if necessary -- jilles */
1161 static void
1162 server_eob (server_t *s)
1163 {
1164 node_t *n;
1165
1166 LIST_FOREACH (n, s->userlist.head)
1167 {
1168 handle_nickchange ((user_t *) n->data);
1169 }
1170 }
1171
1172 static pcommand_t const pcommands[] = {
1173 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
1174 { "PONG", m_pong, 1, MSRC_SERVER },
1175 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
1176 { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
1177 { "SJOIN", m_sjoin, 4, MSRC_SERVER },
1178 { "PART", m_part, 1, MSRC_USER },
1179 { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
1180 { "QUIT", m_quit, 1, MSRC_USER },
1181 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
1182 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
1183 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
1184 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
1185 { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
1186 { "STATS", m_stats, 2, MSRC_USER },
1187 { "ADMIN", m_admin, 1, MSRC_USER },
1188 { "VERSION", m_version, 1, MSRC_USER },
1189 { "INFO", m_info, 1, MSRC_USER },
1190 { "WHOIS", m_whois, 2, MSRC_USER },
1191 { "TRACE", m_trace, 1, MSRC_USER },
1192 { "AWAY", m_away, 0, MSRC_USER },
1193 { "JOIN", m_join, 1, MSRC_USER },
1194 { "PASS", m_pass, 1, MSRC_UNREG },
1195 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1196 { "TOPIC", m_topic, 2, MSRC_USER },
1197 { "TB", m_tb, 3, MSRC_SERVER },
1198 { "ENCAP", m_encap, 2, MSRC_USER | MSRC_SERVER },
1199 { "CAPAB", m_capab, 1, MSRC_UNREG },
1200 { "UID", m_uid, 9, MSRC_SERVER },
1201 { "BMASK", m_bmask, 4, MSRC_SERVER },
1202 { "TMODE", m_tmode, 3, MSRC_USER | MSRC_SERVER },
1203 { "SID", m_sid, 4, MSRC_SERVER },
1204 { "CHGHOST", m_chghost, 2, MSRC_USER | MSRC_SERVER },
1205 { "MOTD", m_motd, 1, MSRC_USER },
1206 };
1207
1208 hybrid_handler::hybrid_handler ()
1209 {
1210 mode_list = hybrid_mode_list;
1211 ignore_mode_list = hybrid_ignore_mode_list;
1212 status_mode_list = hybrid_status_mode_list;
1213 prefix_mode_list = hybrid_prefix_mode_list;
1214
1215 ircd = &Hybrid;
1216
1217 pcommand_add (&pcommands);
1218
1219 server_t::callback.eob.attach (server_eob);
1220 }
1221
1222 hybrid_handler::~hybrid_handler ()
1223 {
1224 server_t::callback.eob.detach (server_eob);
1225
1226 pcommand_delete (&pcommands);
1227
1228 ircd = NULL;
1229
1230 prefix_mode_list = NULL;
1231 status_mode_list = NULL;
1232 ignore_mode_list = NULL;
1233 mode_list = NULL;
1234 }
1235 } // namespace protocol
1236
1237 #define FACREG_TYPE protocol::hybrid_handler
1238 #define FACREG_TYPE_NAME "hybrid"
1239 #define FACREG_INTERFACE_TYPE protocol::handler
1240 #include <ermyth/factory_reg.h>