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