ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/unreal.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 * unreal.C: This file contains protocol support for bahamut-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 © 2003-2004 E. Will et al.
10 * Copyright © 2005-2006 Atheme Development Group
11 * Rights to this code are documented in doc/pod/license.pod.
12 *
13 * $Id: unreal.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $
14 */
15
16 #include <boost/foreach.hpp>
17
18 #include "atheme.h"
19 #include <util/time.h>
20 #include <libermyth.h>
21 #include "servers.h"
22 #include <account/mynick.h>
23 #include <account/myuser.h>
24 #include "uplink.h"
25 #include "pmodule.h"
26 #include "protocol/unreal.h"
27
28 static char const rcsid[] = "$Id: unreal.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 Unreal = {
35 "UnrealIRCd 3.1 or later", /* IRCd name */
36 "$", /* TLD Prefix, used by Global. */
37 false, /* Whether or not we use IRCNet/TS6 UID */
38 false, /* Whether or not we use RCOMMAND */
39 true, /* Whether or not we support channel owners. */
40 true, /* Whether or not we support channel protection. */
41 true, /* Whether or not we support halfops. */
42 false, /* Whether or not we use P10 */
43 true, /* Whether or not we use vHosts. */
44 CMODE_OPERONLY | CMODE_ADMONLY, /* Oper-only cmodes */
45 CMODE_OWNER, /* Integer flag for owner channel flag. */
46 CMODE_PROTECT, /* Integer flag for protect channel flag. */
47 CMODE_HALFOP, /* Integer flag for halfops. */
48 "+q", /* Mode we set for owner. */
49 "+a", /* Mode we set for protect. */
50 "+h", /* Mode we set for halfops. */
51 PROTOCOL_UNREAL, /* Protocol type */
52 0, /* Permanent cmodes */
53 "beI", /* Ban-like cmodes */
54 'e', /* Except mchar */
55 'I', /* Invex mchar */
56 0 /* Flags */
57 };
58
59 struct unreal_handler : handler
60 {
61 unreal_handler ();
62 virtual ~unreal_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 kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
71 virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
72 virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
73 virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
74 virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
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 unreal_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 { 'c', CMODE_NOCOLOR },
99 { 'M', CMODE_MODREG },
100 { 'R', CMODE_REGONLY },
101 { 'O', CMODE_OPERONLY },
102 { 'A', CMODE_ADMONLY },
103 { 'Q', CMODE_PEACE },
104 { 'S', CMODE_STRIP },
105 { 'K', CMODE_NOKNOCK },
106 { 'V', CMODE_NOINVITE },
107 { 'C', CMODE_NOCTCP },
108 { 'u', CMODE_HIDING },
109 { 'z', CMODE_SSLONLY },
110 { 'N', CMODE_STICKY },
111 { 'G', CMODE_CENSOR },
112 { '\0', 0 }
113 };
114
115 static bool check_jointhrottle(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
116
117 static extmode_t unreal_ignore_mode_list[] = {
118 { 'j', check_jointhrottle },
119 { '\0', 0 }
120 };
121
122 static cmode_t unreal_status_mode_list[] = {
123 { 'q', CMODE_OWNER },
124 { 'a', CMODE_PROTECT },
125 { 'o', CMODE_OP },
126 { 'h', CMODE_HALFOP },
127 { 'v', CMODE_VOICE },
128 { '\0', 0 }
129 };
130
131 static cmode_t unreal_prefix_mode_list[] = {
132 { '~', CMODE_OWNER },
133 { '*', CMODE_PROTECT },
134 { '&', CMODE_PROTECT },
135 { '@', CMODE_OP },
136 { '%', CMODE_HALFOP },
137 { '+', CMODE_VOICE },
138 { '\0', 0 }
139 };
140
141 /* *INDENT-ON* */
142
143 static bool
144 check_jointhrottle (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
145 {
146 char const *p = value;
147 char const *arg2 = NULL;
148
149 while (*p != '\0')
150 {
151 if (*p == ':')
152 {
153 if (arg2 != NULL)
154 return false;
155 arg2 = p + 1;
156 }
157 else if (!isdigit (*p))
158 return false;
159 p++;
160 }
161 if (arg2 == NULL)
162 return false;
163 if (p - arg2 > 10 || arg2 - value - 1 > 10 || !atoi (value) || !atoi (arg2))
164 return false;
165 return true;
166 }
167
168 /* login to our uplink */
169 unsigned int
170 unreal_handler::server_login (void)
171 {
172 int ret;
173
174 ret = sts ("PASS %s", curr_uplink->pass);
175 if (ret == 1)
176 return 1;
177
178 me.bursting = true;
179
180 sts ("PROTOCTL TOKEN NICKv2 VHP UMODE2 SJOIN SJOIN2 SJ3 NOQUIT TKLEXT");
181 sts ("SERVER %s 1 :%s", me.name, me.desc);
182
183 services_init ();
184
185 return 0;
186 }
187
188 /* introduce a client */
189 void
190 unreal_handler::introduce_nick (user_t *u)
191 {
192 sts ("NICK %s 1 %ld %s %s %s 0 +%sS * :%s", u->nick, u->ts, u->user, u->host, me.name, "io", u->gecos);
193 }
194
195 /* invite a user to a channel */
196 void
197 unreal_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
198 {
199 sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
200 }
201
202 void
203 unreal_handler::quit_sts (user_t *u, char const * const reason)
204 {
205 if (!me.connected)
206 return;
207
208 sts (":%s QUIT :%s", u->nick, reason);
209 }
210
211 /* WALLOPS wrapper */
212 void
213 unreal_handler::wallops_sts (char const * const text)
214 {
215 sts (":%s GLOBOPS :%s", me.name, text);
216 }
217
218 /* join a channel */
219 void
220 unreal_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
221 {
222 if (isnew)
223 sts (":%s SJOIN %ld %s %s :@%s", me.name, c->ts, c->name, modes, u->nick);
224 else
225 sts (":%s SJOIN %ld %s + :@%s", me.name, c->ts, c->name, u->nick);
226 }
227
228 /* kicks a user from a channel */
229 void
230 unreal_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
231 {
232 channel_t *chan = channel_find (channel);
233 user_t *user = user_find (to);
234
235 if (!chan || !user)
236 return;
237
238 sts (":%s KICK %s %s :%s", from, channel, to, reason);
239
240 chanuser_delete (chan, user);
241 }
242
243 /* PRIVMSG wrapper */
244 void
245 unreal_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
246 {
247 va_list ap;
248 char buf[BUFSIZE];
249
250 va_start (ap, fmt);
251 vsnprintf (buf, BUFSIZE, fmt, ap);
252 va_end (ap);
253
254 sts (":%s PRIVMSG %s :%s", from, target, buf);
255 }
256
257 /* NOTICE wrapper */
258 void
259 unreal_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
260 {
261 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
262 }
263
264 void
265 unreal_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 ? from->nick : me.name, ircd->tldprefix, tld->name, text);
270 else
271 sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
272 }
273
274 void
275 unreal_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
276 {
277 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
278 }
279
280 void
281 unreal_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
282 {
283 va_list ap;
284 char buf[BUFSIZE];
285
286 va_start (ap, fmt);
287 vsnprintf (buf, BUFSIZE, fmt, ap);
288 va_end (ap);
289
290 sts (":%s %d %s %s", from, numeric, target, buf);
291 }
292
293 /* KILL wrapper */
294 void
295 unreal_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
296 {
297 va_list ap;
298 char buf[BUFSIZE];
299
300 va_start (ap, fmt);
301 vsnprintf (buf, BUFSIZE, fmt, ap);
302 va_end (ap);
303
304 sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
305 }
306
307 /* PART wrapper */
308 void
309 unreal_handler::part_sts (channel_t *c, user_t *u)
310 {
311 sts (":%s PART %s", u->nick, c->name);
312 }
313
314 /* server-to-server KLINE wrapper */
315 void
316 unreal_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
317 {
318 if (!me.connected)
319 return;
320
321 sts (":%s TKL + G %s %s %s 0 %ld :%s", me.name, user, host, opersvs.nick, NOW, reason);
322 }
323
324 /* server-to-server UNKLINE wrapper */
325 void
326 unreal_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
327 {
328 if (!me.connected)
329 return;
330
331 sts (":%s TKL - G %s %s %s", me.name, user, host, opersvs.nick);
332 }
333
334 /* topic wrapper */
335 void
336 unreal_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
337 {
338 if (!me.connected || !c)
339 return;
340
341 sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, setter, ts, topic);
342 }
343
344 /* mode wrapper */
345 void
346 unreal_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
347 {
348 if (!me.connected)
349 return;
350
351 sts (":%s MODE %s %s", sender, target->name, modes);
352 }
353
354 /* ping wrapper */
355 void
356 unreal_handler::ping_sts (void)
357 {
358 if (!me.connected)
359 return;
360
361 sts ("PING :%s", me.name);
362 }
363
364 /* protocol-specific stuff to do on login */
365 void
366 unreal_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
367 {
368 user_t *u;
369
370 if (!me.connected)
371 return;
372
373 u = user_find (origin);
374 if (u == NULL)
375 return;
376
377 /* Can only do this for nickserv, and can only record identified
378 * state if logged in to correct nick, sorry -- jilles
379 */
380 /* imo, we should be using SVS2MODE to show the modechange here and on logout --w00t */
381 if (should_reg_umode (u))
382 sts (":%s SVS2MODE %s +rd %ld", nicksvs.nick, origin, u->ts);
383 }
384
385 /* protocol-specific stuff to do on logout */
386 bool
387 unreal_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
388 {
389 if (!me.connected)
390 return false;
391
392 if (!nicksvs.no_nick_ownership)
393 sts (":%s SVS2MODE %s -r+d 0", nicksvs.nick, origin);
394
395 return false;
396 }
397
398 void
399 unreal_handler::jupe (char const * const server, char const * const reason)
400 {
401 if (!me.connected)
402 return;
403
404 server_delete (server);
405 sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
406 sts (":%s SERVER %s 2 :%s", me.name, server, reason);
407 }
408
409 void
410 unreal_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
411 {
412 if (!me.connected)
413 return;
414
415 notice (source, target, "Setting your host to \2%s\2.", host);
416 sts (":%s CHGHOST %s :%s", source, target, host);
417 }
418
419 void
420 unreal_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
421 {
422 sts (":%s SVSNICK %s %s %lu", source->nick, u->nick, newnick, (unsigned long) (NOW - 60));
423 }
424
425 void
426 unreal_handler::holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account)
427 {
428 sts (":%s SVSHOLD %s %d :Reserved by %s for nickname owner (%s)", source->nick, nick, duration, source->nick, account != NULL ? account->name : nick);
429 }
430
431 static void
432 m_topic (sourceinfo_t *si, int parc, char *parv[])
433 {
434 channel_t *c = channel_find (parv[0]);
435
436 if (!c)
437 return;
438
439 /* Our uplink is trying to change the topic during burst,
440 * and we have already set a topic. Assume our change won.
441 * -- jilles */
442 if (si->s != NULL && si->s->uplink == me.me && !(si->s->flags & SF_EOB) && c->topic != NULL)
443 return;
444
445 handle_topic_from (si, c, parv[1], atol (parv[2]), parv[3]);
446 }
447
448 static void
449 m_ping (sourceinfo_t *si, int parc, char *parv[])
450 {
451 /* reply to PING's */
452 sts (":%s PONG %s %s", me.name, me.name, parv[0]);
453 }
454
455 static void
456 m_pong (sourceinfo_t *si, int parc, char *parv[])
457 {
458 server_t *s;
459
460 /* someone replied to our PING */
461 if (!parv[0])
462 return;
463 s = server_find (parv[0]);
464 if (s == NULL)
465 return;
466 handle_eob (s);
467
468 if (irccasecmp (me.actual, parv[0]))
469 return;
470
471 me.uplinkpong = NOW;
472
473 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
474 if (me.bursting)
475 {
476 #ifdef HAVE_GETTIMEOFDAY
477 e_time (burstime, &burstime);
478
479 slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
480
481 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
482 #else
483 slog (LG_INFO, "m_pong(): finished synching with uplink");
484 wallops ("Finished synching to network.");
485 #endif
486
487 me.bursting = false;
488 }
489 }
490
491 static void
492 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
493 {
494 if (parc != 2)
495 return;
496
497 handle_message (si, parv[0], false, parv[1]);
498 }
499
500 static void
501 m_notice (sourceinfo_t *si, int parc, char *parv[])
502 {
503 if (parc != 2)
504 return;
505
506 handle_message (si, parv[0], true, parv[1]);
507 }
508
509 static void
510 remove_our_modes (channel_t *c)
511 {
512 /* the TS changed. a TS change requires the following things
513 * to be done to the channel: reset all modes to nothing, remove
514 * all status modes on known users on the channel (including ours),
515 * and set the new TS.
516 */
517 chanuser_t *cu;
518 node_t *n;
519
520 clear_simple_modes (c);
521
522 LIST_FOREACH (n, c->members.head)
523 {
524 cu = (chanuser_t *) n->data;
525 cu->modes = 0;
526 }
527 }
528
529 static void
530 m_sjoin (sourceinfo_t *si, int parc, char *parv[])
531 {
532 /*
533 * -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur
534 * also:
535 * -> :nenolod_ SJOIN 1117334567 #chat
536 * also:
537 * -> SJOIN 1117334567 #chat :@nenolod
538 */
539
540 channel_t *c;
541 unsigned int userc;
542 char *userv[256];
543 unsigned int i;
544 time_t ts;
545
546 if (parc >= 4)
547 {
548 /* :origin SJOIN ts chan modestr [key or limits] :users */
549 c = channel_find (parv[1]);
550 ts = atol (parv[0]);
551
552 if (!c)
553 {
554 slog (LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
555 c = channel_add (parv[1], ts, si->s);
556 }
557
558 if (ts < c->ts)
559 {
560 remove_our_modes (c);
561 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
562 c->ts = ts;
563 c->callback.tschange (c);
564 }
565
566 channel_mode (NULL, c, parc - 3, parv - 2);
567 userc = sjtoken (parv[parc - 1], ' ', userv);
568
569 for (i = 0; i < userc; i++)
570 if (*userv[i] == '&') /* channel ban */
571 chanban_add (c, userv[i] + 1, 'b');
572 else if (*userv[i] == '"') /* exception */
573 chanban_add (c, userv[i] + 1, 'e');
574 else if (*userv[i] == '\'') /* invex */
575 chanban_add (c, userv[i] + 1, 'I');
576 else
577 chanuser_add (c, userv[i]);
578 }
579 else if (parc == 3)
580 {
581 /* :origin SJOIN ts chan :users */
582 c = channel_find (parv[1]);
583 ts = atol (parv[0]);
584
585 if (!c)
586 {
587 slog (LG_DEBUG, "m_sjoin(): new channel: %s (modes lost)", parv[1]);
588 c = channel_add (parv[1], ts, si->s);
589 }
590
591 if (ts < c->ts)
592 {
593 remove_our_modes (c);
594 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
595 c->ts = ts;
596 c->callback.tschange (c);
597 }
598
599 channel_mode (NULL, c, "+");
600
601 userc = sjtoken (parv[parc - 1], ' ', userv);
602
603 for (i = 0; i < userc; i++)
604 if (*userv[i] == '&') /* channel ban */
605 chanban_add (c, userv[i] + 1, 'b');
606 else if (*userv[i] == '"') /* exception */
607 chanban_add (c, userv[i] + 1, 'e');
608 else if (*userv[i] == '\'') /* invex */
609 chanban_add (c, userv[i] + 1, 'I');
610 else
611 chanuser_add (c, userv[i]);
612 }
613 else if (parc == 2)
614 {
615 c = channel_find (parv[1]);
616 ts = atol (parv[0]);
617 if (!c)
618 {
619 slog (LG_DEBUG, "m_sjoin(): new channel: %s (modes lost)", parv[1]);
620 c = channel_add (parv[1], ts, si->su->server);
621 }
622
623 if (ts < c->ts)
624 {
625 remove_our_modes (c);
626 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
627 c->ts = ts;
628 /* XXX lost modes! -- XXX - pardon? why do we worry about this? TS reset requires modes reset.. */
629 c->callback.tschange (c);
630 }
631
632 channel_mode (NULL, c, "+");
633
634 chanuser_add (c, si->su->nick);
635 }
636 else
637 return;
638
639 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
640 channel_delete (c);
641 }
642
643 static void
644 m_part (sourceinfo_t *si, int parc, char *parv[])
645 {
646 int chanc;
647 char *chanv[256];
648 int i;
649
650 chanc = sjtoken (parv[0], ',', chanv);
651 for (i = 0; i < chanc; i++)
652 {
653 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
654
655 chanuser_delete (channel_find (chanv[i]), si->su);
656 }
657 }
658
659 static void
660 m_nick (sourceinfo_t *si, int parc, char *parv[])
661 {
662 server_t *s;
663 user_t *u;
664 bool realchange;
665
666 if (parc == 10)
667 {
668 s = server_find (parv[5]);
669 if (!s)
670 {
671 slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[5]);
672 return;
673 }
674
675 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
676
677 u = user_add (parv[0], parv[3], parv[4], parv[8], NULL, NULL, parv[9], s, atoi (parv[2]));
678
679 user_mode (u, parv[7]);
680
681 /* If the user's SVID is equal to their nick TS,
682 * they're properly logged in -- jilles */
683 if (u->ts > 100 && (time_t) atoi (parv[6]) == u->ts)
684 handle_burstlogin (u, NULL);
685
686 handle_nickchange (u);
687 }
688
689 /* if it's only 2 then it's a nickname change */
690 else if (parc == 2)
691 {
692 if (!si->su)
693 {
694 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
695 return;
696 }
697
698 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
699
700 realchange = irccasecmp (si->su->nick, parv[0]);
701
702 user_changenick (si->su, parv[0], atoi (parv[1]));
703
704 /* fix up +r if necessary -- jilles */
705 if (realchange && !nicksvs.no_nick_ownership)
706 {
707 if (should_reg_umode (si->su))
708 /* changed nick to registered one, reset +r */
709 sts (":%s SVS2MODE %s +rd %ld", nicksvs.nick, parv[0], atoi (parv[1]));
710 else
711 /* changed from registered nick, remove +r */
712 sts (":%s SVS2MODE %s -r+d 0", nicksvs.nick, parv[0]);
713 }
714
715 handle_nickchange (si->su);
716 }
717 else
718 {
719 int i;
720 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
721
722 for (i = 0; i < parc; i++)
723 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
724 }
725 }
726
727 static void
728 m_quit (sourceinfo_t *si, int parc, char *parv[])
729 {
730 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
731
732 /* user_delete() takes care of removing channels and so forth */
733 user_delete (si->su);
734 }
735
736 static void
737 m_mode (sourceinfo_t *si, int parc, char *parv[])
738 {
739 if (*parv[0] == '#')
740 channel_mode (NULL, channel_find (parv[0]), parc - 2, &parv[1]);
741 else
742 user_mode (user_find (parv[0]), parv[1]);
743 }
744
745 static void
746 m_umode (sourceinfo_t *si, int parc, char *parv[])
747 {
748 user_mode (si->su, parv[0]);
749 }
750
751 static void
752 m_kick (sourceinfo_t *si, int parc, char *parv[])
753 {
754 user_t *u = user_find (parv[1]);
755 channel_t *c = channel_find (parv[0]);
756
757 /* -> :rakaur KICK #shrike rintaun :test */
758 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
759
760 if (!u)
761 {
762 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
763 return;
764 }
765
766 if (!c)
767 {
768 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
769 return;
770 }
771
772 if (!chanuser_find (c, u))
773 {
774 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
775 return;
776 }
777
778 chanuser_delete (c, u);
779
780 /* if they kicked us, let's rejoin */
781 if (is_internal_client (u))
782 {
783 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
784 join (parv[0], u->nick);
785 }
786 }
787
788 static void
789 m_kill (sourceinfo_t *si, int parc, char *parv[])
790 {
791 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
792 }
793
794 static void
795 m_squit (sourceinfo_t *si, int parc, char *parv[])
796 {
797 slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
798 server_delete (parv[0]);
799 }
800
801 static void
802 m_server (sourceinfo_t *si, int parc, char *parv[])
803 {
804 server_t *s;
805
806 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
807 s = handle_server (si, parv[0], NULL, atoi (parv[1]), parv[2]);
808
809 if (s != NULL && s->uplink != me.me)
810 {
811 /* elicit PONG for EOB detection; pinging uplink is
812 * already done elsewhere -- jilles
813 */
814 sts (":%s PING %s %s", me.name, me.name, s->name);
815 }
816 }
817
818 static void
819 m_stats (sourceinfo_t *si, int parc, char *parv[])
820 {
821 handle_stats (si->su, parv[0][0]);
822 }
823
824 static void
825 m_admin (sourceinfo_t *si, int parc, char *parv[])
826 {
827 handle_admin (si->su);
828 }
829
830 static void
831 m_version (sourceinfo_t *si, int parc, char *parv[])
832 {
833 handle_version (si->su);
834 }
835
836 static void
837 m_info (sourceinfo_t *si, int parc, char *parv[])
838 {
839 handle_info (si->su);
840 }
841
842 static void
843 m_whois (sourceinfo_t *si, int parc, char *parv[])
844 {
845 handle_whois (si->su, parv[1]);
846 }
847
848 static void
849 m_trace (sourceinfo_t *si, int parc, char *parv[])
850 {
851 handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
852 }
853
854 static void
855 m_away (sourceinfo_t *si, int parc, char *parv[])
856 {
857 handle_away (si->su, parc >= 1 ? parv[0] : NULL);
858 }
859
860 static void
861 m_join (sourceinfo_t *si, int parc, char *parv[])
862 {
863 chanuser_t *cu;
864 node_t *n, *tn;
865
866 /* JOIN 0 is really a part from all channels */
867 if (parv[0][0] == '0')
868 {
869 LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
870 {
871 cu = (chanuser_t *) n->data;
872 chanuser_delete (cu->chan, si->su);
873 }
874 }
875 }
876
877 static void
878 m_pass (sourceinfo_t *si, int parc, char *parv[])
879 {
880 if (strcmp (curr_uplink->pass, parv[0]))
881 {
882 slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
883 runflags |= RF_SHUTDOWN;
884 }
885 }
886
887 static void
888 m_error (sourceinfo_t *si, int parc, char *parv[])
889 {
890 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
891 }
892
893 static void
894 m_chghost (sourceinfo_t *si, int parc, char *parv[])
895 {
896 user_t *u = user_find (parv[0]);
897
898 if (!u)
899 return;
900
901 strlcpy (u->vhost, parv[1], HOSTLEN);
902 }
903
904 static void
905 m_motd (sourceinfo_t *si, int parc, char *parv[])
906 {
907 handle_motd (si->su);
908 }
909
910 static void
911 nick_group (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
912 {
913 user_t *u;
914
915 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
916 u = si->su;
917 else
918 u = user_find_named (mn->nick);
919
920 if (u != NULL && should_reg_umode (u))
921 sts (":%s SVS2MODE %s +rd %ld", nicksvs.nick, u->nick, u->ts);
922 }
923
924 static void
925 nick_ungroup (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
926 {
927 user_t *u;
928
929 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
930 u = si->su;
931 else
932 u = user_find_named (mn->nick);
933
934 if (u != NULL && !nicksvs.no_nick_ownership)
935 sts (":%s SVS2MODE %s -r+d 0", nicksvs.nick, u->nick);
936 }
937
938 static pcommand_t const pcommands[] = {
939 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
940 { "PONG", m_pong, 1, MSRC_SERVER },
941 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
942 { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
943 { "SJOIN", m_sjoin, 2, MSRC_USER | MSRC_SERVER },
944 { "PART", m_part, 1, MSRC_USER },
945 { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
946 { "QUIT", m_quit, 1, MSRC_USER },
947 { "UMODE2", m_umode, 1, MSRC_USER },
948 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
949 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
950 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
951 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
952 { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
953 { "STATS", m_stats, 2, MSRC_USER },
954 { "ADMIN", m_admin, 1, MSRC_USER },
955 { "VERSION", m_version, 1, MSRC_USER },
956 { "INFO", m_info, 1, MSRC_USER },
957 { "WHOIS", m_whois, 2, MSRC_USER },
958 { "TRACE", m_trace, 1, MSRC_USER },
959 { "AWAY", m_away, 0, MSRC_USER },
960 { "JOIN", m_join, 1, MSRC_USER },
961 { "PASS", m_pass, 1, MSRC_UNREG },
962 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
963 { "TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER },
964 { "CHGHOST", m_chghost, 2, MSRC_USER | MSRC_SERVER },
965 { "MOTD", m_motd, 1, MSRC_USER },
966
967 /*
968 * for fun, and to give nenolod a heart attack
969 * whenever he reads unreal.c, let us do tokens... --w00t
970 *
971 * oh, as a side warning: don't remove the above. not only
972 * are they for compatability, but unreal's server protocol
973 * can be odd in places (ie not sending JOIN token after synch
974 * with no SJOIN, not using KILL token... etc. --w00t.
975 */
976
977 { "8", m_ping, 1, MSRC_USER | MSRC_SERVER },
978 { "9", m_pong, 1, MSRC_SERVER },
979 { "!", m_privmsg, 2, MSRC_USER },
980 { "B", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
981 { "~", m_sjoin, 2, MSRC_USER | MSRC_SERVER },
982 { "D", m_part, 1, MSRC_USER },
983 { "&", m_nick, 2, MSRC_USER | MSRC_SERVER },
984 { ",", m_quit, 1, MSRC_USER },
985 { "|", m_umode, 1, MSRC_USER },
986 { "G", m_mode, 2, MSRC_USER | MSRC_SERVER },
987 { "H", m_kick, 2, MSRC_USER | MSRC_SERVER },
988 { ".", m_kill, 1, MSRC_USER | MSRC_SERVER },
989 { "-", m_squit, 1, MSRC_USER | MSRC_SERVER },
990 { "'", m_server, 3, MSRC_UNREG | MSRC_SERVER },
991 { "2", m_stats, 2, MSRC_USER },
992 { "@", m_admin, 1, MSRC_USER },
993 { "+", m_version, 1, MSRC_USER },
994 { "/", m_info, 1, MSRC_USER },
995 { "#", m_whois, 2, MSRC_USER },
996 { "b", m_trace, 1, MSRC_USER },
997 { "6", m_away, 0, MSRC_USER },
998 { "C", m_join, 1, MSRC_USER },
999 { "<", m_pass, 1, MSRC_UNREG },
1000 { "5", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1001 { ")", m_topic, 4, MSRC_USER | MSRC_SERVER },
1002 { "AL", m_chghost, 2, MSRC_USER | MSRC_SERVER },
1003 { "F", m_motd, 1, MSRC_USER },
1004 };
1005
1006 unreal_handler::unreal_handler ()
1007 {
1008 mode_list = unreal_mode_list;
1009 ignore_mode_list = unreal_ignore_mode_list;
1010 status_mode_list = unreal_status_mode_list;
1011 prefix_mode_list = unreal_prefix_mode_list;
1012
1013 ircd = &Unreal;
1014
1015 pcommand_add (&pcommands);
1016
1017 mynick_t::callback.group.attach (nick_group);
1018 mynick_t::callback.ungroup.attach (nick_ungroup);
1019 }
1020
1021 unreal_handler::~unreal_handler ()
1022 {
1023 mode_list = NULL;
1024 ignore_mode_list = NULL;
1025 status_mode_list = NULL;
1026 prefix_mode_list = NULL;
1027
1028 ircd = NULL;
1029
1030 mynick_t::callback.group.detach (nick_group);
1031 mynick_t::callback.ungroup.detach (nick_ungroup);
1032
1033 pcommand_delete (&pcommands);
1034 }
1035 } // namespace protocol
1036
1037 #define FACREG_TYPE protocol::unreal_handler
1038 #define FACREG_TYPE_NAME "unreal"
1039 #define FACREG_INTERFACE_TYPE protocol::handler
1040 #include <ermyth/factory_reg.h>