ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/hybrid.C
Revision: 1.7
Committed: Sun Sep 16 18:54:44 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.6: +10 -5 lines
Log Message:
#defines to enum

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