ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/hybrid.C
Revision: 1.1
Committed: Thu Jul 19 08:24:55 2007 UTC (19 years, 3 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Log Message:
initial import. the most important changes since Atheme are:
- fixed many memory leaks
- fixed many bugs
- converted to C++ and use more STL containers
- added a (not very enhanced yet) perl module
- greatly improved XML-RPC speed
- added a JSON-RPC module with code from json-cpp
- added a valgrind memcheck module to operserv
- added a more object oriented base64 implementation
- added a specialised unit test framework
- improved stability
- use gettimeofday() if available
- reworked adding/removing commands
- MemoServ IGNORE DEL can now remove indices

File Contents

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