ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/hybrid.C
Revision: 1.8
Committed: Sat Sep 22 14:27:28 2007 UTC (18 years, 11 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

# User Rev Content
1 pippijn 1.7 /**
2     * hybrid.C: This file contains protocol support for hybrid-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 pippijn 1.4 * Copyright © 2005-2006 Atheme Development Group
10 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
11 pippijn 1.1 *
12 pippijn 1.8 * $Id: hybrid.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $
13 pippijn 1.1 */
14    
15 pippijn 1.8 #include <boost/foreach.hpp>
16    
17 pippijn 1.1 #include "atheme.h"
18 pippijn 1.8 #include <util/time.h>
19     #include <libermyth.h>
20 pippijn 1.6 #include "servers.h"
21 pippijn 1.1 #include <account/myuser.h>
22     #include "uplink.h"
23     #include "pmodule.h"
24     #include "protocol/hybrid.h"
25    
26 pippijn 1.8 static char const rcsid[] = "$Id: hybrid.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $";
27 pippijn 1.1
28     /* *INDENT-OFF* */
29    
30 pippijn 1.4 namespace protocol
31 pippijn 1.1 {
32 pippijn 1.4 static ircd_t Hybrid = {
33     "Hybrid 7.1.2+", /* IRCd name */
34     "$$", /* TLD Prefix, used by Global. */
35     true, /* Whether or not we use IRCNet/TS6 UID */
36     false, /* Whether or not we use RCOMMAND */
37     false, /* Whether or not we support channel owners. */
38     false, /* Whether or not we support channel protection. */
39     false, /* Whether or not we support halfops. */
40     false, /* Whether or not we use P10 */
41     false, /* Whether or not we use vHosts. */
42     0, /* Oper-only cmodes */
43     0, /* Integer flag for owner channel flag. */
44     0, /* Integer flag for protect channel flag. */
45     0, /* Integer flag for halfops. */
46     "+", /* Mode we set for owner. */
47     "+", /* Mode we set for protect. */
48     "+", /* Mode we set for halfops. */
49     PROTOCOL_RATBOX, /* Protocol type */
50     0, /* Permanent cmodes */
51     "beI", /* Ban-like cmodes */
52     'e', /* Except mchar */
53     'I', /* Invex mchar */
54     IRCD_CIDR_BANS /* Flags */
55     };
56    
57     struct hybrid_handler : handler
58     {
59     hybrid_handler ();
60     virtual ~hybrid_handler ();
61    
62     virtual unsigned int server_login (void);
63     virtual void introduce_nick (user_t *u);
64     virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
65     virtual void quit_sts (user_t *u, char const * const reason);
66     virtual void wallops_sts (char const * const text);
67     virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
68     virtual void chan_lowerts (channel_t *c, user_t *u);
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 wallchops (user_t *sender, channel_t *channel, char const * const message);
75     virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
76     virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
77     virtual void part_sts (channel_t *c, user_t *u);
78     virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
79     virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
80     virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
81     virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
82     virtual void ping_sts (void);
83     virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
84     virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
85     virtual void jupe (char const * const server, char const * const reason);
86     virtual void sethost_sts (char const * const source, char const * const target, char const * const host);
87     virtual void fnc_sts (user_t *source, user_t *u, char const * const newnick, int type);
88     virtual void holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account);
89     };
90    
91     static cmode_t hybrid_mode_list[] = {
92     { 'i', CMODE_INVITE },
93     { 'm', CMODE_MOD },
94     { 'n', CMODE_NOEXT },
95     { 'p', CMODE_PRIV },
96     { 's', CMODE_SEC },
97     { 't', CMODE_TOPIC },
98     { '\0', 0 }
99     };
100    
101 pippijn 1.5 static extmode_t hybrid_ignore_mode_list[] = {
102 pippijn 1.4 { '\0', 0 }
103     };
104    
105     static cmode_t hybrid_status_mode_list[] = {
106     { 'o', CMODE_OP },
107     { 'v', CMODE_VOICE },
108     { '\0', 0 }
109     };
110    
111     static cmode_t hybrid_prefix_mode_list[] = {
112     { '@', CMODE_OP },
113     { '+', CMODE_VOICE },
114     { '\0', 0 }
115     };
116    
117     static bool use_rserv_support = false;
118     static bool use_tb = false;
119     static bool use_rsfnc = false;
120    
121     static char ts6sid[3 + 1] = "";
122    
123     /* *INDENT-ON* */
124    
125     /* login to our uplink */
126     unsigned int
127     hybrid_handler::server_login (void)
128     {
129     int ret = 1;
130    
131     if (!me.numeric)
132     {
133     ircd->uses_uid = false;
134     ret = sts ("PASS %s :TS", curr_uplink->pass);
135     }
136     else if (strlen (me.numeric) == 3 && isdigit (*me.numeric))
137     {
138     ircd->uses_uid = true;
139     ret = sts ("PASS %s TS 6 :%s", curr_uplink->pass, me.numeric);
140     }
141     else
142     {
143     slog (LG_ERROR, "Invalid numeric (SID) %s", me.numeric);
144     }
145     if (ret == 1)
146     return 1;
147    
148     me.bursting = true;
149    
150     sts ("CAPAB :QS EX IE KLN UNKLN ENCAP TB SERVICES");
151     sts ("SERVER %s 1 :%s", me.name, me.desc);
152     sts ("SVINFO %d 3 0 :%ld", ircd->uses_uid ? 6 : 5, NOW);
153    
154     return 0;
155     }
156    
157     /* introduce a client */
158     void
159     hybrid_handler::introduce_nick (user_t *u)
160     {
161     if (ircd->uses_uid)
162     sts (":%s UID %s 1 %ld +%s%s%s %s %s 0 %s :%s", me.numeric, u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", use_rserv_support ? "S" : "", u->user, u->host, u->uid, u->gecos);
163     else
164     sts ("NICK %s 1 %ld +%s%s%s %s %s %s :%s", u->nick, u->ts, "io", chansvs.fantasy ? "" : "D", use_rserv_support ? "S" : "", u->user, u->host, me.name, u->gecos);
165     }
166    
167     /* invite a user to a channel */
168     void
169     hybrid_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
170     {
171     /* some older TSora ircds require the sender to be
172     * on the channel, but hyb7/ratbox don't
173     * let's just assume it's not necessary -- jilles */
174     sts (":%s INVITE %s %s", CLIENT_NAME (sender), CLIENT_NAME (target), channel->name);
175     }
176    
177     void
178     hybrid_handler::quit_sts (user_t *u, char const * const reason)
179     {
180     if (!me.connected)
181     return;
182    
183     sts (":%s QUIT :%s", CLIENT_NAME (u), reason);
184     }
185    
186     /* WALLOPS wrapper */
187     void
188     hybrid_handler::wallops_sts (char const * const text)
189     {
190     sts (":%s WALLOPS :%s", ME, text);
191     }
192    
193     /* join a channel */
194     void
195     hybrid_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
196     {
197     if (isnew)
198     sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, modes, CLIENT_NAME (u));
199     else
200     sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (u));
201     }
202    
203     void
204     hybrid_handler::chan_lowerts (channel_t *c, user_t *u)
205     {
206     slog (LG_DEBUG, "hybrid_chan_lowerts(): lowering TS for %s to %ld", c->name, (long) c->ts);
207     sts (":%s SJOIN %ld %s %s :@%s", ME, c->ts, c->name, channel_modes (c, true), CLIENT_NAME (u));
208     if (ircd->uses_uid)
209     chanban_clear (c);
210     }
211    
212     /* kicks a user from a channel */
213     void
214     hybrid_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
215     {
216     channel_t *chan = channel_find (channel);
217     user_t *user = user_find (to);
218     user_t *from_p = user_find (from);
219    
220     if (!chan || !user)
221     return;
222    
223     if (chan->ts != 0 || chanuser_find (chan, from_p))
224     sts (":%s KICK %s %s :%s", CLIENT_NAME (from_p), channel, CLIENT_NAME (user), reason);
225     else
226     sts (":%s KICK %s %s :%s", ME, channel, CLIENT_NAME (user), reason);
227    
228     chanuser_delete (chan, user);
229     }
230    
231     /* PRIVMSG wrapper */
232     void
233     hybrid_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
234     {
235     va_list ap;
236     char buf[BUFSIZE];
237     user_t *u = user_find (from);
238     user_t *t = user_find (target);
239    
240     if (!u)
241     return;
242    
243     va_start (ap, fmt);
244     vsnprintf (buf, BUFSIZE, fmt, ap);
245     va_end (ap);
246    
247     /* If this is to a channel, it's the snoop channel so chanserv
248     * is on it -- jilles
249     *
250     * Well, now it's operserv, but yes it's safe to assume that
251     * the source would be able to send to whatever target it is
252     * sending to. --nenolod
253     */
254     sts (":%s PRIVMSG %s :%s", CLIENT_NAME (u), t ? CLIENT_NAME (t) : target, buf);
255     }
256    
257     /* NOTICE wrapper */
258     void
259     hybrid_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
260     {
261     sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, CLIENT_NAME (target), text);
262     }
263    
264     void
265     hybrid_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
266     {
267     if (!strcmp (mask, "*"))
268 pippijn 1.8 foreach (tld_t *tld, tld_t::list)
269     sts (":%s NOTICE %s*%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, tld->name, text);
270 pippijn 1.4 else
271     sts (":%s NOTICE %s%s :%s", from ? CLIENT_NAME (from) : ME, ircd->tldprefix, mask, text);
272     }
273    
274     void
275     hybrid_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
276     {
277     if (from == NULL || chanuser_find (target, from))
278     sts (":%s NOTICE %s :%s", from ? CLIENT_NAME (from) : ME, target->name, text);
279     else
280     /* not on channel, let's send it from the server
281     * hyb6 won't accept this, oh well, they'll have to
282     * enable join_chans -- jilles */
283     sts (":%s NOTICE %s :[%s:%s]: %s", ME, target->name, from->nick, target->name, text);
284     }
285    
286     void
287     hybrid_handler::wallchops (user_t *sender, channel_t *channel, char const * const message)
288     {
289     if (chanuser_find (channel, sender))
290     sts (":%s NOTICE @%s :%s", CLIENT_NAME (sender), channel->name, message);
291     else /* do not join for this, everyone would see -- jilles */
292     handler::wallchops (sender, channel, message);
293     }
294    
295     /* numeric wrapper */
296     void
297     hybrid_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
298     {
299     va_list ap;
300     char buf[BUFSIZE];
301     user_t *t = user_find (target);
302    
303     va_start (ap, fmt);
304     vsnprintf (buf, BUFSIZE, fmt, ap);
305     va_end (ap);
306    
307     sts (":%s %d %s %s", ME, numeric, CLIENT_NAME (t), buf);
308     }
309    
310     /* KILL wrapper */
311     void
312     hybrid_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
313     {
314     va_list ap;
315     char buf[BUFSIZE];
316     user_t *killer = user_find (from);
317     user_t *victim = user_find (nick);
318    
319     va_start (ap, fmt);
320     vsnprintf (buf, BUFSIZE, fmt, ap);
321     va_end (ap);
322    
323     sts (":%s KILL %s :%s!%s!%s (%s)", killer ? CLIENT_NAME (killer) : ME, victim ? CLIENT_NAME (victim) : nick, from, from, from, buf);
324     }
325    
326     /* PART wrapper */
327     void
328     hybrid_handler::part_sts (channel_t *c, user_t *u)
329     {
330     sts (":%s PART %s", CLIENT_NAME (u), c->name);
331     }
332    
333     /* server-to-server KLINE wrapper */
334     void
335     hybrid_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
336     {
337     if (!me.connected)
338     return;
339    
340     sts (":%s KLINE %s %ld %s %s :%s", CLIENT_NAME (opersvs.me->me), server, duration, user, host, reason);
341     }
342    
343     /* server-to-server UNKLINE wrapper */
344     void
345     hybrid_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
346     {
347     if (!me.connected)
348     return;
349    
350     sts (":%s UNKLINE %s %s %s", CLIENT_NAME (opersvs.me->me), server, user, host);
351     }
352    
353     /* topic wrapper */
354     void
355     hybrid_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
356     {
357     int joined = 0;
358    
359     if (!me.connected || !c)
360     return;
361    
362     /* If possible, try to use TB
363     * Note that because TOPIC does not contain topicTS, it may be
364     * off a few seconds on other servers, hence the 60 seconds here.
365     * -- jilles */
366     if (use_tb && *topic != '\0')
367     {
368     /* Restoring old topic */
369     if (ts < prevts || prevts == 0)
370     {
371     if (prevts != 0 && ts + 60 > prevts)
372     ts = prevts - 60;
373     sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
374     c->topicts = ts;
375     return;
376     }
377     /* Tweaking a topic */
378     else if (ts == prevts)
379     {
380     ts -= 60;
381     sts (":%s TB %s %ld %s :%s", ME, c->name, ts, setter, topic);
382     c->topicts = ts;
383     return;
384     }
385     }
386     /* We have to be on channel to change topic.
387     * We cannot nicely change topic from the server:
388     * :server.name TOPIC doesn't propagate and TB requires
389     * us to specify an older topicts.
390     * -- jilles
391     */
392     if (!chanuser_find (c, chansvs.me->me))
393 pippijn 1.1 {
394 pippijn 1.4 sts (":%s SJOIN %ld %s + :@%s", ME, c->ts, c->name, CLIENT_NAME (chansvs.me->me));
395     joined = 1;
396 pippijn 1.1 }
397 pippijn 1.4 sts (":%s TOPIC %s :%s", CLIENT_NAME (chansvs.me->me), c->name, topic);
398     if (joined)
399     sts (":%s PART %s :Topic set", CLIENT_NAME (chansvs.me->me), c->name);
400     c->topicts = NOW;
401     }
402    
403     /* mode wrapper */
404     void
405     hybrid_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
406     {
407     user_t *u = user_find (sender);
408    
409     if (!me.connected || !u)
410     return;
411    
412     if (ircd->uses_uid)
413     sts (":%s TMODE %ld %s %s", CLIENT_NAME (u), target->ts, target->name, modes);
414     else
415     sts (":%s MODE %s %s", CLIENT_NAME (u), target->name, modes);
416     }
417    
418     /* ping wrapper */
419     void
420     hybrid_handler::ping_sts (void)
421     {
422     if (!me.connected)
423     return;
424    
425     sts ("PING :%s", me.name);
426     }
427    
428     /* protocol-specific stuff to do on login */
429     void
430     hybrid_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
431     {
432     user_t *u = user_find (origin);
433    
434     if (!me.connected || !use_rserv_support || !u)
435     return;
436    
437     sts (":%s ENCAP * SU %s %s", ME, CLIENT_NAME (u), user);
438     }
439    
440     /* protocol-specific stuff to do on login */
441     bool
442     hybrid_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
443     {
444     user_t *u = user_find (origin);
445    
446     if (!me.connected || !use_rserv_support || !u)
447     return false;
448    
449     sts (":%s ENCAP * SU %s", ME, CLIENT_NAME (u));
450 pippijn 1.1 return false;
451 pippijn 1.4 }
452    
453     void
454     hybrid_handler::jupe (char const * const server, char const * const reason)
455     {
456     if (!me.connected)
457     return;
458    
459     server_delete (server);
460     sts (":%s SQUIT %s :%s", CLIENT_NAME (opersvs.me->me), server, reason);
461     sts (":%s SERVER %s 2 :(H) %s", me.name, server, reason);
462     }
463    
464     void
465     hybrid_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
466     {
467     if (use_rsfnc)
468     /* XXX assumes whole net has it -- jilles */
469     sts (":%s ENCAP %s RSFNC %s %s %lu %lu", ME, u->server->name, CLIENT_NAME (u), newnick, (unsigned long) (NOW - 60), (unsigned long) u->ts);
470     else
471     handler::fnc_sts (source, u, newnick, type);
472     }
473    
474     void
475     hybrid_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
476     {
477     user_t *tu = user_find (target);
478    
479     if (!tu)
480     return;
481    
482     notice (source, target, "Setting your host to \2%s\2.", host);
483     sts (":%s CHGHOST %s :%s", ME, tu->nick, host);
484     }
485    
486     void
487     hybrid_handler::holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account)
488     {
489     if (duration == 0)
490     return; /* can't do this safely */
491     sts (":%s ENCAP * RESV %d %s 0 :Reserved by %s for nickname owner (%s)", CLIENT_NAME (source), duration > 300 ? 300 : duration, nick, source->nick, account != NULL ? account->name : nick);
492     }
493    
494     static void
495     m_topic (sourceinfo_t *si, int parc, char *parv[])
496     {
497     channel_t *c = channel_find (parv[0]);
498    
499     if (c == NULL)
500     return;
501    
502     handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
503     }
504    
505     static void
506     m_tb (sourceinfo_t *si, int parc, char *parv[])
507     {
508     channel_t *c = channel_find (parv[0]);
509     time_t ts = atol (parv[1]);
510    
511     if (c == NULL)
512     return;
513    
514     if (c->topic != NULL && c->topicts <= ts)
515     {
516     slog (LG_DEBUG, "m_tb(): ignoring newer topic on %s", c->name);
517     return;
518     }
519    
520     handle_topic_from (si, c, parc > 3 ? parv[2] : si->s->name, ts, parv[parc - 1]);
521     }
522    
523     static void
524     m_ping (sourceinfo_t *si, int parc, char *parv[])
525     {
526     /* reply to PING's */
527     sts (":%s PONG %s %s", ME, me.name, parv[0]);
528     }
529    
530     static void
531     m_pong (sourceinfo_t *si, int parc, char *parv[])
532     {
533     server_t *s;
534    
535     /* someone replied to our PING */
536     if (!parv[0])
537     return;
538     s = server_find (parv[0]);
539     if (s == NULL)
540     return;
541     handle_eob (s);
542    
543     if (irccasecmp (me.actual, parv[0]))
544     return;
545    
546     me.uplinkpong = NOW;
547    
548     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
549     if (me.bursting)
550     {
551     #ifdef HAVE_GETTIMEOFDAY
552     e_time (burstime, &burstime);
553    
554     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
555    
556     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
557     #else
558     slog (LG_INFO, "m_pong(): finished synching with uplink");
559     wallops ("Finished synching to network.");
560     #endif
561    
562     me.bursting = false;
563     }
564     }
565    
566     static void
567     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
568     {
569     if (parc != 2)
570     return;
571    
572     handle_message (si, parv[0], false, parv[1]);
573     }
574    
575     static void
576     m_notice (sourceinfo_t *si, int parc, char *parv[])
577     {
578     if (parc != 2)
579     return;
580    
581     handle_message (si, parv[0], true, parv[1]);
582     }
583    
584     static void
585     m_sjoin (sourceinfo_t *si, int parc, char *parv[])
586     {
587     /* -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur */
588    
589     channel_t *c;
590     bool keep_new_modes = true;
591     unsigned int userc;
592     char *userv[256];
593     unsigned int i;
594     time_t ts;
595     char *p;
596    
597     /* :origin SJOIN ts chan modestr [key or limits] :users */
598     c = channel_find (parv[1]);
599     ts = atol (parv[0]);
600    
601     if (!c)
602     {
603     slog (LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
604     c = channel_add (parv[1], ts, si->s);
605     }
606    
607     if (ts == 0 || c->ts == 0)
608     {
609     if (c->ts != 0)
610     slog (LG_INFO, "m_sjoin(): server %s changing TS on %s from %ld to 0", si->s->name, c->name, (long) c->ts);
611     c->ts = 0;
612     c->callback.tschange (c);
613     }
614     else if (ts < c->ts)
615     {
616     chanuser_t *cu;
617     node_t *n;
618    
619     /* the TS changed. a TS change requires the following things
620     * to be done to the channel: reset all modes to nothing, remove
621     * all status modes on known users on the channel (including ours),
622     * and set the new TS.
623     *
624     * if the source does TS6, also remove all bans
625     * note that JOIN does not do this
626     */
627    
628     clear_simple_modes (c);
629     if (si->s->sid != NULL)
630     chanban_clear (c);
631    
632     LIST_FOREACH (n, c->members.head)
633     {
634     cu = (chanuser_t *) n->data;
635     if (cu->user->server == me.me)
636     {
637     /* it's a service, reop */
638     sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
639     sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
640     cu->modes = CMODE_OP;
641     }
642     else
643     cu->modes = 0;
644     }
645    
646     slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
647    
648     c->ts = ts;
649     c->callback.tschange (c);
650     }
651     else if (ts > c->ts)
652     keep_new_modes = false;
653    
654     if (keep_new_modes)
655     channel_mode (NULL, c, parc - 3, parv + 2);
656    
657     userc = sjtoken (parv[parc - 1], ' ', userv);
658    
659     if (keep_new_modes)
660     for (i = 0; i < userc; i++)
661     chanuser_add (c, userv[i]);
662     else
663     for (i = 0; i < userc; i++)
664     {
665     p = userv[i];
666     while (*p == '@' || *p == '%' || *p == '+')
667     p++;
668     /* XXX for TS5 we should mark them deopped
669     * if they were opped and drop modes from them
670     * -- jilles */
671     chanuser_add (c, p);
672     }
673    
674     if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
675     channel_delete (c);
676     }
677    
678     static void
679     m_join (sourceinfo_t *si, int parc, char *parv[])
680     {
681     /* -> :1JJAAAAAB JOIN 1127474195 #test +tn */
682     bool keep_new_modes = true;
683     node_t *n, *tn;
684     channel_t *c;
685     chanuser_t *cu;
686     time_t ts;
687    
688     /* JOIN 0 is really a part from all channels */
689     /* be sure to allow joins to TS 0 channels -- jilles */
690     if (parv[0][0] == '0' && parc <= 2)
691     {
692     LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
693     {
694     cu = (chanuser_t *) n->data;
695     chanuser_delete (cu->chan, si->su);
696     }
697     return;
698     }
699    
700     /* :user JOIN ts chan modestr [key or limits] */
701     c = channel_find (parv[1]);
702     ts = atol (parv[0]);
703    
704     if (!c)
705     {
706     slog (LG_DEBUG, "m_join(): new channel: %s", parv[1]);
707     c = channel_add (parv[1], ts, si->su->server);
708     }
709    
710     if (ts == 0 || c->ts == 0)
711     {
712     if (c->ts != 0)
713     slog (LG_INFO, "m_join(): server %s changing TS on %s from %ld to 0", si->su->server->name, c->name, (long) c->ts);
714     c->ts = 0;
715     c->callback.tschange (c);
716     }
717     else if (ts < c->ts)
718     {
719     /* the TS changed. a TS change requires the following things
720     * to be done to the channel: reset all modes to nothing, remove
721     * all status modes on known users on the channel (including ours),
722     * and set the new TS.
723     */
724     clear_simple_modes (c);
725    
726     LIST_FOREACH (n, c->members.head)
727     {
728     cu = (chanuser_t *) n->data;
729     if (cu->user->server == me.me)
730     {
731     /* it's a service, reop */
732     sts (":%s PART %s :Reop", CLIENT_NAME (cu->user), c->name);
733     sts (":%s SJOIN %ld %s + :@%s", ME, ts, c->name, CLIENT_NAME (cu->user));
734     cu->modes = CMODE_OP;
735     }
736     else
737     cu->modes = 0;
738     }
739     slog (LG_DEBUG, "m_join(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
740     c->ts = ts;
741     c->callback.tschange (c);
742     }
743     else if (ts > c->ts)
744     keep_new_modes = false;
745    
746     if (keep_new_modes)
747     channel_mode (NULL, c, parc - 2, parv + 2);
748    
749     chanuser_add (c, CLIENT_NAME (si->su));
750     }
751    
752     static void
753     m_bmask (sourceinfo_t *si, int parc, char *parv[])
754     {
755     unsigned int ac, i;
756     char *av[256];
757     channel_t *c = channel_find (parv[1]);
758     int type;
759    
760     /* :1JJ BMASK 1127474361 #services b :*!*@*evil* *!*eviluser1@* */
761     if (!c)
762     {
763     slog (LG_DEBUG, "m_bmask(): got bmask for unknown channel");
764     return;
765     }
766    
767     if (atol (parv[0]) > c->ts)
768 pippijn 1.1 return;
769 pippijn 1.4
770     type = *parv[2];
771     if (!strchr (ircd->ban_like_modes, type))
772     {
773     slog (LG_DEBUG, "m_bmask(): got unknown type '%c'", type);
774     return;
775     }
776    
777     ac = sjtoken (parv[parc - 1], ' ', av);
778    
779     for (i = 0; i < ac; i++)
780     chanban_add (c, av[i], type);
781     }
782    
783     static void
784     m_part (sourceinfo_t *si, int parc, char *parv[])
785     {
786     int chanc;
787     char *chanv[256];
788     int i;
789    
790     chanc = sjtoken (parv[0], ',', chanv);
791     for (i = 0; i < chanc; i++)
792     {
793     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
794    
795     chanuser_delete (channel_find (chanv[i]), si->su);
796     }
797     }
798    
799     static void
800     m_nick (sourceinfo_t *si, int parc, char *parv[])
801     {
802     server_t *s;
803     user_t *u;
804    
805     /* got the right number of args for an introduction? */
806     if (parc == 8)
807     {
808     s = server_find (parv[6]);
809     if (!s)
810     {
811     slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
812     return;
813     }
814    
815     slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
816    
817     u = user_add (parv[0], parv[4], parv[5], NULL, NULL, NULL, parv[7], s, atoi (parv[2]));
818    
819     user_mode (u, parv[3]);
820    
821     /* If server is not yet EOB we will do this later.
822     * This avoids useless "please identify" -- jilles */
823     if (s->flags & SF_EOB)
824     handle_nickchange (user_find (parv[0]));
825     }
826    
827     /* if it's only 2 then it's a nickname change */
828     else if (parc == 2)
829     {
830     if (!si->su)
831     {
832     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
833     return;
834     }
835    
836     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
837    
838     user_changenick (si->su, parv[0], atoi (parv[1]));
839    
840     /* It could happen that our PING arrived late and the
841     * server didn't acknowledge EOB yet even though it is
842     * EOB; don't send double notices in that case -- jilles */
843     if (si->su->server->flags & SF_EOB)
844     handle_nickchange (si->su);
845     }
846     else
847     {
848     int i;
849     slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
850    
851     for (i = 0; i < parc; i++)
852     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
853     }
854     }
855    
856     static void
857     m_uid (sourceinfo_t *si, int parc, char *parv[])
858     {
859     server_t *s;
860     user_t *u;
861    
862     /* got the right number of args for an introduction? */
863     if (parc == 9)
864 pippijn 1.1 {
865 pippijn 1.4 s = si->s;
866     slog (LG_DEBUG, "m_uid(): new user on `%s': %s", s->name, parv[0]);
867    
868    
869     u = user_add (parv[0], parv[4], parv[5], NULL, parv[6], parv[7], parv[8], s, atoi (parv[2]));
870    
871     user_mode (u, parv[3]);
872    
873     /* If server is not yet EOB we will do this later.
874     * This avoids useless "please identify" -- jilles
875     */
876     if (s->flags & SF_EOB)
877     handle_nickchange (user_find (parv[0]));
878 pippijn 1.1 }
879 pippijn 1.4 else
880     {
881     int i;
882     slog (LG_DEBUG, "m_uid(): got UID with wrong number of params");
883    
884     for (i = 0; i < parc; i++)
885     slog (LG_DEBUG, "m_uid(): parv[%d] = %s", i, parv[i]);
886 pippijn 1.1 }
887 pippijn 1.4 }
888    
889     static void
890     m_quit (sourceinfo_t *si, int parc, char *parv[])
891     {
892     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
893    
894     /* user_delete() takes care of removing channels and so forth */
895     user_delete (si->su);
896     }
897    
898     static void
899     m_mode (sourceinfo_t *si, int parc, char *parv[])
900     {
901     if (*parv[0] == '#')
902     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
903     else
904     user_mode (user_find (parv[0]), parv[1]);
905     }
906    
907     static void
908     m_tmode (sourceinfo_t *si, int parc, char *parv[])
909     {
910     channel_t *c;
911    
912     /* -> :1JJAAAAAB TMODE 1127511579 #new +o 2JJAAAAAB */
913     c = channel_find (parv[1]);
914     if (c == NULL)
915 pippijn 1.1 {
916 pippijn 1.4 slog (LG_DEBUG, "m_tmode(): nonexistent channel %s", parv[1]);
917     return;
918 pippijn 1.1 }
919 pippijn 1.4
920     if (atol (parv[0]) > c->ts)
921 pippijn 1.1 return;
922 pippijn 1.4
923     channel_mode (NULL, c, parc - 2, &parv[2]);
924     }
925    
926     static void
927     m_kick (sourceinfo_t *si, int parc, char *parv[])
928     {
929     user_t *u = user_find (parv[1]);
930     channel_t *c = channel_find (parv[0]);
931    
932     /* -> :rakaur KICK #shrike rintaun :test */
933     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
934    
935     if (!u)
936     {
937     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
938     return;
939     }
940    
941     if (!c)
942     {
943     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
944     return;
945     }
946    
947     if (!chanuser_find (c, u))
948     {
949     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
950     return;
951     }
952    
953     chanuser_delete (c, u);
954    
955     /* if they kicked us, let's rejoin */
956     if (is_internal_client (u))
957     {
958     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
959     join (parv[0], u->nick);
960     }
961     }
962    
963     static void
964     m_kill (sourceinfo_t *si, int parc, char *parv[])
965     {
966     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
967     }
968    
969     static void
970     m_squit (sourceinfo_t *si, int parc, char *parv[])
971     {
972     slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
973     server_delete (parv[0]);
974     }
975    
976     static void
977     m_server (sourceinfo_t *si, int parc, char *parv[])
978     {
979     server_t *s;
980    
981     slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
982     s = handle_server (si, parv[0], si->s || !ircd->uses_uid ? NULL : ts6sid, atoi (parv[1]), parv[2]);
983    
984     if (s != NULL && s->uplink != me.me)
985     {
986     /* elicit PONG for EOB detection; pinging uplink is
987     * already done elsewhere -- jilles
988     */
989     sts (":%s PING %s %s", ME, me.name, s->name);
990     }
991     }
992    
993     static void
994     m_sid (sourceinfo_t *si, int parc, char *parv[])
995     {
996     /* -> :1JJ SID file. 2 00F :telnet server */
997     server_t *s;
998    
999     slog (LG_DEBUG, "m_sid(): new server: %s", parv[0]);
1000     s = handle_server (si, parv[0], parv[2], atoi (parv[1]), parv[3]);
1001    
1002     if (s != NULL && s->uplink != me.me)
1003     {
1004     /* elicit PONG for EOB detection; pinging uplink is
1005     * already done elsewhere -- jilles
1006     */
1007     sts (":%s PING %s %s", ME, me.name, s->sid);
1008     }
1009     }
1010    
1011     static void
1012     m_stats (sourceinfo_t *si, int parc, char *parv[])
1013     {
1014     handle_stats (si->su, parv[0][0]);
1015     }
1016    
1017     static void
1018     m_admin (sourceinfo_t *si, int parc, char *parv[])
1019     {
1020     handle_admin (si->su);
1021     }
1022    
1023     static void
1024     m_version (sourceinfo_t *si, int parc, char *parv[])
1025     {
1026     handle_version (si->su);
1027     }
1028    
1029     static void
1030     m_info (sourceinfo_t *si, int parc, char *parv[])
1031     {
1032     handle_info (si->su);
1033     }
1034    
1035     static void
1036     m_whois (sourceinfo_t *si, int parc, char *parv[])
1037     {
1038     handle_whois (si->su, parv[1]);
1039     }
1040    
1041     static void
1042     m_trace (sourceinfo_t *si, int parc, char *parv[])
1043     {
1044     handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
1045     }
1046    
1047     static void
1048     m_away (sourceinfo_t *si, int parc, char *parv[])
1049     {
1050     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1051     }
1052    
1053     static void
1054     m_pass (sourceinfo_t *si, int parc, char *parv[])
1055     {
1056     /* TS5: PASS mypassword :TS
1057     * TS6: PASS mypassword TS 6 :sid */
1058     if (strcmp (curr_uplink->pass, parv[0]))
1059     {
1060     slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
1061     runflags |= RF_SHUTDOWN;
1062     }
1063    
1064     if (ircd->uses_uid && parc > 3 && atoi (parv[2]) >= 6)
1065     strlcpy (ts6sid, parv[3], sizeof (ts6sid));
1066     else
1067 pippijn 1.1 {
1068 pippijn 1.4 if (ircd->uses_uid)
1069     {
1070     slog (LG_INFO, "m_pass(): uplink does not support TS6, falling back to TS5");
1071     ircd->uses_uid = false;
1072     }
1073     ts6sid[0] = '\0';
1074     }
1075     }
1076    
1077     static void
1078     m_error (sourceinfo_t *si, int parc, char *parv[])
1079     {
1080     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1081     }
1082    
1083     static void
1084     m_encap (sourceinfo_t *si, int parc, char *parv[])
1085     {
1086     user_t *u;
1087    
1088     if (match (parv[0], me.name))
1089 pippijn 1.1 return;
1090 pippijn 1.4 if (!irccasecmp (parv[1], "LOGIN"))
1091     {
1092     /* :jilles ENCAP * LOGIN jilles */
1093     if (!use_rserv_support)
1094     return;
1095     if (parc < 3)
1096     return;
1097     u = si->su;
1098     if (u == NULL)
1099     return;
1100     if (u->server->flags & SF_EOB)
1101     {
1102     slog (LG_DEBUG, "m_encap(): %s sent ENCAP LOGIN for user %s outside burst", u->server->name, si->su->nick);
1103     return;
1104     }
1105     handle_burstlogin (u, parv[2]);
1106     }
1107     }
1108    
1109     static void
1110     m_capab (sourceinfo_t *si, int parc, char *parv[])
1111     {
1112     char *p;
1113    
1114     use_rserv_support = false;
1115     use_tb = false;
1116     use_rsfnc = false;
1117     for (p = strtok (parv[0], " "); p != NULL; p = strtok (NULL, " "))
1118     {
1119     if (!irccasecmp (p, "SERVICES"))
1120     {
1121     slog (LG_DEBUG, "m_capab(): uplink has rserv extensions, enabling support.");
1122     use_rserv_support = true;
1123     }
1124     if (!irccasecmp (p, "TB"))
1125     {
1126     slog (LG_DEBUG, "m_capab(): uplink does topic bursting, using if appropriate.");
1127     use_tb = true;
1128     }
1129     if (!irccasecmp (p, "RSFNC"))
1130     {
1131     slog (LG_DEBUG, "m_capab(): uplink does RSFNC, assuming whole net has it.");
1132     use_rsfnc = true;
1133     }
1134     }
1135    
1136     /* Now we know whether or not we should enable services support,
1137     * so burst the clients.
1138     * --nenolod
1139     */
1140     services_init ();
1141     }
1142    
1143     static void
1144     m_chghost (sourceinfo_t *si, int parc, char *parv[])
1145     {
1146     user_t *u = user_find (parv[0]);
1147    
1148     if (!u)
1149 pippijn 1.1 return;
1150 pippijn 1.4
1151     strlcpy (u->vhost, parv[1], HOSTLEN);
1152     }
1153    
1154     static void
1155     m_motd (sourceinfo_t *si, int parc, char *parv[])
1156     {
1157     handle_motd (si->su);
1158     }
1159    
1160     /* Server ended their burst: warn all their users if necessary -- jilles */
1161     static void
1162     server_eob (server_t *s)
1163     {
1164     node_t *n;
1165    
1166     LIST_FOREACH (n, s->userlist.head)
1167 pippijn 1.1 {
1168 pippijn 1.4 handle_nickchange ((user_t *) n->data);
1169 pippijn 1.1 }
1170 pippijn 1.4 }
1171    
1172     static pcommand_t const pcommands[] = {
1173     { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
1174     { "PONG", m_pong, 1, MSRC_SERVER },
1175     { "PRIVMSG", m_privmsg, 2, MSRC_USER },
1176     { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
1177     { "SJOIN", m_sjoin, 4, MSRC_SERVER },
1178     { "PART", m_part, 1, MSRC_USER },
1179     { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
1180     { "QUIT", m_quit, 1, MSRC_USER },
1181     { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
1182     { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
1183     { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
1184     { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
1185     { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
1186     { "STATS", m_stats, 2, MSRC_USER },
1187     { "ADMIN", m_admin, 1, MSRC_USER },
1188     { "VERSION", m_version, 1, MSRC_USER },
1189     { "INFO", m_info, 1, MSRC_USER },
1190     { "WHOIS", m_whois, 2, MSRC_USER },
1191     { "TRACE", m_trace, 1, MSRC_USER },
1192     { "AWAY", m_away, 0, MSRC_USER },
1193     { "JOIN", m_join, 1, MSRC_USER },
1194     { "PASS", m_pass, 1, MSRC_UNREG },
1195     { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1196     { "TOPIC", m_topic, 2, MSRC_USER },
1197     { "TB", m_tb, 3, MSRC_SERVER },
1198     { "ENCAP", m_encap, 2, MSRC_USER | MSRC_SERVER },
1199     { "CAPAB", m_capab, 1, MSRC_UNREG },
1200     { "UID", m_uid, 9, MSRC_SERVER },
1201     { "BMASK", m_bmask, 4, MSRC_SERVER },
1202     { "TMODE", m_tmode, 3, MSRC_USER | MSRC_SERVER },
1203     { "SID", m_sid, 4, MSRC_SERVER },
1204     { "CHGHOST", m_chghost, 2, MSRC_USER | MSRC_SERVER },
1205     { "MOTD", m_motd, 1, MSRC_USER },
1206     };
1207    
1208     hybrid_handler::hybrid_handler ()
1209     {
1210     mode_list = hybrid_mode_list;
1211     ignore_mode_list = hybrid_ignore_mode_list;
1212     status_mode_list = hybrid_status_mode_list;
1213     prefix_mode_list = hybrid_prefix_mode_list;
1214    
1215     ircd = &Hybrid;
1216    
1217     pcommand_add (&pcommands);
1218    
1219     server_t::callback.eob.attach (server_eob);
1220     }
1221    
1222     hybrid_handler::~hybrid_handler ()
1223 pippijn 1.1 {
1224 pippijn 1.4 server_t::callback.eob.detach (server_eob);
1225    
1226     pcommand_delete (&pcommands);
1227    
1228     ircd = NULL;
1229    
1230     prefix_mode_list = NULL;
1231     status_mode_list = NULL;
1232     ignore_mode_list = NULL;
1233     mode_list = NULL;
1234 pippijn 1.1 }
1235 pippijn 1.4 } // namespace protocol
1236 pippijn 1.1
1237 pippijn 1.4 #define FACREG_TYPE protocol::hybrid_handler
1238     #define FACREG_TYPE_NAME "hybrid"
1239     #define FACREG_INTERFACE_TYPE protocol::handler
1240     #include <ermyth/factory_reg.h>