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

File Contents

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