ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/protocol/sorcery.C
Revision: 1.6
Committed: Sun Sep 9 20:05:52 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.5: +3 -2 lines
Log Message:
- changed configurations to the c++ stdlib
- more #defines to enum
- removed getopt.h and link.h from the system as they were unused
- reworked logstreams
- added an itoa with old syntax
- made klines objects
- moved some global variables into appropriate classes
- fixed boost.foreach's compiler workaround #if's
- allow other files to add exceptions with ADD_EXCEPTION
- changed mynick_t to c++ object
- moved servers.h out of atheme.h
- corrected PING from inspircd 1.2

File Contents

# User Rev Content
1 pippijn 1.1 /*
2 pippijn 1.4 * Copyright © 2003-2004 E. Will et al.
3     * Copyright © 2005-2006 Atheme Development Group
4 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
5 pippijn 1.1 *
6     * This file contains protocol support for bahamut-based ircd.
7     *
8 pippijn 1.6 * $Id: sorcery.C,v 1.5 2007-09-05 11:23:15 pippijn Exp $
9 pippijn 1.1 */
10    
11     #include "atheme.h"
12 pippijn 1.6 #include "servers.h"
13 pippijn 1.1 #include "uplink.h"
14     #include "pmodule.h"
15     #include "protocol/sorcery.h"
16    
17 pippijn 1.6 static char const rcsid[] = "$Id: sorcery.C,v 1.5 2007-09-05 11:23:15 pippijn Exp $";
18 pippijn 1.1
19     /* *INDENT-OFF* */
20    
21 pippijn 1.4 namespace protocol
22 pippijn 1.1 {
23 pippijn 1.4 static ircd_t Sorcery = {
24     "SorceryNet IRCd 1.3.1+", /* IRCd name */
25     "$", /* TLD Prefix, used by Global. */
26     false, /* Whether or not we use IRCNet/TS6 UID */
27     false, /* Whether or not we use RCOMMAND */
28     false, /* Whether or not we support channel owners. */
29     false, /* Whether or not we support channel protection. */
30     false, /* Whether or not we support halfops. */
31     false, /* Whether or not we use P10 */
32     false, /* Whether or not we use vHosts. */
33     CMODE_OPERONLY, /* Oper-only cmodes */
34     0, /* Integer flag for owner channel flag. */
35     0, /* Integer flag for protect channel flag. */
36     0, /* Integer flag for halfops. */
37     "+", /* Mode we set for owner. */
38     "+", /* Mode we set for protect. */
39     "+", /* Mode we set for halfops. */
40     PROTOCOL_SORCERY, /* Protocol type */
41     0, /* Permanent cmodes */
42     "b", /* Ban-like cmodes */
43     0, /* Except mchar */
44     0, /* Invex mchar */
45     0 /* Flags */
46     };
47    
48     struct sorcery_handler : handler
49     {
50     sorcery_handler ();
51     virtual ~sorcery_handler ();
52    
53     virtual unsigned int server_login (void);
54     virtual void introduce_nick (user_t *u);
55     virtual void invite_sts (user_t *source, user_t *target, channel_t *channel);
56     virtual void quit_sts (user_t *u, char const * const reason);
57     virtual void wallops_sts (char const * const text);
58     virtual void join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes);
59     virtual void kick (char const * const from, char const * const channel, char const * const to, char const * const reason);
60     virtual void privmsg (char const * const from, char const * const target, char const * const fmt, ...);
61     virtual void notice_user_sts (user_t *from, user_t *target, char const * const text);
62     virtual void notice_global_sts (user_t *from, char const * const mask, char const * const text);
63     virtual void notice_channel_sts (user_t *from, channel_t *target, char const * const text);
64     virtual void numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...);
65     virtual void skill (char const * const from, char const * const nick, char const * const fmt, ...);
66     virtual void part_sts (channel_t *c, user_t *u);
67     virtual void kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason);
68     virtual void unkline_sts (char const * const server, char const * const user, char const * const host);
69     virtual void topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic);
70     virtual void mode_sts (char const * const sender, channel_t *target, char const * const modes);
71     virtual void ping_sts (void);
72     virtual void ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost);
73     virtual bool ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost);
74     virtual void jupe (char const * const server, char const * const reason);
75     };
76    
77     static cmode_t sorcery_mode_list[] = {
78     { 'i', CMODE_INVITE },
79     { 'm', CMODE_MOD },
80     { 'n', CMODE_NOEXT },
81     { 'p', CMODE_PRIV },
82     { 's', CMODE_SEC },
83     { 't', CMODE_TOPIC },
84     { 'c', CMODE_NOCOLOR },
85     { 'R', CMODE_REGONLY },
86     { 'O', CMODE_OPERONLY },
87     { '\0', 0 }
88     };
89    
90 pippijn 1.5 static extmode_t sorcery_ignore_mode_list[] = {
91 pippijn 1.4 { '\0', 0 }
92     };
93    
94     static cmode_t sorcery_status_mode_list[] = {
95     { 'o', CMODE_OP },
96     { 'v', CMODE_VOICE },
97     { '\0', 0 }
98     };
99    
100     static cmode_t sorcery_prefix_mode_list[] = {
101     { '@', CMODE_OP },
102     { '+', CMODE_VOICE },
103     { '\0', 0 }
104     };
105    
106     /* *INDENT-ON* */
107    
108     /* login to our uplink */
109     unsigned int
110     sorcery_handler::server_login (void)
111     {
112     int ret;
113    
114     ret = sts ("PASS %s", curr_uplink->pass);
115     if (ret == 1)
116     return 1;
117    
118     me.bursting = true;
119    
120     sts ("PROTOCTL NOQUIT WATCH=128 SAFELIST");
121     sts ("SERVER %s 1 :%s", me.name, me.desc);
122    
123     services_init ();
124    
125     return 0;
126     }
127    
128     /* introduce a client */
129     void
130     sorcery_handler::introduce_nick (user_t *u)
131     {
132     sts ("NICK %s 1 %ld %s %s %s :%s", u->nick, u->ts, u->user, u->host, me.name, u->gecos);
133     sts (":%s MODE %s +%s", u->nick, u->nick, "io");
134     }
135    
136     /* invite a user to a channel */
137     void
138     sorcery_handler::invite_sts (user_t *sender, user_t *target, channel_t *channel)
139     {
140     sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
141     }
142    
143     void
144     sorcery_handler::quit_sts (user_t *u, char const * const reason)
145     {
146     if (!me.connected)
147     return;
148    
149     sts (":%s QUIT :%s", u->nick, reason);
150     }
151    
152     /* WALLOPS wrapper */
153     void
154     sorcery_handler::wallops_sts (char const * const text)
155     {
156     sts (":%s GLOBOPS :%s", me.name, text);
157     }
158    
159     /* join a channel */
160     void
161     sorcery_handler::join_sts (channel_t *c, user_t *u, bool isnew, char const * const modes)
162     {
163     if (isnew)
164     {
165     sts (":%s JOIN %s", u->nick, c->name);
166     if (modes[0] && modes[1])
167     sts (":%s MODE %s %s", u->nick, c->name, modes);
168     }
169     else
170     {
171     sts (":%s JOIN %s", u->nick, c->name);
172     }
173     sts (":%s MODE %s +o %s %ld", u->nick, c->name, u->nick, c->ts);
174     }
175    
176     /* kicks a user from a channel */
177     void
178     sorcery_handler::kick (char const * const from, char const * const channel, char const * const to, char const * const reason)
179     {
180     channel_t *chan = channel_find (channel);
181     user_t *user = user_find (to);
182    
183     if (!chan || !user)
184     return;
185    
186     sts (":%s KICK %s %s :%s", from, channel, to, reason);
187    
188     chanuser_delete (chan, user);
189     }
190    
191     /* PRIVMSG wrapper */
192     void
193     sorcery_handler::privmsg (char const * const from, char const * const target, char const * const fmt, ...)
194     {
195     va_list ap;
196     char buf[BUFSIZE];
197    
198     va_start (ap, fmt);
199     vsnprintf (buf, BUFSIZE, fmt, ap);
200     va_end (ap);
201    
202     sts (":%s PRIVMSG %s :%s", from, target, buf);
203     }
204    
205     /* NOTICE wrapper */
206     void
207     sorcery_handler::notice_user_sts (user_t *from, user_t *target, char const * const text)
208     {
209     sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
210     }
211    
212     void
213     sorcery_handler::notice_global_sts (user_t *from, char const * const mask, char const * const text)
214     {
215     node_t *n;
216     tld_t *tld;
217    
218     if (!strcmp (mask, "*"))
219     {
220     LIST_FOREACH (n, tldlist.head)
221     {
222     tld = static_cast<tld_t *> (n->data);
223     sts (":%s NOTICE %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
224     }
225     }
226     else
227     sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
228     }
229    
230     void
231     sorcery_handler::notice_channel_sts (user_t *from, channel_t *target, char const * const text)
232     {
233     sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
234     }
235    
236     void
237     sorcery_handler::numeric_sts (char const * const from, int numeric, char const * const target, char const * const fmt, ...)
238     {
239     va_list ap;
240     char buf[BUFSIZE];
241    
242     va_start (ap, fmt);
243     vsnprintf (buf, BUFSIZE, fmt, ap);
244     va_end (ap);
245    
246     sts (":%s %d %s %s", from, numeric, target, buf);
247     }
248    
249     /* KILL wrapper */
250     void
251     sorcery_handler::skill (char const * const from, char const * const nick, char const * const fmt, ...)
252     {
253     va_list ap;
254     char buf[BUFSIZE];
255    
256     va_start (ap, fmt);
257     vsnprintf (buf, BUFSIZE, fmt, ap);
258     va_end (ap);
259    
260     sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
261     }
262    
263     /* PART wrapper */
264     void
265     sorcery_handler::part_sts (channel_t *c, user_t *u)
266     {
267     sts (":%s PART %s", u->nick, c->name);
268     }
269    
270     /* server-to-server KLINE wrapper */
271     void
272     sorcery_handler::kline_sts (char const * const server, char const * const user, char const * const host, long duration, char const * const reason)
273     {
274     if (!me.connected)
275     return;
276    
277     sts (":%s AKILL %s %s %ld %s %ld :%s", me.name, host, user, duration, opersvs.nick, NOW, reason);
278     }
279    
280     /* server-to-server UNKLINE wrapper */
281     void
282     sorcery_handler::unkline_sts (char const * const server, char const * const user, char const * const host)
283     {
284     if (!me.connected)
285     return;
286    
287     sts (":%s RAKILL %s %s", me.name, host, user);
288     }
289    
290     /* topic wrapper */
291     void
292     sorcery_handler::topic_sts (channel_t *c, char const * const setter, time_t ts, time_t prevts, char const * const topic)
293     {
294     if (!me.connected)
295     return;
296    
297     if (ts < prevts || prevts == 0)
298     sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, setter, ts, topic);
299     else if (prevts > 1)
300     {
301     ts = prevts - 1;
302     sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, "topictime.wrong", ts, topic);
303     c->topicts = ts;
304     }
305     else
306     {
307     notice (chansvs.nick, c->name, "Unable to change topic to: %s", topic);
308     c->topicts = 1;
309     }
310     }
311    
312     /* mode wrapper */
313     void
314     sorcery_handler::mode_sts (char const * const sender, channel_t *target, char const * const modes)
315     {
316     if (!me.connected)
317     return;
318    
319     sts (":%s MODE %s %s", sender, target->name, modes);
320     }
321    
322     /* ping wrapper */
323     void
324     sorcery_handler::ping_sts (void)
325     {
326     if (!me.connected)
327     return;
328    
329     sts ("PING :%s", me.name);
330     }
331    
332     /* protocol-specific stuff to do on login */
333     void
334     sorcery_handler::ircd_on_login (char const * const origin, char const * const user, char const * const wantedhost)
335     {
336     if (!me.connected)
337     return;
338    
339     /* nothing to do here */
340     }
341    
342     /* protocol-specific stuff to do on login */
343     bool
344     sorcery_handler::ircd_on_logout (char const * const origin, char const * const user, char const * const wantedhost)
345     {
346     if (!me.connected)
347     return false;
348    
349     /* nothing to do here */
350 pippijn 1.1 return false;
351 pippijn 1.4 }
352    
353     void
354     sorcery_handler::jupe (char const * const server, char const * const reason)
355     {
356     if (!me.connected)
357     return;
358    
359     server_delete (server);
360     sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
361     sts (":%s SERVER %s 2 :%s", me.name, server, reason);
362     }
363    
364     static void
365     m_topic (sourceinfo_t *si, int parc, char *parv[])
366     {
367     channel_t *c = channel_find (parv[0]);
368    
369     if (!c)
370     return;
371    
372     handle_topic_from (si, c, parv[1], atol (parv[2]), parv[3]);
373     }
374    
375     static void
376     m_ping (sourceinfo_t *si, int parc, char *parv[])
377     {
378     /* reply to PING's */
379     sts (":%s PONG %s %s", me.name, me.name, parv[0]);
380     }
381    
382     static void
383     m_pong (sourceinfo_t *si, int parc, char *parv[])
384     {
385     server_t *s;
386    
387     /* someone replied to our PING */
388     if (!parv[0])
389     return;
390     s = server_find (parv[0]);
391     if (s == NULL)
392     return;
393     handle_eob (s);
394    
395     if (irccasecmp (me.actual, parv[0]))
396 pippijn 1.1 return;
397 pippijn 1.4
398     me.uplinkpong = NOW;
399    
400     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
401     if (me.bursting)
402     {
403     #ifdef HAVE_GETTIMEOFDAY
404     e_time (burstime, &burstime);
405    
406     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
407    
408     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
409     #else
410     slog (LG_INFO, "m_pong(): finished synching with uplink");
411     wallops ("Finished synching to network.");
412     #endif
413    
414     me.bursting = false;
415     }
416     }
417    
418     static void
419     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
420     {
421     if (parc != 2)
422 pippijn 1.1 return;
423 pippijn 1.4
424     handle_message (si, parv[0], false, parv[1]);
425     }
426    
427     static void
428     m_notice (sourceinfo_t *si, int parc, char *parv[])
429     {
430     if (parc != 2)
431 pippijn 1.1 return;
432 pippijn 1.4
433     handle_message (si, parv[0], true, parv[1]);
434     }
435    
436     static void
437     m_part (sourceinfo_t *si, int parc, char *parv[])
438     {
439     int chanc;
440     char *chanv[256];
441     int i;
442    
443     chanc = sjtoken (parv[0], ',', chanv);
444     for (i = 0; i < chanc; i++)
445     {
446     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
447    
448     chanuser_delete (channel_find (chanv[i]), si->su);
449     }
450     }
451    
452     static void
453     m_nick (sourceinfo_t *si, int parc, char *parv[])
454     {
455     user_t *u;
456     server_t *s;
457    
458     if (parc == 7)
459     {
460     s = server_find (parv[5]);
461     if (!s)
462     {
463     slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[5]);
464     return;
465     }
466    
467     slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
468    
469     u = user_add (parv[0], parv[3], parv[4], NULL, NULL, NULL, parv[6], s, atoi (parv[2]));
470    
471     handle_nickchange (u);
472     }
473    
474     /* if it's only 2 then it's a nickname change */
475     else if (parc == 2)
476     {
477     if (!si->su)
478     {
479     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
480     return;
481     }
482    
483     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
484    
485     user_changenick (si->su, parv[0], atoi (parv[1]));
486    
487     handle_nickchange (si->su);
488     }
489     else
490     {
491     int i;
492     slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
493    
494     for (i = 0; i < parc; i++)
495     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
496     }
497     }
498    
499     static void
500     m_quit (sourceinfo_t *si, int parc, char *parv[])
501     {
502     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
503    
504     /* user_delete() takes care of removing channels and so forth */
505     user_delete (si->su);
506     }
507    
508     static void
509     m_mode (sourceinfo_t *si, int parc, char *parv[])
510     {
511     if (*parv[0] == '#')
512     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
513     else
514     user_mode (user_find (parv[0]), parv[1]);
515     }
516    
517     static void
518     m_kick (sourceinfo_t *si, int parc, char *parv[])
519     {
520     user_t *u = user_find (parv[1]);
521     channel_t *c = channel_find (parv[0]);
522    
523     /* -> :rakaur KICK #shrike rintaun :test */
524     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
525    
526     if (!u)
527     {
528     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
529     return;
530     }
531    
532     if (!c)
533     {
534     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
535     return;
536     }
537    
538     if (!chanuser_find (c, u))
539     {
540     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
541     return;
542     }
543    
544     chanuser_delete (c, u);
545    
546     /* if they kicked us, let's rejoin */
547     if (is_internal_client (u))
548     {
549     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
550     join (parv[0], u->nick);
551     }
552     }
553    
554     static void
555     m_kill (sourceinfo_t *si, int parc, char *parv[])
556     {
557     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
558     }
559    
560     static void
561     m_squit (sourceinfo_t *si, int parc, char *parv[])
562     {
563     slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
564     server_delete (parv[0]);
565     }
566    
567     static void
568     m_server (sourceinfo_t *si, int parc, char *parv[])
569     {
570     server_t *s;
571    
572     slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
573     s = handle_server (si, parv[0], NULL, atoi (parv[1]), parv[2]);
574    
575     if (s != NULL && s->uplink != me.me)
576     {
577     /* elicit PONG for EOB detection; pinging uplink is
578     * already done elsewhere -- jilles
579     */
580     sts (":%s PING %s %s", me.name, me.name, s->name);
581     }
582     }
583    
584     static void
585     m_stats (sourceinfo_t *si, int parc, char *parv[])
586     {
587     handle_stats (si->su, parv[0][0]);
588     }
589    
590     static void
591     m_admin (sourceinfo_t *si, int parc, char *parv[])
592     {
593     handle_admin (si->su);
594     }
595    
596     static void
597     m_version (sourceinfo_t *si, int parc, char *parv[])
598     {
599     handle_version (si->su);
600     }
601    
602     static void
603     m_info (sourceinfo_t *si, int parc, char *parv[])
604     {
605     handle_info (si->su);
606     }
607    
608     static void
609     m_whois (sourceinfo_t *si, int parc, char *parv[])
610     {
611     handle_whois (si->su, parv[1]);
612     }
613    
614     static void
615     m_trace (sourceinfo_t *si, int parc, char *parv[])
616     {
617     handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
618     }
619    
620     static void
621     m_away (sourceinfo_t *si, int parc, char *parv[])
622     {
623     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
624     }
625    
626     static void
627     m_join (sourceinfo_t *si, int parc, char *parv[])
628     {
629     chanuser_t *cu;
630     node_t *n, *tn;
631     int chanc;
632     char *chanv[256];
633     int i;
634    
635     /* JOIN 0 is really a part from all channels */
636     if (parv[0][0] == '0')
637     {
638     LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
639     {
640     cu = (chanuser_t *) n->data;
641     chanuser_delete (cu->chan, si->su);
642     }
643     }
644     else
645     {
646     chanc = sjtoken (parv[0], ',', chanv);
647     for (i = 0; i < chanc; i++)
648     {
649     channel_t *c = channel_find (chanv[i]);
650    
651     if (!c)
652     {
653     slog (LG_DEBUG, "m_join(): new channel: %s", parv[0]);
654     c = channel_add (chanv[i], NOW, si->su->server);
655     /* Tell the core to check mode locks now,
656     * otherwise it may only happen after the next
657     * mode change.
658     * DreamForge does not allow any redundant modes
659     * so this will not look ugly. -- jilles */
660     /* If this is in a burst, a MODE with the
661     * simple modes will follow so we can skip
662     * this. -- jilles */
663     if (!me.bursting)
664     channel_mode (NULL, c, "+");
665     }
666     chanuser_add (c, si->su->nick);
667     }
668     }
669     }
670    
671     static void
672     m_pass (sourceinfo_t *si, int parc, char *parv[])
673     {
674     if (strcmp (curr_uplink->pass, parv[0]))
675     {
676     slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
677     runflags |= RF_SHUTDOWN;
678     }
679     }
680    
681     static void
682     m_error (sourceinfo_t *si, int parc, char *parv[])
683     {
684     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
685     }
686    
687     static pcommand_t const pcommands[] = {
688     { "PING", m_ping, 1, MSRC_USER | MSRC_SERVER },
689     { "PONG", m_pong, 1, MSRC_SERVER },
690     { "PRIVMSG", m_privmsg, 2, MSRC_USER },
691     { "NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER },
692     { "PART", m_part, 1, MSRC_USER },
693     { "NICK", m_nick, 2, MSRC_USER | MSRC_SERVER },
694     { "QUIT", m_quit, 1, MSRC_USER },
695     { "MODE", m_mode, 2, MSRC_USER | MSRC_SERVER },
696     { "KICK", m_kick, 2, MSRC_USER | MSRC_SERVER },
697     { "KILL", m_kill, 1, MSRC_USER | MSRC_SERVER },
698     { "SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER },
699     { "SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER },
700     { "STATS", m_stats, 2, MSRC_USER },
701     { "ADMIN", m_admin, 1, MSRC_USER },
702     { "VERSION", m_version, 1, MSRC_USER },
703     { "INFO", m_info, 1, MSRC_USER },
704     { "WHOIS", m_whois, 2, MSRC_USER },
705     { "TRACE", m_trace, 1, MSRC_USER },
706     { "AWAY", m_away, 0, MSRC_USER },
707     { "JOIN", m_join, 1, MSRC_USER },
708     { "PASS", m_pass, 1, MSRC_UNREG },
709     { "ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER },
710     { "TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER },
711     };
712    
713     sorcery_handler::sorcery_handler ()
714     {
715     mode_list = sorcery_mode_list;
716     ignore_mode_list = sorcery_ignore_mode_list;
717     status_mode_list = sorcery_status_mode_list;
718     prefix_mode_list = sorcery_prefix_mode_list;
719    
720     ircd = &Sorcery;
721    
722     pcommand_add (&pcommands);
723     }
724    
725     sorcery_handler::~sorcery_handler ()
726     {
727     mode_list = NULL;
728     ignore_mode_list = NULL;
729     status_mode_list = NULL;
730     prefix_mode_list = NULL;
731    
732     ircd = NULL;
733    
734     pcommand_delete (&pcommands);
735     }
736     } // namespace protocol
737    
738     #define FACREG_TYPE protocol::sorcery_handler
739     #define FACREG_TYPE_NAME "sorcery"
740     #define FACREG_INTERFACE_TYPE protocol::handler
741     #include <ermyth/factory_reg.h>