ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/inspircd12.C
Revision: 1.9
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.8: +6 -2 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.8 /**
2     * inspircd12.C: This file contains protocol support for spanning tree 1.2 branch inspircd.
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.1 * Copyright © 2005-2006 Atheme Development Group
10     * Rights to this code are documented in doc/pod/license.pod.
11     *
12 pippijn 1.9 * $Id: inspircd12.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $
13 pippijn 1.1 */
14    
15 pippijn 1.9 #include <boost/foreach.hpp>
16    
17 pippijn 1.1 #include "atheme.h"
18 pippijn 1.9 #include <util/time.h>
19     #include <libermyth.h>
20 pippijn 1.7 #include "servers.h"
21 pippijn 1.1 #include <account/mychan.h>
22     #include <account/myuser.h>
23     #include "uplink.h"
24     #include "pmodule.h"
25     #include "protocol/inspircd.h"
26    
27 pippijn 1.9 static char const rcsid[] = "$Id: inspircd12.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $";
28 pippijn 1.1
29     /* *INDENT-OFF* */
30    
31     namespace protocol
32     {
33     static ircd_t InspIRCd = {
34     "InspIRCd 1.2.x", /* IRCd name */
35     "$", /* TLD Prefix, used by Global. */
36     true, /* Whether or not we use IRCNet/TS6 UID */
37     false, /* Whether or not we use RCOMMAND */
38 pippijn 1.5 false, /* Whether or not we support channel owners. */
39     false, /* Whether or not we support channel protection. */
40 pippijn 1.1 true, /* Whether or not we support halfops. */
41     false, /* Whether or not we use P10 */
42     true, /* Whether or not we use vHosts. */
43     CMODE_OPERONLY, /* Oper-only cmodes */
44     CMODE_OWNER, /* Integer flag for owner channel flag. */
45     CMODE_PROTECT, /* Integer flag for protect channel flag. */
46     CMODE_HALFOP, /* Integer flag for halfops. */
47     "+q", /* Mode we set for owner. */
48     "+a", /* Mode we set for protect. */
49     "+h", /* Mode we set for halfops. */
50     PROTOCOL_INSPIRCD, /* Protocol type */
51     0, /* Permanent cmodes */
52     "beIg", /* Ban-like cmodes */
53     'e', /* Except mchar */
54     'I', /* Invex mchar */
55     IRCD_CIDR_BANS /* Flags */
56     };
57    
58     struct inspircd12_handler : handler
59     {
60     inspircd12_handler ();
61     virtual ~inspircd12_handler ();
62    
63     virtual unsigned int server_login (void);
64     virtual void introduce_nick (user_t *u);
65     virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
66     virtual void quit_sts (user_t *u, char const * const reason);
67     virtual void wallops_sts (char const * const text);
68     virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
69     virtual void chan_lowerts (channel_t *c, user_t *u);
70     virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
71     virtual void mdchange (char const * const target, char const * const key, char const * const value);
72     virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
73     virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
74     virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
75     virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
76     virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
77     virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
78     virtual void part_sts (channel_t *c, user_t *u);
79     virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
80     virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
81     virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
82     virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
83     virtual void ping_sts (void);
84     virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
85     virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
86     virtual void jupe (char const * const server, char const * const reason);
87     virtual void sethost_sts (char const * const source, char const * const target, char const * const host);
88     virtual void fnc_sts (user_t *source, user_t *u, char const * const newnick, int type);
89     virtual void holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account);
90     };
91    
92     static cmode_t inspircd12_mode_list[] = {
93     { 'i', CMODE_INVITE },
94     { 'm', CMODE_MOD },
95     { 'n', CMODE_NOEXT },
96     { 'p', CMODE_PRIV },
97     { 's', CMODE_SEC },
98     { 't', CMODE_TOPIC },
99     { 'c', CMODE_NOCOLOR },
100     { 'M', CMODE_MODREG },
101     { 'R', CMODE_REGONLY },
102     { 'O', CMODE_OPERONLY },
103     { 'S', CMODE_STRIP },
104     { 'K', CMODE_NOKNOCK },
105     { 'V', CMODE_NOINVITE },
106     { 'C', CMODE_NOCTCP },
107     { 'N', CMODE_STICKY },
108     { 'G', CMODE_CENSOR },
109     { 'P', CMODE_NOCAPS },
110     { 'z', CMODE_SSLONLY },
111     { 'T', CMODE_NONOTICE },
112     { 'u', CMODE_HIDING },
113     { 'Q', CMODE_PEACE },
114     { '\0', 0 }
115     };
116    
117     static bool check_flood(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
118     static bool check_nickflood (char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
119     static bool check_jointhrottle(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
120     static bool check_forward(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
121     static bool check_rejoindelay(char const * const , channel_t *, mychan_t *, user_t *, myuser_t *);
122    
123 pippijn 1.6 static extmode_t inspircd12_ignore_mode_list[] = {
124 pippijn 1.1 { 'f', check_flood },
125     { 'F', check_nickflood },
126     { 'j', check_jointhrottle },
127     { 'L', check_forward },
128     { 'J', check_rejoindelay },
129     { '\0', 0 }
130     };
131    
132     static cmode_t inspircd12_status_mode_list[] = {
133     { 'q', CMODE_OWNER },
134     { 'a', CMODE_PROTECT },
135     { 'o', CMODE_OP },
136     { 'h', CMODE_HALFOP },
137     { 'v', CMODE_VOICE },
138     { '\0', 0 }
139     };
140    
141     static cmode_t inspircd12_prefix_mode_list[] = {
142     { '~', CMODE_OWNER },
143     { '&', CMODE_PROTECT },
144     { '@', CMODE_OP },
145     { '%', CMODE_HALFOP },
146     { '+', CMODE_VOICE },
147     { '\0', 0 }
148     };
149    
150     /* CAPABilities */
151     static bool has_servicesmod = false;
152     static bool has_globopsmod = false;
153     static bool has_svshold = false;
154     static int has_protocol = 0;
155    
156     #define PROTOCOL_SNONOTICE 1102 /* has SNONOTICE and OPERNOTICE commands */
157     #define PROTOCOL_NEWTS 1104 /* does not send "confirming" FMODEs on TS changes and uses clear/ignore semantics also for FMODE */
158    
159     /* *INDENT-ON* */
160    
161     static bool
162     check_flood (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
163     {
164     /* +F doesn't support *, so don't bother checking for it -- w00t */
165     return check_jointhrottle (value, c, mc, u, mu);
166     }
167    
168     static bool
169     check_nickflood (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
170     {
171     return *value == '*' ? check_jointhrottle(value + 1, c, mc, u, mu) : check_jointhrottle(value, c, mc, u, mu);
172     }
173    
174     static bool
175     check_jointhrottle (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
176     {
177     char const *p;
178     char const *arg2;
179    
180     p = value, arg2 = NULL;
181     while (*p != '\0')
182     {
183     if (*p == ':')
184     {
185     if (arg2 != NULL)
186     return false;
187     arg2 = p + 1;
188     }
189     else if (!isdigit (*p))
190     return false;
191     p++;
192     }
193     if (arg2 == NULL)
194     return false;
195     if (p - arg2 > 10 || arg2 - value - 1 > 10 || !atoi (value) || !atoi (arg2))
196     return false;
197     return true;
198     }
199    
200     static bool
201     check_forward (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
202     {
203     channel_t *target_c;
204     mychan_t *target_mc;
205    
206     if (*value != '#' || strlen (value) > 50)
207     return false;
208     if (u == NULL && mu == NULL)
209     return true;
210     target_c = channel_find (value);
211 pippijn 1.4 target_mc = mychan_t::find (value);
212 pippijn 1.1 if (target_c == NULL && target_mc == NULL)
213     return false;
214     return true;
215     }
216    
217     static bool
218     check_rejoindelay (char const * const value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
219     {
220     char const *ch = value;
221    
222     while (*ch)
223     {
224     if (!isdigit (*ch))
225     return false;
226     ch++;
227     }
228    
229     if (atoi (value) <= 0)
230     {
231     return false;
232     }
233     else
234     {
235     return true;
236     }
237     }
238    
239    
240     /* login to our uplink */
241     unsigned int
242     inspircd12_handler::server_login (void)
243     {
244     int ret;
245    
246     /* Check if we have a numeric set. InspIRCd 1.2 protocol
247     * requires it. -nenolod
248     */
249     if (me.numeric == NULL)
250     {
251     slog (LG_ERROR, "inspircd12_handler::server_login(): inspircd 1.2 requires a unique identifier. set serverinfo::numeric.");
252     exit (EXIT_FAILURE);
253     }
254    
255     ret = sts ("SERVER %s %s 0 %s :%s", me.name, curr_uplink->pass, me.numeric, me.desc);
256     if (ret == 1)
257     return 1;
258    
259     me.bursting = true;
260     sts ("BURST");
261     /* XXX: Being able to get this data as a char* would be nice - Brain */
262     sts (":%s VERSION :atheme-%s. %s %s%s%s%s%s%s%s%s%s%s",
263     me.name,
264     version,
265     me.numeric,
266     match_mapping ? "A" : "",
267     log_debug_enabled () ? "d" : "",
268     me.auth ? "e" : "",
269     config_options.flood_msgs ? "F" : "",
270     config_options.leave_chans ? "l" : "",
271     config_options.join_chans ? "j" : "",
272     chansvs.changets ? "t" : "",
273     !match_mapping ? "R" : "",
274     config_options.raw ? "r" : "",
275     runflags & RF_LIVE ? "n" : "");
276     services_init ();
277     return 0;
278     }
279    
280     /* introduce a client */
281     void
282     inspircd12_handler::introduce_nick (user_t *u)
283     {
284     /* :penguin.omega.org.za UID 497AAAAAB 1188302517 OperServ 127.0.0.1 127.0.0.1 OperServ +s 127.0.0.1 :Operator Server */
285 pippijn 1.2 sts (":%s UID %s %ld %s %s %s %s +%s 0.0.0.0 %ld :%s", me.numeric, u->uid, u->ts, u->nick, u->host, u->host, u->user, "io", u->ts, u->gecos);
286 pippijn 1.1 sts (":%s OPERTYPE Services", u->uid);
287     }
288    
289     void
290     inspircd12_handler::quit_sts (user_t *u, char const * const reason)
291     {
292     if (!me.connected)
293     return;
294    
295     sts (":%s QUIT :%s", u->uid, reason);
296     }
297    
298     /* WALLOPS wrapper */
299     void
300     inspircd12_handler::wallops_sts (char const * const text)
301     {
302     char *sendernick = NULL;
303     user_t *u;
304     node_t *n;
305    
306     if (has_protocol >= PROTOCOL_SNONOTICE)
307     {
308     /* XXX */
309     if (has_globopsmod)
310 pippijn 1.2 sts (":%s SNONOTICE g :%s", me.numeric, text);
311 pippijn 1.1 else
312 pippijn 1.2 sts (":%s OPERNOTICE :%s", me.numeric, text);
313 pippijn 1.1 return;
314     }
315    
316     if (me.me == NULL)
317     return;
318    
319     if (is_internal_client (user_find_named (opersvs.nick)))
320     {
321     sendernick = opersvs.nick;
322     }
323     else
324     {
325     LIST_FOREACH (n, me.me->userlist.head)
326     {
327     u = (user_t *) n->data;
328    
329     sendernick = u->uid;
330     break;
331     }
332     }
333    
334     if (sendernick == NULL)
335     {
336     /*
337     * this means we have no pseudoclients -- under present inspircd, servers cannot globops, and
338     * thus, we will need to bail -- slog, and let them know. --w00t
339     */
340     slog (LG_ERROR, "wallops_sts(): InspIRCD requires at least one pseudoclient module to be loaded to send wallops.");
341     return;
342     }
343    
344     sts (":%s GLOBOPS :%s", sendernick, text);
345     }
346    
347     /* join a channel */
348     void
349     inspircd12_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
350     {
351     if (isnew)
352     {
353 pippijn 1.2 sts (":%s FJOIN %s %ld :@,%s", me.numeric, c->name, c->ts, u->uid);
354 pippijn 1.1 if (modes[0] && modes[1])
355 pippijn 1.2 sts (":%s FMODE %s %ld %s", me.numeric, c->name, c->ts, modes);
356 pippijn 1.1 }
357     else
358     {
359 pippijn 1.2 sts (":%s FJOIN %s %ld :@,%s", me.numeric, c->name, c->ts, u->uid);
360 pippijn 1.1 }
361     }
362    
363     void
364     inspircd12_handler::chan_lowerts (channel_t *c, user_t *u)
365     {
366     slog (LG_DEBUG, "inspircd12_handler::chan_lowerts(): lowering TS for %s to %ld", c->name, (long) c->ts);
367    
368 pippijn 1.2 sts (":%s FJOIN %s %ld :@,%s", me.numeric, c->name, c->ts, u->uid);
369     sts (":%s FMODE %s %ld %s", me.numeric, c->name, c->ts, channel_modes (c, true));
370 pippijn 1.1 }
371    
372     /* kicks a user from a channel */
373     void
374     inspircd12_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
375     {
376     channel_t *c = channel_find (channel);
377     user_t *u = user_find (to);
378     user_t *su = user_find (from);
379    
380     if (!c || !u)
381     return;
382    
383     sts (":%s KICK %s %s :%s", su->uid, channel, u->uid, reason);
384    
385     chanuser_delete (c, u);
386     }
387    
388     // changes ircd metadata
389     void
390     inspircd12_handler::mdchange (char const * const target, char const * const key, char const * const value)
391     {
392 pippijn 1.2 sts (":%s METADATA %s %s :%s", me.numeric, target, key, value);
393 pippijn 1.1 }
394    
395     /* PRIVMSG wrapper */
396     void
397     inspircd12_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
398     {
399     va_list ap;
400     char buf[BUFSIZE];
401     user_t *u = user_find (from);
402     user_t *su = user_find (from);
403    
404     va_start (ap, fmt);
405     vsnprintf (buf, BUFSIZE, fmt, ap);
406     va_end (ap);
407    
408     sts (":%s PRIVMSG %s :%s", su->uid, *target != '#' ? u->uid : target, buf);
409     }
410    
411     /* NOTICE wrapper */
412     void
413     inspircd12_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
414     {
415 pippijn 1.2 sts (":%s NOTICE %s :%s", from ? from->uid : me.numeric, target->nick, text);
416 pippijn 1.1 }
417    
418     void
419     inspircd12_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
420     {
421 pippijn 1.2 sts (":%s NOTICE %s%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, mask, text);
422 pippijn 1.1 }
423    
424     void
425     inspircd12_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
426     {
427 pippijn 1.2 sts (":%s NOTICE %s :%s", from ? from->uid : me.numeric, target->name, text);
428 pippijn 1.1 }
429    
430     void
431     inspircd12_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
432     {
433     va_list ap;
434     char buf[BUFSIZE];
435    
436     va_start (ap, fmt);
437     vsnprintf (buf, BUFSIZE, fmt, ap);
438     va_end (ap);
439    
440 pippijn 1.2 sts (":%s PUSH %s ::%s %d %s %s", me.numeric, target, from, numeric, target, buf);
441 pippijn 1.1 }
442    
443     /* KILL wrapper */
444     void
445     inspircd12_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
446     {
447     va_list ap;
448     char buf[BUFSIZE];
449    
450     va_start (ap, fmt);
451     vsnprintf (buf, BUFSIZE, fmt, ap);
452     va_end (ap);
453    
454     sts (":%s KILL %s :[%s] Killed (%s (%s))", from, nick, me.name, from, buf);
455     }
456    
457     /* PART wrapper */
458     void
459     inspircd12_handler::part_sts (channel_t *c, user_t *u)
460     {
461     sts (":%s PART %s :Leaving", u->uid, c->name);
462     }
463    
464     /* server-to-server KLINE wrapper */
465     void
466     inspircd12_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
467     {
468     if (!me.connected)
469     return;
470    
471     /* :services-dev.chatspike.net ADDLINE G test@test.com Brain 1133994664 0 :You are banned from this network */
472 pippijn 1.2 sts (":%s ADDLINE G %s@%s %s %ld %ld :%s", me.numeric, user, host, opersvs.nick, NOW, duration, reason);
473 pippijn 1.1 }
474    
475     /* server-to-server UNKLINE wrapper */
476     void
477     inspircd12_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
478     {
479     if (!me.connected)
480     return;
481    
482     /* I know this looks wrong, but it's really not. Trust me. --w00t */
483     sts (":%s GLINE %s@%s", opersvs.nick, user, host);
484     }
485    
486     /* topic wrapper */
487     void
488     inspircd12_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
489     {
490     if (!me.connected || !c)
491     return;
492    
493     /* If possible, try to use FTOPIC
494     * Note that because TOPIC does not contain topicTS, it may be
495     * off a few seconds on other servers, hence the 60 seconds here.
496     * -- jilles */
497     /* Restoring old topic */
498     if (ts > prevts + 60 || prevts == 0)
499     {
500     sts (":%s FTOPIC %s %ld %s :%s", chansvs.me->me->uid, c->name, ts, setter, topic);
501     return;
502     }
503     /* Tweaking a topic */
504     else if (ts == prevts)
505     {
506     ts += 60;
507     sts (":%s FTOPIC %s %ld %s :%s", chansvs.me->me->uid, c->name, ts, setter, topic);
508     c->topicts = ts;
509     return;
510     }
511     sts (":%s TOPIC %s :%s", chansvs.me->me->uid, c->name, topic);
512     c->topicts = NOW;
513     }
514    
515     /* mode wrapper */
516     void
517     inspircd12_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
518     {
519     user_t *su = user_find (sender);
520    
521     if (!me.connected)
522     return;
523    
524     if (has_protocol >= PROTOCOL_NEWTS)
525     /* FMODE from user is ok, use it */
526     sts (":%s FMODE %s %ld %s", su->uid, target->name, target->ts, modes);
527     else
528     sts (":%s MODE %s %s", su->uid, target->name, modes);
529     }
530    
531     /* ping wrapper */
532     void
533     inspircd12_handler::ping_sts (void)
534     {
535     if (!me.connected)
536     return;
537    
538 pippijn 1.7 sts (":%s PING %s", me.numeric, curr_uplink->name);
539 pippijn 1.1 }
540    
541     /* protocol-specific stuff to do on login */
542     void
543     inspircd12_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
544     {
545     if (!me.connected)
546     return;
547    
548 pippijn 1.2 sts (":%s METADATA %s accountname :%s", me.numeric, origin, user);
549 pippijn 1.1 }
550    
551     /* protocol-specific stuff to do on logout */
552     bool
553     inspircd12_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
554     {
555     if (!me.connected)
556     return false;
557    
558 pippijn 1.2 sts (":%s METADATA %s accountname :", me.numeric, origin);
559 pippijn 1.1 return false;
560     }
561    
562     void
563     inspircd12_handler::jupe (char const * const server, char const * const reason)
564     {
565     server_t *s;
566     if (!me.connected)
567     return;
568    
569     s = server_find (server);
570     if (s != NULL)
571     {
572     /* We need to wait for the RSQUIT to be processed -- jilles */
573     sts (":%s RSQUIT :%s", opersvs.nick, server);
574     s->flags |= SF_JUPE_PENDING;
575     }
576     else
577     {
578     /* Remove any previous jupe first */
579 pippijn 1.2 sts (":%s SQUIT %s :%s", me.numeric, server, reason);
580     sts (":%s SERVER %s * 1 :%s", me.numeric, server, reason);
581 pippijn 1.1 }
582     }
583    
584     void
585     inspircd12_handler::sethost_sts (char const * const source, char const * const target, char const * const host)
586     {
587     if (!me.connected)
588     return;
589    
590     notice (source, target, "Setting your host to \2%s\2.", host);
591     sts (":%s CHGHOST %s %s", source, target, host);
592     }
593    
594     void
595     inspircd12_handler::fnc_sts (user_t *source, user_t *u, char const * const newnick, int type)
596     {
597     /* svsnick can only be sent by a server */
598 pippijn 1.2 sts (":%s SVSNICK %s %s %lu", me.numeric, u->uid, newnick, NOW - 60);
599 pippijn 1.1 }
600    
601    
602     /* invite a user to a channel */
603     void
604     inspircd12_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
605     {
606     sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
607     }
608    
609     void
610     inspircd12_handler::holdnick_sts (user_t *source, int duration, char const * const nick, myuser_t *account)
611     {
612     if (duration == 0)
613     /* remove SVSHOLD */
614     sts (":%s SVSHOLD %s", source->nick, nick);
615     else
616     sts (":%s SVSHOLD %s %ds :Registered nickname.", source->nick, nick, duration);
617     }
618    
619     static void
620     m_topic (sourceinfo_t *si, int parc, char *parv[])
621     {
622     channel_t *c = channel_find (parv[0]);
623    
624     if (!c)
625     return;
626    
627     handle_topic_from (si, c, si->su->nick, NOW, parv[1]);
628     }
629    
630     static void
631     m_ftopic (sourceinfo_t *si, int parc, char *parv[])
632     {
633     channel_t *c = channel_find (parv[0]);
634     time_t ts = atol (parv[1]);
635    
636     if (!c)
637     return;
638    
639     if (c->topic != NULL && c->topicts >= ts)
640     {
641     slog (LG_DEBUG, "m_ftopic(): ignoring older topic on %s", c->name);
642     return;
643     }
644    
645     handle_topic_from (si, c, parv[2], ts, parv[3]);
646     }
647    
648     static void
649     m_ping (sourceinfo_t *si, int parc, char *parv[])
650     {
651     /* reply to PING's */
652 pippijn 1.2 sts (":%s PONG %s", me.numeric, parv[0]);
653 pippijn 1.1 }
654    
655     static void
656     m_pong (sourceinfo_t *si, int parc, char *parv[])
657     {
658     handle_eob (si->s);
659    
660     if (irccasecmp (me.actual, si->s->name))
661     return;
662     me.uplinkpong = NOW;
663    
664     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
665     if (me.bursting)
666     {
667     #ifdef HAVE_GETTIMEOFDAY
668     e_time (burstime, &burstime);
669    
670     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
671    
672     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
673     #else
674     slog (LG_INFO, "m_pong(): finished synching with uplink");
675     wallops ("Finished synching to network.");
676     #endif
677    
678     me.bursting = false;
679     }
680     }
681    
682     static void
683     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
684     {
685     if (parc != 2)
686     return;
687    
688     handle_message (si, parv[0], false, parv[1]);
689     }
690    
691     static void
692     m_notice (sourceinfo_t *si, int parc, char *parv[])
693     {
694     if (parc != 2)
695     return;
696    
697     handle_message (si, parv[0], true, parv[1]);
698     }
699    
700     static void
701     m_fjoin (sourceinfo_t *si, int parc, char *parv[])
702     {
703     /* FJOIN #flaps 1234 :@,fanny +%,arse ,tits ,breasts &~,poontang */
704     channel_t *c;
705     unsigned int userc;
706     unsigned int i;
707     unsigned int j;
708     unsigned int nlen;
709     bool prefix = true;
710     bool keep_new_modes = true;
711     char *userv[256];
712     char prefixandnick[51];
713     time_t ts;
714    
715     c = channel_find (parv[0]);
716     ts = atol (parv[1]);
717    
718     if (!c)
719     {
720     slog (LG_DEBUG, "m_fjoin(): new channel: %s", parv[0]);
721     c = channel_add (parv[0], ts, si->s);
722     return_if_fail (c != NULL);
723     /* Tell the core to check mode locks now,
724     * otherwise it may only happen after the next
725     * mode change.
726     * Inspircd does not allow any redundant modes
727     * so this will not look ugly. -- jilles */
728     /* As discussed with Brain, if this is in a burst,
729     * an FMODE with the simple modes will follow so we
730     * can skip this. -- jilles */
731     if (!me.bursting)
732     channel_mode (NULL, c, "+");
733     }
734    
735     if (ts < c->ts)
736     {
737     chanuser_t *cu;
738     node_t *n;
739    
740     /* the TS changed. a TS change requires us to do
741     * bugger all except update the TS, because in InspIRCd
742     * remote servers enforce the TS change - Brain
743     *
744     * This is no longer the case with 1.1, we need to bounce their modes
745     * as well as lowering the channel ts. Do both. -- w00t
746     */
747    
748     if (has_protocol >= PROTOCOL_NEWTS)
749     {
750     clear_simple_modes (c);
751     chanban_clear (c);
752     }
753     LIST_FOREACH (n, c->members.head)
754     {
755     cu = (chanuser_t *) n->data;
756     if (cu->user->server == me.me)
757     {
758     /* it's a service, reop */
759 pippijn 1.2 sts (":%s FMODE %s %ld +o %s", me.numeric, c->name, ts, cu->user->nick);
760 pippijn 1.1 cu->modes = CMODE_OP;
761     }
762     else
763     cu->modes = 0;
764     }
765    
766     c->ts = ts;
767     c->callback.tschange (c);
768     }
769     else if (ts > c->ts)
770     {
771     keep_new_modes = false; /* ignore statuses */
772     }
773    
774     /*
775     * ok, here's the difference from 1.0 -> 1.1:
776     * 1.0 sent p[3] and up as individual users, prefixed with their 'highest' prefix, @, % or +
777     * in 1.1, this is more complex. All prefixes are sent, with the additional caveat that modules
778     * can add their own prefixes (dangerous!) - therefore, don't just chanuser_add(), split the prefix
779     * out and ignore unknown prefixes (probably the safest option). --w00t
780     */
781     userc = sjtoken (parv[parc - 1], ' ', userv);
782    
783     /* loop over all the users in this fjoin */
784     for (i = 0; i < userc; i++)
785     {
786     nlen = 0;
787     prefix = true;
788    
789     slog (LG_DEBUG, "m_fjoin(): processing user: %s", userv[i]);
790    
791     /*
792     * ok, now look at the chars in the nick.. we have something like "@%,w00t", but need @%w00t.. and
793     * we also want to ignore unknown prefixes.. loop through the chars
794     */
795     for (; *userv[i]; userv[i]++)
796     {
797     /* does this char match a known prefix? */
798     for (j = 0; prefix_mode_list[j].mode; j++)
799     {
800     /* yup. add it to the 'final' combination (@%w00t) */
801     if (*userv[i] == prefix_mode_list[j].mode)
802     {
803     prefixandnick[nlen++] = *userv[i];
804     continue;
805     }
806     }
807    
808     /* it's not a known prefix char, have we reached the end of the prefixes? */
809     if (*userv[i] == ',')
810     {
811     /* yup, skip over the comma */
812     userv[i]++;
813    
814     /* if we're ignoring status (keep_new_modes is false) then just add them to chan here.. */
815     if (keep_new_modes == false)
816     {
817     /* This ignores the @%, and just adds 'w00t' to the chan */
818     chanuser_add (c, userv[i]);
819     }
820     else
821     {
822     /* else, we do care about their prefixes.. add '@%w00t' to the chan */
823     strlcpy (prefixandnick + nlen, userv[i], sizeof (prefixandnick) - nlen);
824     chanuser_add (c, prefixandnick);
825     }
826    
827     /* added them.. break out of this loop, which will move us to the next user */
828     break;
829     }
830     else
831     {
832     /* unknown prefix char */
833     }
834     }
835    
836     /* go to the next user */
837     }
838    
839     if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
840     channel_delete (c);
841     }
842    
843     static void
844     m_part (sourceinfo_t *si, int parc, char *parv[])
845     {
846     int chanc;
847     char *chanv[256];
848     int i;
849    
850     chanc = sjtoken (parv[0], ',', chanv);
851     for (i = 0; i < chanc; i++)
852     {
853     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
854    
855     chanuser_delete (channel_find (chanv[i]), si->su);
856     }
857     }
858    
859     static void
860     m_uid (sourceinfo_t *si, int parc, char *parv[])
861     {
862     user_t *u;
863    
864 pippijn 1.2 /* :3ZZ UID 3ZZAAAAAB 1133994664 nenolod petrie.ipv6.nenolod.net petrie.ipv6.nenolod.net nenolod +i 0.0.0.0 1351531513 :William Pitcock */
865 pippijn 1.3 if (parc == 10)
866 pippijn 1.1 {
867     slog (LG_DEBUG, "m_uid(): new user on `%s': %s", si->s->name, parv[2]);
868    
869 pippijn 1.2 /* char *nick, char *user, char *host, char *vhost, char *ip, char *uid, char *gecos, server_t *server, unsigned int ts */
870     u = user_add (parv[2], parv[5], parv[3], parv[4], parv[7], parv[0], parv[9], si->s, atol (parv[9]));
871 pippijn 1.1 user_mode (u, parv[5]);
872    
873     /* If server is not yet EOB we will do this later.
874     * This avoids useless "please identify" -- jilles */
875     if (si->s->flags & SF_EOB)
876     handle_nickchange (u);
877     }
878     else
879     {
880     int i;
881     slog (LG_DEBUG, "m_uid(): got UID with wrong number of params");
882    
883     for (i = 0; i < parc; i++)
884     slog (LG_DEBUG, "m_uid(): parv[%d] = %s", i, parv[i]);
885     }
886     }
887    
888     static void
889     m_nick (sourceinfo_t *si, int parc, char *parv[])
890     {
891     /* if it's only 1 then it's a nickname change, if it's 2, it's a nickname change with a TS */
892     if (parc == 1 || parc == 2)
893     {
894     if (!si->su)
895     {
896     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
897     return;
898     }
899    
900     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
901    
902     /* No TS here for some reason, hmm */
903     user_changenick (si->su, parv[0], si->su->ts);
904    
905     /* It could happen that our PING arrived late and the
906     * server didn't acknowledge EOB yet even though it is
907     * EOB; don't send double notices in that case -- jilles */
908     if (si->su->server->flags & SF_EOB)
909     handle_nickchange (si->su);
910     }
911     else
912     {
913     int i;
914     slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
915    
916     for (i = 0; i < parc; i++)
917     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
918     }
919     }
920    
921     static void
922     m_quit (sourceinfo_t *si, int parc, char *parv[])
923     {
924     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
925    
926     /* user_delete() takes care of removing channels and so forth */
927     user_delete (si->su);
928     }
929    
930     static void
931     m_saquit (sourceinfo_t *si, int parc, char *parv[])
932     {
933     user_t *u = user_find (parv[0]);
934    
935     slog (LG_DEBUG, "m_saquit(): user leaving: %s", parv[0]);
936    
937     /* user_delete() takes care of removing channels and so forth */
938     user_delete (u);
939     }
940    
941     static void
942     m_mode (sourceinfo_t *si, int parc, char *parv[])
943     {
944     if (*parv[0] == '#')
945     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
946     else
947     user_mode (user_find (parv[0]), parv[1]);
948     }
949    
950     static void
951     m_fmode (sourceinfo_t *si, int parc, char *parv[])
952     {
953     channel_t *c;
954     bool onlydeop;
955     time_t ts;
956     char const *p;
957    
958     /* :server.moo FMODE #blarp tshere +ntsklLg keymoo 1337 secks */
959     if (*parv[0] == '#')
960     {
961     c = channel_find (parv[0]);
962     if (c == NULL)
963     {
964     slog (LG_DEBUG, "m_fmode(): nonexistant channel: %s", parv[0]);
965     return;
966     }
967     ts = atoi (parv[1]);
968     if (ts == c->ts && has_protocol < PROTOCOL_NEWTS)
969     {
970     onlydeop = true;
971     p = parv[2];
972     while (*p != '\0')
973     {
974     if (!strchr ("-qaohv", *p))
975     onlydeop = false;
976     p++;
977     }
978     if (onlydeop && si->s != NULL)
979     {
980     /* ignore redundant deops generated
981     * if we lower the TS of a channel
982     * scenario: user with autoop privs recreates
983     * channel
984     * XXX could this ignore other stuff too?
985     * -- jilles */
986     slog (LG_DEBUG, "m_fmode(): ignoring %s %s: incoming TS %ld is equal to our TS %ld, and only deops", parv[0], parv[2], ts, c->ts);
987     return;
988     }
989     }
990     else if (ts > c->ts)
991     {
992     if (has_protocol < PROTOCOL_NEWTS)
993     slog (LG_DEBUG, "m_fmode(): accepting but should bounce %s %s: incoming TS %ld is newer than our TS %ld", parv[0], parv[2], ts, c->ts);
994     else
995     return;
996     }
997     else if (ts < c->ts)
998     slog (LG_DEBUG, "m_fmode(): %s %s: incoming TS %ld is older than our TS %ld, possible desync", parv[0], parv[2], ts, c->ts);
999     channel_mode (NULL, c, parc - 2, &parv[2]);
1000     }
1001     else
1002     user_mode (user_find (parv[0]), parv[2]);
1003     }
1004    
1005     static void
1006     m_kick (sourceinfo_t *si, int parc, char *parv[])
1007     {
1008     user_t *u = user_find (parv[1]);
1009     channel_t *c = channel_find (parv[0]);
1010    
1011     /* -> :rakaur KICK #shrike rintaun :test */
1012     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
1013    
1014     if (!u)
1015     {
1016     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
1017     return;
1018     }
1019    
1020     if (!c)
1021     {
1022     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
1023     return;
1024     }
1025    
1026     if (!chanuser_find (c, u))
1027     {
1028     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->uid, c->name);
1029     return;
1030     }
1031    
1032     chanuser_delete (c, u);
1033    
1034     /* if they kicked us, let's rejoin */
1035     if (is_internal_client (u))
1036     {
1037     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
1038     join (parv[0], u->uid);
1039     }
1040     }
1041    
1042     static void
1043     m_kill (sourceinfo_t *si, int parc, char *parv[])
1044     {
1045     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
1046     }
1047    
1048     static void
1049     m_squit (sourceinfo_t *si, int parc, char *parv[])
1050     {
1051     slog (LG_DEBUG, "m_squit(): server leaving: %s", parv[0]);
1052     server_delete (parv[0]);
1053     }
1054    
1055     static void
1056     m_server (sourceinfo_t *si, int parc, char *parv[])
1057     {
1058     server_t *s;
1059    
1060     slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
1061 pippijn 1.2 s = handle_server (si, parv[0], parv[3], atoi(parv[2]), parv[4]);
1062 pippijn 1.1
1063     if (s != NULL && s->uplink != me.me)
1064 pippijn 1.2 /* elicit PONG for EOB detection; pinging uplink is
1065     * already done elsewhere -- jilles
1066     */
1067     sts (":%s PING %s %s", me.numeric, me.numeric, s->name);
1068 pippijn 1.1 }
1069    
1070     static void
1071     m_stats (sourceinfo_t *si, int parc, char *parv[])
1072     {
1073     handle_stats (si->su, parv[0][0]);
1074     }
1075    
1076     static void
1077     m_motd (sourceinfo_t *si, int parc, char *parv[])
1078     {
1079     handle_motd (si->su);
1080     }
1081    
1082     static void
1083     m_admin (sourceinfo_t *si, int parc, char *parv[])
1084     {
1085     handle_admin (si->su);
1086     }
1087    
1088     static void
1089     m_away (sourceinfo_t *si, int parc, char *parv[])
1090     {
1091     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
1092     }
1093    
1094     static void
1095     m_join (sourceinfo_t *si, int parc, char *parv[])
1096     {
1097     channel_t *c;
1098    
1099     c = channel_find (parv[0]);
1100     if (!c)
1101     {
1102     slog (LG_DEBUG, "m_join(): new channel: %s (modes lost)", parv[0]);
1103     c = channel_add (parv[0], parc > 1 ? atol (parv[1]) : NOW, si->su->server);
1104     return_if_fail (c != NULL);
1105     channel_mode (NULL, c, "+");
1106     }
1107     chanuser_add (c, si->su->nick);
1108     }
1109    
1110     static void
1111     m_sajoin (sourceinfo_t *si, int parc, char *parv[])
1112     {
1113     si->su = user_find (parv[0]);
1114     if (si->su == NULL)
1115     return;
1116     m_join (si, 1, &parv[1]);
1117     }
1118    
1119     static void
1120     m_sapart (sourceinfo_t *si, int parc, char *parv[])
1121     {
1122     si->su = user_find (parv[0]);
1123     if (si->su == NULL)
1124     return;
1125     m_part (si, 1, &parv[1]);
1126     }
1127    
1128     static void
1129     m_sanick (sourceinfo_t *si, int parc, char *parv[])
1130     {
1131     si->su = user_find (parv[0]);
1132     if (si->su == NULL)
1133     return;
1134     m_nick (si, 1, &parv[1]);
1135     }
1136    
1137     static void
1138     m_samode (sourceinfo_t *si, int parc, char *parv[])
1139     {
1140     /* note that SAMODE is not checked in any way before propagation,
1141     * and only works on channels, not users */
1142     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
1143     }
1144    
1145     static void
1146     m_error (sourceinfo_t *si, int parc, char *parv[])
1147     {
1148     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
1149     }
1150    
1151     static void
1152     m_idle (sourceinfo_t *si, int parc, char *parv[])
1153     {
1154     if (parc == 1 && si->su != NULL)
1155     {
1156     sts (":%s IDLE %s %ld 0", parv[0], si->su->nick, NOW);
1157     }
1158     else
1159     {
1160     slog (LG_INFO, "m_idle(): Received an IDLE response but we didn't WHOIS anybody!");
1161     }
1162     }
1163    
1164     static void
1165     m_opertype (sourceinfo_t *si, int parc, char *parv[])
1166     {
1167     /*
1168     * Hope this works.. InspIRCd OPERTYPE means user is an oper, mark them so
1169     * Later, we may want to look at saving their OPERTYPE for informational
1170     * purposes, or not. --w00t
1171     */
1172     user_mode (si->su, "+o");
1173     }
1174    
1175     static void
1176     m_fhost (sourceinfo_t *si, int parc, char *parv[])
1177     {
1178     strlcpy (si->su->vhost, parv[0], HOSTLEN);
1179     }
1180    
1181     /*
1182     * :<source server> METADATA <channel|user> <key> :<value>
1183     * The sole piece of metadata we're interested in is 'accountname', set by Services,
1184     * and kept by ircd.
1185     *
1186     * :services.barafranca METADATA w00t accountname :w00t
1187     */
1188    
1189     static void
1190     m_metadata (sourceinfo_t *si, int parc, char *parv[])
1191     {
1192     user_t *u;
1193    
1194     if (!irccasecmp (parv[1], "accountname"))
1195     {
1196     /* find user */
1197     u = user_find (parv[0]);
1198    
1199     if (u == NULL)
1200     return;
1201    
1202     handle_burstlogin (u, parv[2]);
1203     }
1204     }
1205    
1206     /*
1207     * rsquit:
1208     * remote/request squit
1209     * when squitting a remote server, inspircd sends RSQUIT along the tree until it reaches the server that has
1210     * the server to be squit as a local connection, which should then close it's connection and send SQUIT back
1211     * to the rest of the network.
1212     */
1213     static void
1214     m_rsquit (sourceinfo_t *si, int parc, char *parv[])
1215     {
1216 pippijn 1.2 sts (":%s SQUIT %s :Jupe removed by %s", me.numeric, parv[0], si->su->nick);
1217 pippijn 1.1 }
1218    
1219     static void
1220     m_capab (sourceinfo_t *si, int parc, char *parv[])
1221     {
1222     int i, varc;
1223     char *varv[256];
1224    
1225     if (strcasecmp (parv[0], "START") == 0)
1226     {
1227     /* reset all our previously recieved CAPAB stuff */
1228     has_servicesmod = false;
1229     has_globopsmod = false;
1230     has_svshold = false;
1231     has_protocol = 0;
1232     }
1233     else if (strcasecmp (parv[0], "CAPABILITIES") == 0 && parc > 1)
1234     {
1235     varc = sjtoken (parv[1], ' ', varv);
1236     for (i = 0; i < varc; i++)
1237     {
1238     if (!strncmp (varv[i], "PROTOCOL=", 9))
1239     has_protocol = atoi (varv[i] + 9);
1240     /* XXX check/store HALFOP/CHANMAX/IDENTMAX */
1241     }
1242     }
1243     else if (strcasecmp (parv[0], "MODULES") == 0 && parc > 1)
1244     {
1245     if (strstr (parv[1], "m_services_account.so"))
1246 pippijn 1.5 has_servicesmod = true;
1247 pippijn 1.1 if (strstr (parv[1], "m_globops.so"))
1248 pippijn 1.5 has_globopsmod = true;
1249 pippijn 1.1 if (strstr (parv[1], "m_svshold.so"))
1250 pippijn 1.5 has_svshold = true;
1251     if (strstr (parv[1], "m_chanprotect.so"))
1252 pippijn 1.1 {
1253 pippijn 1.5 ircd->uses_owner = true;
1254     ircd->uses_protect = true;
1255 pippijn 1.1 }
1256     }
1257     else if (strcasecmp (parv[0], "END") == 0)
1258     {
1259     if (has_globopsmod == false && has_protocol < PROTOCOL_SNONOTICE)
1260     {
1261     slog (LG_ERROR, "m_capab(): you didn't load m_globops into inspircd. " PACKAGE_NAME " support requires this module. exiting.");
1262     exit (EXIT_FAILURE);
1263     }
1264    
1265     if (has_servicesmod == false)
1266     {
1267     slog (LG_ERROR, "m_capab(): you didn't load m_services_account into inspircd. " PACKAGE_NAME " support requires this module. exiting.");
1268     exit (EXIT_FAILURE);
1269     }
1270    
1271     if (has_svshold == false)
1272     {
1273     slog (LG_INFO, "m_capab(): you didn't load m_svshold into inspircd. nickname enforcers will not work.");
1274     }
1275     }
1276     else
1277     {
1278     slog (LG_DEBUG, "m_capab(): unknown CAPAB type %s - out of date protocol module?", parv[0]);
1279     }
1280     }
1281    
1282     /* Server ended their burst: warn all their users if necessary -- jilles */
1283     static void
1284     server_eob (server_t *s)
1285     {
1286     node_t *n;
1287    
1288     LIST_FOREACH (n, s->userlist.head)
1289     {
1290     handle_nickchange ((user_t *) n->data);
1291     }
1292     }
1293    
1294     static pcommand_t const pcommands[] = {
1295     { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
1296     { "PONG", m_pong, 1, MSRC_SERVER },
1297     { "PRIVMSG", m_privmsg, 2, MSRC_USER },
1298     { "NOTICE", m_notice, 2, MSRC_USER | MSRC_SERVER },
1299     { "FJOIN", m_fjoin, 3, MSRC_SERVER },
1300     { "PART", m_part, 1, MSRC_USER },
1301     { "NICK", m_nick, 1, MSRC_USER | MSRC_SERVER },
1302     { "UID", m_uid, 9, MSRC_USER | MSRC_SERVER },
1303     { "QUIT", m_quit, 1, MSRC_USER },
1304     { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
1305     { "FMODE", m_fmode, 3, MSRC_USER | MSRC_SERVER },
1306     { "SAMODE", m_samode, 2, MSRC_USER },
1307     { "SAJOIN", m_sajoin, 2, MSRC_USER },
1308     { "SAPART", m_sapart, 2, MSRC_USER },
1309     { "SANICK", m_sanick, 2, MSRC_USER },
1310     { "SAQUIT", m_saquit, 1, MSRC_USER },
1311     { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
1312     { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
1313     { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
1314     { "RSQUIT", m_rsquit, 1, MSRC_USER },
1315     { "SERVER", m_server, 4, MSRC_UNREG | MSRC_SERVER },
1316     { "STATS", m_stats, 2, MSRC_USER },
1317     { "MOTD", m_motd, 1, MSRC_USER },
1318     { "ADMIN", m_admin, 1, MSRC_USER },
1319     { "FTOPIC", m_ftopic, 4, MSRC_SERVER },
1320     { "JOIN", m_join, 1, MSRC_USER },
1321     { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
1322     { "TOPIC", m_topic, 2, MSRC_USER },
1323     { "FHOST", m_fhost, 1, MSRC_USER },
1324     { "IDLE", m_idle, 1, MSRC_USER },
1325     { "AWAY", m_away, 0, MSRC_USER },
1326     { "OPERTYPE", m_opertype, 1, MSRC_USER },
1327     { "METADATA", m_metadata, 3, MSRC_SERVER },
1328     { "CAPAB", m_capab, 1, MSRC_UNREG | MSRC_SERVER },
1329     };
1330    
1331     inspircd12_handler::inspircd12_handler ()
1332     {
1333     mode_list = inspircd12_mode_list;
1334     ignore_mode_list = inspircd12_ignore_mode_list;
1335     status_mode_list = inspircd12_status_mode_list;
1336     prefix_mode_list = inspircd12_prefix_mode_list;
1337    
1338     ircd = &InspIRCd;
1339    
1340     pcommand_add (&pcommands);
1341    
1342     server_t::callback.eob.attach (server_eob);
1343     }
1344    
1345     inspircd12_handler::~inspircd12_handler ()
1346     {
1347     server_t::callback.eob.detach (server_eob);
1348    
1349     pcommand_delete (&pcommands);
1350    
1351     ircd = NULL;
1352    
1353     prefix_mode_list = NULL;
1354     status_mode_list = NULL;
1355     ignore_mode_list = NULL;
1356     mode_list = NULL;
1357     }
1358     } // namespace protocol
1359    
1360     #define FACREG_TYPE protocol::inspircd12_handler
1361     #define FACREG_TYPE_NAME "inspircd12"
1362     #define FACREG_INTERFACE_TYPE protocol::handler
1363     #include <ermyth/factory_reg.h>