ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/ircnet.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 * ircnet.C: This file contains protocol support for IRCnet ircd's.
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 William Pitcock, et al.
10 * Rights to this code are documented in doc/pod/license.pod.
11 *
12 * $Id: ircnet.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 "uplink.h"
22 #include "pmodule.h"
23 #include "protocol/ircnet.h"
24
25 static char const rcsid[] = "$Id: ircnet.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $";
26
27 /* *INDENT-OFF* */
28
29 namespace protocol
30 {
31 static ircd_t IRCNet = {
32 "ircd 2.11.1p1 or later", /* IRCd name */
33 "$$", /* TLD Prefix, used by Global. */
34 true, /* Whether or not we use IRCNet/TS6 UID */
35 false, /* Whether or not we use RCOMMAND */
36 false, /* Whether or not we support channel owners. */
37 false, /* Whether or not we support channel protection. */
38 false, /* Whether or not we support halfops. */
39 false, /* Whether or not we use P10 */
40 false, /* Whether or not we use vHosts. */
41 0, /* Oper-only cmodes */
42 0, /* Integer flag for owner channel flag. */
43 0, /* Integer flag for protect channel flag. */
44 0, /* Integer flag for halfops. */
45 "+", /* Mode we set for owner. */
46 "+", /* Mode we set for protect. */
47 "+", /* Mode we set for halfops. */
48 PROTOCOL_IRCNET, /* Protocol type */
49 0, /* Permanent cmodes */
50 "beIR", /* Ban-like cmodes */
51 'e', /* Except mchar */
52 'I', /* Invex mchar */
53 IRCD_CIDR_BANS /* Flags */
54 };
55
56 struct ircnet_handler : handler
57 {
58 ircnet_handler ();
59 virtual ~ircnet_handler ();
60
61 virtual unsigned int server_login (void);
62 virtual void introduce_nick (user_t *u);
63 virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
64 virtual void quit_sts (user_t *u, char const * const reason);
65 virtual void wallops_sts (char const * const text);
66 virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
67 virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
68 virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
69 virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
70 virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
71 virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
72 virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
73 virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
74 virtual void part_sts (channel_t *c, user_t *u);
75 virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
76 virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
77 virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
78 virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
79 virtual void ping_sts (void);
80 virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
81 virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
82 virtual void jupe (char const * const server, char const * const reason);
83 };
84
85 static cmode_t ircnet_mode_list[] = {
86 { 'i', CMODE_INVITE },
87 { 'm', CMODE_MOD },
88 { 'n', CMODE_NOEXT },
89 { 'p', CMODE_PRIV },
90 { 's', CMODE_SEC },
91 { 't', CMODE_TOPIC },
92 { '\0', 0 }
93 };
94
95 static extmode_t ircnet_ignore_mode_list[] = {
96 { '\0', 0 }
97 };
98
99 static cmode_t ircnet_status_mode_list[] = {
100 { 'o', CMODE_OP },
101 { 'v', CMODE_VOICE },
102 { '\0', 0 }
103 };
104
105 static cmode_t ircnet_prefix_mode_list[] = {
106 { '@', CMODE_OP },
107 { '+', CMODE_VOICE },
108 { '\0', 0 }
109 };
110
111 /* *INDENT-ON* */
112
113 /* login to our uplink */
114 unsigned int
115 ircnet_handler::server_login (void)
116 {
117 int ret;
118
119 ret = sts ("PASS %s 0211010000 IRC|aDEFiIJMuw P", curr_uplink->pass);
120 if (ret == 1)
121 return 1;
122
123 me.bursting = true;
124
125 sts ("SERVER %s 1 %s :%s", me.name, me.numeric, me.desc);
126
127 services_init ();
128
129 sts (":%s EOB", me.numeric);
130
131 return 0;
132 }
133
134 /* introduce a client */
135 void
136 ircnet_handler::introduce_nick (user_t *u)
137 {
138 sts (":%s UNICK %s %s %s %s 0.0.0.0 +%s :%s", me.numeric, u->nick, u->uid, u->user, u->host, "io", u->gecos);
139 }
140
141 /* invite a user to a channel */
142 void
143 ircnet_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
144 {
145 int joined = 0;
146
147 /* Need to join to invite -- jilles */
148 if (!chanuser_find (channel, sender))
149 {
150 sts (":%s NJOIN %s :@%s", ME, channel->name, CLIENT_NAME (sender));
151 joined = 1;
152 }
153 /* ircnet's UID implementation is incomplete, in many places,
154 * like this one, it does not accept UIDs -- jilles */
155 sts (":%s INVITE %s %s", CLIENT_NAME (sender), target->nick, channel->name);
156 if (joined)
157 sts (":%s PART %s :Invited %s", CLIENT_NAME (sender), channel->name, target->nick);
158 }
159
160 void
161 ircnet_handler::quit_sts (user_t *u, char const * const reason)
162 {
163 if (!me.connected)
164 return;
165
166 sts (":%s QUIT :%s", u->nick, reason);
167 }
168
169 /* WALLOPS wrapper */
170 void
171 ircnet_handler::wallops_sts (char const * const text)
172 {
173 sts (":%s WALLOPS :%s", me.name, text);
174 }
175
176 /* join a channel */
177 void
178 ircnet_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
179 {
180 sts (":%s NJOIN %s :@%s", me.numeric, c->name, u->uid);
181 if (isnew && modes[0] && modes[1])
182 sts (":%s MODE %s %s", me.numeric, c->name, modes);
183 }
184
185 /* kicks a user from a channel */
186 void
187 ircnet_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
188 {
189 channel_t *chan = channel_find (channel);
190 user_t *user = user_find (to);
191 user_t *from_p = user_find (from);
192
193 if (!chan || !user)
194 return;
195
196 /* sigh server kicks will generate snotes
197 * but let's avoid joining N times for N kicks */
198 sts (":%s KICK %s %s :%s", from_p != NULL && chanuser_find (chan, from_p) ? CLIENT_NAME (from_p) : ME, channel, CLIENT_NAME (user), reason);
199
200 chanuser_delete (chan, user);
201 }
202
203 /* PRIVMSG wrapper */
204 void
205 ircnet_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
206 {
207 va_list ap;
208 char buf[BUFSIZE];
209
210 va_start (ap, fmt);
211 vsnprintf (buf, BUFSIZE, fmt, ap);
212 va_end (ap);
213
214 sts (":%s PRIVMSG %s :%s", from, target, buf);
215 }
216
217 /* NOTICE wrapper */
218 void
219 ircnet_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
220 {
221 sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, CLIENT_NAME (target), text);
222 }
223
224 void
225 ircnet_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
226 {
227 if (!strcmp (mask, "*"))
228 foreach (tld_t *tld, tld_t::list)
229 sts (":%s NOTICE %s*%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, tld->name, text);
230 else
231 sts (":%s NOTICE %s%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, mask, text);
232 }
233
234 void
235 ircnet_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
236 {
237 if (from == NULL || chanuser_find (target, from))
238 sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, target->name, text);
239 else
240 sts (":%s NOTICE %s :[%s:%s] %s", ME, target->name, from->nick, target->name, text);
241 }
242
243 /* numeric wrapper */
244 void
245 ircnet_handler::numeric_sts (char const * const from, int numeric, 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 %d %s %s", from, numeric, target, buf);
255 }
256
257 /* KILL wrapper */
258 void
259 ircnet_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
260 {
261 va_list ap;
262 char buf[BUFSIZE];
263
264 va_start (ap, fmt);
265 vsnprintf (buf, BUFSIZE, fmt, ap);
266 va_end (ap);
267
268 sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
269 }
270
271 /* PART wrapper */
272 void
273 ircnet_handler::part_sts (channel_t *c, user_t *u)
274 {
275 sts (":%s PART %s", u->nick, c->name);
276 }
277
278 /* server-to-server KLINE wrapper */
279 void
280 ircnet_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
281 {
282 if (!me.connected)
283 return;
284
285 /* this won't propagate!
286 * you'll need some bot/service on each server to do that */
287 if (irccasecmp (server, me.actual) && cnt.server > 2)
288 wallops ("Missed a tkline");
289 sts (":%s TKLINE %lds %s@%s :%s", opersvs.nick, duration, user, host, reason);
290 }
291
292 /* server-to-server UNKLINE wrapper */
293 void
294 ircnet_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
295 {
296 if (!me.connected)
297 return;
298
299 if (irccasecmp (server, me.actual) && cnt.server > 2)
300 wallops ("Missed an untkline");
301 sts (":%s UNTKLINE %s@%s", opersvs.nick, user, host);
302 }
303
304 /* topic wrapper */
305 void
306 ircnet_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
307 {
308 int joined = 0;
309
310 if (!me.connected || !c)
311 return;
312
313 /* Need to join to set topic -- jilles */
314 if (!chanuser_find (c, chansvs.me->me))
315 {
316 sts (":%s NJOIN %s :@%s", ME, c->name, CLIENT_NAME (chansvs.me->me));
317 joined = 1;
318 }
319 sts (":%s TOPIC %s :%s", CLIENT_NAME (chansvs.me->me), c->name, topic);
320 if (joined)
321 sts (":%s PART %s :Topic set", CLIENT_NAME (chansvs.me->me), c->name);
322 }
323
324 /* mode wrapper */
325 void
326 ircnet_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
327 {
328 user_t *u;
329
330 if (!me.connected)
331 return;
332
333 u = user_find (sender);
334
335 /* send it from the server if that service isn't on channel
336 * -- jilles */
337 sts (":%s MODE %s %s", chanuser_find (target, u) ? CLIENT_NAME (u) : ME, target->name, modes);
338 }
339
340 /* ping wrapper */
341 void
342 ircnet_handler::ping_sts (void)
343 {
344 if (!me.connected)
345 return;
346
347 sts ("PING :%s", me.name);
348 }
349
350 /* protocol-specific stuff to do on login */
351 void
352 ircnet_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
353 {
354 /* nothing to do on ratbox */
355 return;
356 }
357
358 /* protocol-specific stuff to do on login */
359 bool
360 ircnet_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
361 {
362 /* nothing to do on ratbox */
363 return false;
364 }
365
366 void
367 ircnet_handler::jupe (char const * const server, char const * const reason)
368 {
369 static char sid[4 + 1];
370 int i;
371 server_t *s;
372
373 if (!me.connected)
374 return;
375
376 sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
377 s = server_find (server);
378 /* We need to wait for the SQUIT to be processed -- jilles */
379 if (s != NULL)
380 {
381 s->flags |= SF_JUPE_PENDING;
382 return;
383 }
384
385 /* dirty dirty make up some sid */
386 if (sid[0] == '\0')
387 strlcpy (sid, me.numeric, sizeof sid);
388 do
389 {
390 i = 3;
391 for (;;)
392 {
393 if (sid[i] == 'Z')
394 {
395 sid[i] = '0';
396 i--;
397 /* eek, no more sids */
398 if (i < 0)
399 return;
400 continue;
401 }
402 else if (sid[i] == '9')
403 sid[i] = 'A';
404 else
405 sid[i]++;
406 break;
407 }
408 }
409 while (server_find (sid));
410
411 sts (":%s SERVER %s 2 %s 0211010000 :%s", me.name, server, sid, reason);
412 }
413
414 static void
415 m_topic (sourceinfo_t *si, int parc, char *parv[])
416 {
417 channel_t *c = channel_find (parv[0]);
418
419 if (!c)
420 return;
421
422 handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
423 }
424
425 static void
426 m_ping (sourceinfo_t *si, int parc, char *parv[])
427 {
428 /* reply to PING's */
429 sts (":%s PONG %s %s", me.name, me.name, parv[0]);
430 }
431
432 static void
433 m_pong (sourceinfo_t *si, int parc, char *parv[])
434 {
435 /* someone replied to our PING */
436 if ((!parv[0]) || (strcasecmp (me.actual, parv[0])))
437 return;
438
439 me.uplinkpong = NOW;
440
441 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
442 }
443
444 static void
445 m_eob (sourceinfo_t *si, int parc, char *parv[])
446 {
447 server_t *serv;
448 char sidbuf[4 + 1], *p;
449
450 handle_eob (si->s);
451 if (parc >= 1)
452 {
453 sidbuf[4] = '\0';
454 p = parv[0];
455 while (p[0] && p[1] && p[2] && p[3])
456 {
457 memcpy (sidbuf, p, 4);
458 serv = server_find (sidbuf);
459 handle_eob (serv);
460 if (p[4] != ',')
461 break;
462 p += 5;
463 }
464 }
465
466 if (me.bursting)
467 {
468 sts (":%s EOBACK", me.numeric);
469 #ifdef HAVE_GETTIMEOFDAY
470 e_time (burstime, &burstime);
471
472 slog (LG_INFO, "m_eob(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
473
474 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
475 #else
476 slog (LG_INFO, "m_eob(): finished synching with uplink");
477 wallops ("Finished synching to network.");
478 #endif
479
480 me.bursting = false;
481 }
482 }
483
484 static void
485 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
486 {
487 if (parc != 2)
488 return;
489
490 handle_message (si, parv[0], false, parv[1]);
491 }
492
493 static void
494 m_notice (sourceinfo_t *si, int parc, char *parv[])
495 {
496 if (parc != 2)
497 return;
498
499 handle_message (si, parv[0], true, parv[1]);
500 }
501
502 static void
503 m_njoin (sourceinfo_t *si, int parc, char *parv[])
504 {
505 channel_t *c;
506 unsigned int userc;
507 char *userv[256];
508 unsigned int i;
509
510 c = channel_find (parv[0]);
511
512 if (!c)
513 {
514 slog (LG_DEBUG, "m_njoin(): new channel: %s", parv[0]);
515 /* Give channels created during burst an older "TS"
516 * so they won't be deopped -- jilles */
517 c = channel_add (parv[0], si->s->flags & SF_EOB ? NOW : NOW - 601, si->s);
518 /* if !/+ channel, we don't want to do anything with it */
519 if (c == NULL)
520 return;
521 /* Check mode locks */
522 channel_mode (NULL, c, "+");
523 }
524
525 userc = sjtoken (parv[parc - 1], ',', userv);
526
527 for (i = 0; i < userc; i++)
528 chanuser_add (c, userv[i]);
529
530 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
531 channel_delete (c);
532 }
533
534 static void
535 m_part (sourceinfo_t *si, int parc, char *parv[])
536 {
537 int chanc;
538 char *chanv[256];
539 int i;
540
541 chanc = sjtoken (parv[0], ',', chanv);
542 for (i = 0; i < chanc; i++)
543 {
544 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
545
546 chanuser_delete (channel_find (chanv[i]), si->su);
547 }
548 }
549
550 static void
551 m_nick (sourceinfo_t *si, int parc, char *parv[])
552 {
553 user_t *u;
554
555 /* got the right number of args for an introduction? */
556 if (parc == 7)
557 {
558 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
559
560 u = user_add (parv[0], parv[2], parv[3], NULL, parv[4], parv[1], parv[6], si->s, 0);
561
562 user_mode (u, parv[5]);
563 if (strchr (parv[5], 'a'))
564 handle_away (u, "Gone");
565
566 handle_nickchange (u);
567 }
568
569 /* if it's only 1 then it's a nickname change */
570 else if (parc == 1)
571 {
572 if (!si->su)
573 {
574 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
575 return;
576 }
577
578 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
579
580 user_changenick (si->su, parv[0], 0);
581
582 handle_nickchange (si->su);
583 }
584 else
585 {
586 int i;
587 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
588
589 for (i = 0; i < parc; i++)
590 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
591 }
592 }
593
594 static void
595 m_save (sourceinfo_t *si, int parc, char *parv[])
596 {
597 user_t *u;
598
599 u = user_find (parv[0]);
600 if (u == NULL)
601 return;
602 if (!strcmp (u->nick, u->uid))
603 {
604 slog (LG_DEBUG, "m_save(): ignoring noop SAVE message for %s", u->nick);
605 return;
606 }
607 if (is_internal_client (u))
608 {
609 slog (LG_INFO, "m_save(): service %s got hit, changing back", u->nick);
610 sts (":%s NICK %s", u->uid, u->nick);
611 /* XXX services wars */
612 }
613 else
614 {
615 slog (LG_DEBUG, "m_save(): nickname change for `%s': %s", u->nick, u->uid);
616
617 user_changenick (u, u->uid, 0);
618
619 handle_nickchange (u);
620 }
621 }
622
623 static void
624 m_quit (sourceinfo_t *si, int parc, char *parv[])
625 {
626 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
627
628 /* user_delete() takes care of removing channels and so forth */
629 user_delete (si->su);
630 }
631
632 static void
633 m_mode (sourceinfo_t *si, int parc, char *parv[])
634 {
635 /* The following is hackish, but it works, because user MODE
636 * is not used in bursts and users are not allowed to change away
637 * status using MODE.
638 * -- jilles */
639 if (*parv[0] == '#')
640 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
641 else if (!strcmp (parv[1], "-a"))
642 handle_away (user_find (parv[0]), NULL);
643 else if (!strcmp (parv[1], "+a"))
644 handle_away (user_find (parv[0]), "Gone");
645 else
646 user_mode (user_find (parv[0]), parv[1]);
647 }
648
649 static void
650 m_kick (sourceinfo_t *si, int parc, char *parv[])
651 {
652 user_t *u = user_find (parv[1]);
653 channel_t *c = channel_find (parv[0]);
654
655 /* -> :rakaur KICK #shrike rintaun :test */
656 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
657
658 if (!u)
659 {
660 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
661 return;
662 }
663
664 if (!c)
665 {
666 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
667 return;
668 }
669
670 if (!chanuser_find (c, u))
671 {
672 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
673 return;
674 }
675
676 chanuser_delete (c, u);
677
678 /* if they kicked us, let's rejoin */
679 if (is_internal_client (u))
680 {
681 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
682 join (parv[0], u->nick);
683 }
684 }
685
686 static void
687 m_kill (sourceinfo_t *si, int parc, char *parv[])
688 {
689 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
690 }
691
692 static void
693 m_squit (sourceinfo_t *si, int parc, char *parv[])
694 {
695 slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
696 if (server_find (parv[0]))
697 server_delete (parv[0]);
698 else if (si->su != NULL)
699 {
700 /* XXX we don't have a list of jupes, so let's just
701 * assume it is one if we don't know it */
702 slog (LG_INFO, "m_squit(): accepting SQUIT for jupe %s from %s", parv[0], si->su->nick);
703 sts (":%s WALLOPS :Received SQUIT %s from %s (%s)", me.numeric, parv[0], si->su->nick, parv[1]);
704 sts (":%s SQUIT %s :%s", me.numeric, parv[0], parv[1]);
705 }
706 }
707
708 static void
709 m_server (sourceinfo_t *si, int parc, char *parv[])
710 {
711 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
712 handle_server (si, parv[0], parv[2], atoi (parv[1]), parv[parc - 1]);
713 }
714
715 static void
716 m_stats (sourceinfo_t *si, int parc, char *parv[])
717 {
718 handle_stats (si->su, parv[0][0]);
719 }
720
721 static void
722 m_admin (sourceinfo_t *si, int parc, char *parv[])
723 {
724 handle_admin (si->su);
725 }
726
727 static void
728 m_version (sourceinfo_t *si, int parc, char *parv[])
729 {
730 handle_version (si->su);
731 }
732
733 static void
734 m_info (sourceinfo_t *si, int parc, char *parv[])
735 {
736 handle_info (si->su);
737 }
738
739 static void
740 m_whois (sourceinfo_t *si, int parc, char *parv[])
741 {
742 handle_whois (si->su, parv[1]);
743 }
744
745 static void
746 m_trace (sourceinfo_t *si, int parc, char *parv[])
747 {
748 handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
749 }
750
751 static void
752 m_join (sourceinfo_t *si, int parc, char *parv[])
753 {
754 chanuser_t *cu;
755 node_t *n, *tn;
756
757 /* JOIN 0 is really a part from all channels */
758 if (parv[0][0] == '0')
759 {
760 LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
761 {
762 cu = (chanuser_t *) n->data;
763 chanuser_delete (cu->chan, si->su);
764 }
765 }
766 }
767
768 static void
769 m_pass (sourceinfo_t *si, int parc, char *parv[])
770 {
771 if (strcmp (curr_uplink->pass, parv[0]))
772 {
773 slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
774 runflags |= RF_SHUTDOWN;
775 }
776 }
777
778 static void
779 m_error (sourceinfo_t *si, int parc, char *parv[])
780 {
781 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
782 }
783
784 static void
785 m_motd (sourceinfo_t *si, int parc, char *parv[])
786 {
787 handle_motd (si->su);
788 }
789
790 static pcommand_t const pcommands[] = {
791 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
792 { "PONG", m_pong, 1, MSRC_SERVER },
793 { "EOB", m_eob, 0, MSRC_SERVER },
794 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
795 { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
796 { "NJOIN", m_njoin, 2, MSRC_SERVER },
797 { "PART", m_part, 1, MSRC_USER },
798 { "NICK", m_nick, 1, MSRC_USER },
799 { "UNICK", m_nick, 7, MSRC_SERVER },
800 { "SAVE", m_save, 1, MSRC_SERVER },
801 { "QUIT", m_quit, 1, MSRC_USER },
802 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
803 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
804 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
805 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
806 { "SERVER", m_server, 4, MSRC_UNREG | MSRC_SERVER },
807 { "STATS", m_stats, 2, MSRC_USER },
808 { "ADMIN", m_admin, 1, MSRC_USER },
809 { "VERSION", m_version, 1, MSRC_USER },
810 { "INFO", m_info, 1, MSRC_USER },
811 { "WHOIS", m_whois, 2, MSRC_USER },
812 { "TRACE", m_trace, 1, MSRC_USER },
813 { "JOIN", m_join, 1, MSRC_USER },
814 { "PASS", m_pass, 1, MSRC_UNREG },
815 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
816 { "TOPIC", m_topic, 2, MSRC_USER },
817 { "MOTD", m_motd, 1, MSRC_USER },
818 };
819
820 ircnet_handler::ircnet_handler ()
821 {
822 mode_list = ircnet_mode_list;
823 ignore_mode_list = ircnet_ignore_mode_list;
824 status_mode_list = ircnet_status_mode_list;
825 prefix_mode_list = ircnet_prefix_mode_list;
826
827 ircd = &IRCNet;
828
829 pcommand_add (&pcommands);
830 }
831
832 ircnet_handler::~ircnet_handler ()
833 {
834 mode_list = NULL;
835 ignore_mode_list = NULL;
836 status_mode_list = NULL;
837 prefix_mode_list = NULL;
838
839 ircd = NULL;
840
841 pcommand_delete (&pcommands);
842 }
843 } // namespace protocol
844
845 #define FACREG_TYPE protocol::ircnet_handler
846 #define FACREG_TYPE_NAME "ircnet"
847 #define FACREG_INTERFACE_TYPE protocol::handler
848 #include <ermyth/factory_reg.h>