ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/officeirc.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 * officeirc.C: This file contains reverse-engineered IRCXPRO 1.2/OfficeIRC support.
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: officeirc.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 "uplink.h"
24 #include "pmodule.h"
25 #include "protocol/officeirc.h"
26
27 static char const rcsid[] = "$Id: officeirc.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 officeirc = {
34 "officeirc/ircxpro", /* 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 true, /* Whether or not we support channel owners. */
39 false, /* Whether or not we support channel protection. */
40 false, /* Whether or not we support halfops. */
41 false, /* Whether or not we use P10 */
42 false, /* Whether or not we use vHosts. */
43 0, /* Oper-only cmodes */
44 0, /* Integer flag for owner channel flag. */
45 0, /* Integer flag for protect channel flag. */
46 0, /* Integer flag for halfops. */
47 "+q", /* Mode we set for owner. */
48 "+", /* Mode we set for protect. */
49 "+", /* Mode we set for halfops. */
50 PROTOCOL_OFFICEIRC, /* Protocol type */
51 0, /* Permanent cmodes */
52 "b", /* Ban-like cmodes */
53 0, /* Except mchar */
54 0, /* Invex mchar */
55 0 /* Flags */
56 };
57
58 struct officeirc_handler : handler
59 {
60 officeirc_handler ();
61 virtual ~officeirc_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 kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
70 virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
71 virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
72 virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
73 virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
74 virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
75 virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
76 virtual void part_sts (channel_t *c, user_t *u);
77 virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
78 virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
79 virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
80 virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
81 virtual void ping_sts (void);
82 virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
83 virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
84 virtual void jupe (char const * const server, char const * const reason);
85 };
86
87 static cmode_t officeirc_mode_list[] = {
88 { 'i', CMODE_INVITE },
89 { 'm', CMODE_MOD },
90 { 'n', CMODE_NOEXT },
91 { 'p', CMODE_PRIV },
92 { 's', CMODE_SEC },
93 { 't', CMODE_TOPIC },
94 { 'c', CMODE_NOCOLOR },
95 { 'R', CMODE_REGONLY },
96 { '\0', 0 }
97 };
98
99 static extmode_t officeirc_ignore_mode_list[] = {
100 { '\0', 0 }
101 };
102
103 static cmode_t officeirc_status_mode_list[] = {
104 { 'q', CMODE_OWNER | CMODE_OP },
105 { 'o', CMODE_OP },
106 { 'v', CMODE_VOICE },
107 { '\0', 0 }
108 };
109
110 static cmode_t officeirc_prefix_mode_list[] = {
111 { '.', CMODE_OWNER | CMODE_OP },
112 { '@', CMODE_OP },
113 { '+', CMODE_VOICE },
114 { '\0', 0 }
115 };
116
117 /* *INDENT-ON* */
118
119 /* login to our uplink */
120 unsigned int
121 officeirc_handler::server_login (void)
122 {
123 int ret;
124
125 ret = sts ("PASS %s", curr_uplink->pass);
126 if (ret == 1)
127 return 1;
128
129 me.bursting = true;
130
131 sts ("SERVER %s 1 :%s", me.name, me.desc);
132
133 services_init ();
134
135 return 0;
136 }
137
138 /* introduce a client */
139 void
140 officeirc_handler::introduce_nick (user_t *u)
141 {
142 /* NICK nenolod 1 1171690800 ~nenolod 69.60.119.15[69.60.119.15] ircxserver01 0 :Unknown */
143
144 sts ("NICK %s 1 %ld %s %s[0.0.0.0] %s 0 :%s", u->nick, u->ts, u->user, u->host, me.name, u->gecos);
145 sts (":%s MODE %s +%s", u->nick, u->nick, "io");
146 }
147
148 /* invite a user to a channel */
149 void
150 officeirc_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
151 {
152 sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
153 }
154
155 void
156 officeirc_handler::quit_sts (user_t *u, char const * const reason)
157 {
158 if (!me.connected)
159 return;
160
161 sts (":%s QUIT :%s", u->nick, reason);
162 }
163
164 /* WALLOPS wrapper */
165 void
166 officeirc_handler::wallops_sts (char const * const text)
167 {
168 sts (":%s GLOBOPS :%s", me.name, text);
169 }
170
171 /* join a channel */
172 void
173 officeirc_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
174 {
175 if (isnew)
176 {
177 sts (":%s JOIN %s", u->nick, c->name);
178 if (modes[0] && modes[1])
179 sts (":%s MODE %s %s", u->nick, c->name, modes);
180 }
181 else
182 {
183 sts (":%s JOIN %s", u->nick, c->name);
184 }
185 sts (":%s MODE %s +q %s", u->nick, c->name, u->nick);
186 }
187
188 /* kicks a user from a channel */
189 void
190 officeirc_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
191 {
192 channel_t *chan = channel_find (channel);
193 user_t *user = user_find (to);
194
195 if (!chan || !user)
196 return;
197
198 sts (":%s KICK %s %s :%s", from, channel, to, reason);
199
200 chanuser_delete (chan, user);
201 }
202
203 /* PRIVMSG wrapper */
204 void
205 officeirc_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 officeirc_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
220 {
221 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
222 }
223
224 void
225 officeirc_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 ? from->nick : me.name, ircd->tldprefix, tld->name, text);
230 else
231 sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
232 }
233
234 void
235 officeirc_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
236 {
237 sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
238 }
239
240 void
241 officeirc_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
242 {
243 va_list ap;
244 char buf[BUFSIZE];
245
246 va_start (ap, fmt);
247 vsnprintf (buf, BUFSIZE, fmt, ap);
248 va_end (ap);
249
250 sts (":%s %d %s %s", from, numeric, target, buf);
251 }
252
253 /* KILL wrapper */
254 void
255 officeirc_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
256 {
257 va_list ap;
258 char buf[BUFSIZE];
259
260 va_start (ap, fmt);
261 vsnprintf (buf, BUFSIZE, fmt, ap);
262 va_end (ap);
263
264 sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
265 }
266
267 /* PART wrapper */
268 void
269 officeirc_handler::part_sts (channel_t *c, user_t *u)
270 {
271 sts (":%s PART %s", u->nick, c->name);
272 }
273
274 /* server-to-server KLINE wrapper */
275 void
276 officeirc_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
277 {
278 if (!me.connected)
279 return;
280
281 sts (":%s ACCESS * ADD AKILL 0 *!%s@%s$* %ld 0 %s %ld 0 :%s", opersvs.nick, host, user, NOW, opersvs.nick, duration, reason);
282 }
283
284 /* server-to-server UNKLINE wrapper */
285 void
286 officeirc_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
287 {
288 if (!me.connected)
289 return;
290
291 sts (":%s ACCESS * DELETE AKILL 0 *!%s@%s$* %ld 0 %s 0 0 :", opersvs.nick, host, user, NOW, opersvs.nick);
292 }
293
294 /* topic wrapper */
295 void
296 officeirc_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
297 {
298 if (!me.connected)
299 return;
300
301 if (ts < prevts || prevts == 0)
302 sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, setter, ts, topic);
303 else if (prevts > 1)
304 {
305 ts = prevts - 1;
306 sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, "topictime.wrong", ts, topic);
307 c->topicts = ts;
308 }
309 else
310 {
311 notice (chansvs.nick, c->name, "Unable to change topic to: %s", topic);
312 c->topicts = 1;
313 }
314 }
315
316 /* mode wrapper */
317 void
318 officeirc_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
319 {
320 if (!me.connected)
321 return;
322
323 sts (":%s MODE %s %s", sender, target->name, modes);
324 }
325
326 /* ping wrapper */
327 void
328 officeirc_handler::ping_sts (void)
329 {
330 if (!me.connected)
331 return;
332
333 sts ("PING :%s", me.name);
334 }
335
336 /* protocol-specific stuff to do on login */
337 void
338 officeirc_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
339 {
340 user_t *u = user_find (origin);
341
342 if (!me.connected || u == NULL)
343 return;
344
345 /* Can only do this for nickserv, and can only record identified
346 * state if logged in to correct nick, sorry -- jilles
347 */
348 if (should_reg_umode (u))
349 sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, origin, NOW);
350 }
351
352 /* protocol-specific stuff to do on login */
353 bool
354 officeirc_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
355 {
356 if (!me.connected)
357 return false;
358
359 if (!nicksvs.no_nick_ownership)
360 sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, origin, NOW);
361 return false;
362 }
363
364 void
365 officeirc_handler::jupe (char const * const server, char const * const reason)
366 {
367 if (!me.connected)
368 return;
369
370 server_delete (server);
371 sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
372 sts (":%s SERVER %s 2 :%s", me.name, server, reason);
373 }
374
375 static void
376 m_topic (sourceinfo_t *si, int parc, char *parv[])
377 {
378 channel_t *c = channel_find (parv[0]);
379
380 if (!c)
381 return;
382
383 handle_topic_from (si, c, parv[1], atol (parv[2]), parv[3]);
384 }
385
386 static void
387 m_ping (sourceinfo_t *si, int parc, char *parv[])
388 {
389 /* reply to PING's */
390 sts (":%s PONG %s %s", me.name, me.name, parv[0]);
391 }
392
393 static void
394 m_pong (sourceinfo_t *si, int parc, char *parv[])
395 {
396 server_t *s;
397
398 /* someone replied to our PING */
399 if (!parv[0])
400 return;
401 s = server_find (parv[0]);
402 if (s == NULL)
403 return;
404 handle_eob (s);
405
406 if (irccasecmp (me.actual, parv[0]))
407 return;
408
409 me.uplinkpong = NOW;
410
411 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
412 if (me.bursting)
413 {
414 #ifdef HAVE_GETTIMEOFDAY
415 e_time (burstime, &burstime);
416
417 slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
418
419 wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
420 #else
421 slog (LG_INFO, "m_pong(): finished synching with uplink");
422 wallops ("Finished synching to network.");
423 #endif
424
425 me.bursting = false;
426 }
427 }
428
429 static void
430 m_privmsg (sourceinfo_t *si, int parc, char *parv[])
431 {
432 if (parc != 2)
433 return;
434
435 handle_message (si, parv[0], false, parv[1]);
436 }
437
438 static void
439 m_notice (sourceinfo_t *si, int parc, char *parv[])
440 {
441 if (parc != 2)
442 return;
443
444 handle_message (si, parv[0], true, parv[1]);
445 }
446
447 static void
448 m_part (sourceinfo_t *si, int parc, char *parv[])
449 {
450 int chanc;
451 char *chanv[256];
452 int i;
453
454 chanc = sjtoken (parv[0], ',', chanv);
455 for (i = 0; i < chanc; i++)
456 {
457 slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
458
459 chanuser_delete (channel_find (chanv[i]), si->su);
460 }
461 }
462
463 static void
464 m_nick (sourceinfo_t *si, int parc, char *parv[])
465 {
466 server_t *s;
467 bool realchange;
468
469 if (parc == 8)
470 {
471 char *host, *ip;
472
473 s = server_find (parv[5]);
474 if (!s)
475 {
476 slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
477 return;
478 }
479
480 slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
481
482 host = sstrdup (parv[4]);
483 ip = strchr (host, '[');
484
485 if (ip != NULL)
486 {
487 *ip++ = '\0';
488 ip = strchr (ip, ']');
489
490 if (ip != NULL)
491 *ip = '\0';
492 }
493
494 user_add (parv[0], parv[3], host, ip, NULL, NULL, parv[7], s, atoi (parv[2]));
495
496 sfree (host);
497
498 /* Note: cannot rely on umode +r to see if they're identified
499 * -- jilles */
500
501 handle_nickchange (user_find (parv[0]));
502 }
503
504 /* if it's only 2 then it's a nickname change */
505 else if (parc == 2)
506 {
507 if (!si->su)
508 {
509 slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
510 return;
511 }
512
513 slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
514
515 realchange = irccasecmp (si->su->nick, parv[0]);
516
517 user_changenick (si->su, parv[0], atoi (parv[1]));
518
519 /* fix up +r if necessary -- jilles */
520 if (realchange && !nicksvs.no_nick_ownership)
521 {
522 if (should_reg_umode (si->su))
523 /* changed nick to registered one, reset +r */
524 sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, parv[0], NOW);
525 else
526 /* changed from registered nick, remove +r */
527 sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, parv[0], NOW);
528 }
529
530 handle_nickchange (si->su);
531 }
532 else
533 {
534 int i;
535 slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
536
537 for (i = 0; i < parc; i++)
538 slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
539 }
540 }
541
542 static void
543 m_quit (sourceinfo_t *si, int parc, char *parv[])
544 {
545 if (si->su->server == me.me)
546 {
547 slog (LG_DEBUG, "m_quit(): not destroying own user %s (fake direction)", si->su->nick);
548 return;
549 }
550 slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
551
552 /* user_delete() takes care of removing channels and so forth */
553 user_delete (si->su);
554 }
555
556 static void
557 m_mode (sourceinfo_t *si, int parc, char *parv[])
558 {
559 if (*parv[0] == '#')
560 channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
561 else
562 user_mode (user_find (parv[0]), parv[1]);
563 }
564
565 static void
566 m_kick (sourceinfo_t *si, int parc, char *parv[])
567 {
568 user_t *u = user_find (parv[1]);
569 channel_t *c = channel_find (parv[0]);
570
571 /* -> :rakaur KICK #shrike rintaun :test */
572 slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
573
574 if (!u)
575 {
576 slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
577 return;
578 }
579
580 if (!c)
581 {
582 slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
583 return;
584 }
585
586 if (!chanuser_find (c, u))
587 {
588 slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
589 return;
590 }
591
592 chanuser_delete (c, u);
593
594 /* if they kicked us, let's rejoin */
595 if (is_internal_client (u))
596 {
597 slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
598 join (parv[0], u->nick);
599 }
600 }
601
602 static void
603 m_kill (sourceinfo_t *si, int parc, char *parv[])
604 {
605 handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
606 }
607
608 static void
609 m_squit (sourceinfo_t *si, int parc, char *parv[])
610 {
611 slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
612 server_delete (parv[0]);
613 }
614
615 static void
616 m_server (sourceinfo_t *si, int parc, char *parv[])
617 {
618 server_t *s;
619
620 slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
621 s = handle_server (si, parv[0], NULL, atoi (parv[1]), parv[2]);
622
623 if (s != NULL && s->uplink != me.me)
624 {
625 /* elicit PONG for EOB detection; pinging uplink is
626 * already done elsewhere -- jilles
627 */
628 sts (":%s PING %s %s", me.name, me.name, s->name);
629 }
630 }
631
632 static void
633 m_stats (sourceinfo_t *si, int parc, char *parv[])
634 {
635 handle_stats (si->su, parv[0][0]);
636 }
637
638 static void
639 m_admin (sourceinfo_t *si, int parc, char *parv[])
640 {
641 handle_admin (si->su);
642 }
643
644 static void
645 m_version (sourceinfo_t *si, int parc, char *parv[])
646 {
647 handle_version (si->su);
648 }
649
650 static void
651 m_info (sourceinfo_t *si, int parc, char *parv[])
652 {
653 handle_info (si->su);
654 }
655
656 static void
657 m_whois (sourceinfo_t *si, int parc, char *parv[])
658 {
659 handle_whois (si->su, parv[1]);
660 }
661
662 static void
663 m_trace (sourceinfo_t *si, int parc, char *parv[])
664 {
665 handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
666 }
667
668 static void
669 m_away (sourceinfo_t *si, int parc, char *parv[])
670 {
671 handle_away (si->su, parc >= 1 ? parv[0] : NULL);
672 }
673
674 static void
675 m_join (sourceinfo_t *si, int parc, char *parv[])
676 {
677 chanuser_t *cu;
678 node_t *n, *tn;
679 int chanc;
680 char *chanv[256];
681 int i;
682
683 /* JOIN 0 is really a part from all channels */
684 if (parv[0][0] == '0')
685 {
686 LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
687 {
688 cu = (chanuser_t *) n->data;
689 chanuser_delete (cu->chan, si->su);
690 }
691 }
692 else
693 {
694 chanc = sjtoken (parv[0], ',', chanv);
695 for (i = 0; i < chanc; i++)
696 {
697 channel_t *c = channel_find (chanv[i]);
698
699 if (!c)
700 {
701 slog (LG_DEBUG, "m_join(): new channel: %s", parv[0]);
702 c = channel_add (chanv[i], NOW, si->su->server);
703 /* Tell the core to check mode locks now,
704 * otherwise it may only happen after the next
705 * mode change.
706 * DreamForge does not allow any redundant modes
707 * so this will not look ugly. -- jilles */
708 /* If this is in a burst, a MODE with the
709 * simple modes will follow so we can skip
710 * this. -- jilles */
711 if (!me.bursting)
712 channel_mode (NULL, c, "+");
713 }
714 chanuser_add (c, si->su->nick);
715 }
716 }
717 }
718
719 static void
720 m_pass (sourceinfo_t *si, int parc, char *parv[])
721 {
722 if (strcmp (curr_uplink->pass, parv[0]))
723 {
724 slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
725 runflags |= RF_SHUTDOWN;
726 }
727 }
728
729 static void
730 m_error (sourceinfo_t *si, int parc, char *parv[])
731 {
732 slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
733 }
734
735 static void
736 m_motd (sourceinfo_t *si, int parc, char *parv[])
737 {
738 handle_motd (si->su);
739 }
740
741 static void
742 m_njoin (sourceinfo_t *si, int parc, char *parv[])
743 {
744 /* -> :ircxserver01 NJOIN #take 1073516550 :.qbot */
745
746 channel_t *c;
747 unsigned int userc;
748 char *userv[256];
749 unsigned int i;
750 time_t ts;
751 char *p;
752 bool keep_new_modes = true;
753
754 /* :origin SJOIN chan ts :users */
755 if (si->s == NULL)
756 return;
757
758 c = channel_find (parv[0]);
759 ts = atol (parv[1]);
760
761 if (!c)
762 {
763 slog (LG_DEBUG, "m_njoin(): new channel: %s", parv[1]);
764 c = channel_add (parv[0], ts, si->s);
765 /* Tell the core to check mode locks now,
766 * otherwise it may only happen after the next
767 * mode change.
768 * DreamForge does not allow any redundant modes
769 * so this will not look ugly. -- jilles */
770 channel_mode (NULL, c, "+");
771 }
772
773 if (ts == 0 || c->ts == 0)
774 {
775 if (c->ts != 0)
776 slog (LG_INFO, "m_sjoin(): server %s changing TS on %s from %ld to 0", si->s->name, c->name, (long) c->ts);
777 c->ts = 0;
778 c->callback.tschange (c);
779 }
780 else if (ts < c->ts)
781 {
782 chanuser_t *cu;
783 node_t *n;
784
785 /* the TS changed. a TS change requires the following things
786 * to be done to the channel: reset all modes to nothing, remove
787 * all status modes on known users on the channel (including ours),
788 * and set the new TS.
789 */
790
791 clear_simple_modes (c);
792
793 LIST_FOREACH (n, c->members.head)
794 {
795 cu = (chanuser_t *) n->data;
796 if (cu->user->server == me.me)
797 {
798 /* it's a service, reop */
799 sts (":%s MODE %s +o %s", CLIENT_NAME (cu->user), c->name, CLIENT_NAME (cu->user));
800 cu->modes = CMODE_OP;
801 }
802 else
803 cu->modes = 0;
804 }
805
806 slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
807
808 c->ts = ts;
809 c->callback.tschange (c);
810 }
811 else if (ts > c->ts)
812 keep_new_modes = false;
813
814 userc = sjtoken (parv[parc - 1], ' ', userv);
815
816 if (keep_new_modes)
817 for (i = 0; i < userc; i++)
818 chanuser_add (c, userv[i]);
819 else
820 for (i = 0; i < userc; i++)
821 {
822 p = userv[i];
823 while (*p == '@' || *p == '%' || *p == '+' || *p == '.')
824 p++;
825 chanuser_add (c, p);
826 }
827
828 if (c->nummembers == 0)
829 channel_delete (c);
830 }
831
832 static void
833 nick_group (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
834 {
835 user_t *u;
836
837 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
838 u = si->su;
839 else
840 u = user_find_named (mn->nick);
841
842 if (u != NULL && should_reg_umode (u))
843 sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, u->nick, NOW);
844 }
845
846 static void
847 nick_ungroup (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
848 {
849 user_t *u;
850
851 if (si->su != NULL && !irccasecmp (si->su->nick, mn->nick))
852 u = si->su;
853 else
854 u = user_find_named (mn->nick);
855
856 if (u != NULL && !nicksvs.no_nick_ownership)
857 sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, u->nick, NOW);
858 }
859
860 static pcommand_t const pcommands[] = {
861 { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
862 { "PONG", m_pong, 1, MSRC_SERVER },
863 { "PRIVMSG", m_privmsg, 2, MSRC_USER },
864 { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
865 { "PART", m_part, 1, MSRC_USER },
866 { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
867 { "QUIT", m_quit, 1, MSRC_USER },
868 { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
869 { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
870 { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
871 { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
872 { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
873 { "STATS", m_stats, 2, MSRC_USER },
874 { "ADMIN", m_admin, 1, MSRC_USER },
875 { "VERSION", m_version, 1, MSRC_USER },
876 { "INFO", m_info, 1, MSRC_USER },
877 { "WHOIS", m_whois, 2, MSRC_USER },
878 { "TRACE", m_trace, 1, MSRC_USER },
879 { "AWAY", m_away, 0, MSRC_USER },
880 { "JOIN", m_join, 1, MSRC_USER },
881 { "PASS", m_pass, 1, MSRC_UNREG },
882 { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
883 { "TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER },
884 { "MOTD", m_motd, 1, MSRC_USER },
885 { "NJOIN", m_njoin, 3, MSRC_SERVER },
886 };
887
888 officeirc_handler::officeirc_handler ()
889 {
890 mode_list = officeirc_mode_list;
891 ignore_mode_list = officeirc_ignore_mode_list;
892 status_mode_list = officeirc_status_mode_list;
893 prefix_mode_list = officeirc_prefix_mode_list;
894
895 ircd = &officeirc;
896
897 pcommand_add (&pcommands);
898
899 mynick_t::callback.group.attach (nick_group);
900 mynick_t::callback.ungroup.attach (nick_ungroup);
901 }
902
903 officeirc_handler::~officeirc_handler ()
904 {
905 mode_list = NULL;
906 ignore_mode_list = NULL;
907 status_mode_list = NULL;
908 prefix_mode_list = NULL;
909
910 ircd = NULL;
911
912 mynick_t::callback.group.detach (nick_group);
913 mynick_t::callback.ungroup.detach (nick_ungroup);
914
915 pcommand_delete (&pcommands);
916 }
917 } // namespace protocol
918
919 #define FACREG_TYPE protocol::officeirc_handler
920 #define FACREG_TYPE_NAME "officeirc"
921 #define FACREG_INTERFACE_TYPE protocol::handler
922 #include <ermyth/factory_reg.h>