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