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