ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/modules/statserv/main.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 24690 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# User Rev Content
1 michael 3389 /* Statistics generation (StatServ) main module.
2     * Based on code by Andrew Kempe (TheShadow).
3     *
4     * IRC Services is copyright (c) 1996-2009 Andrew Church.
5     * E-mail: <achurch@achurch.org>
6     * Parts written by Andrew Kempe and others.
7     * This program is free but copyrighted software; see the file GPL.txt for
8     * details.
9     */
10    
11     #include "services.h"
12     #include "modules.h"
13     #include "conffile.h"
14     #include "commands.h"
15     #include "databases.h"
16     #include "language.h"
17     #include "modules/operserv/operserv.h"
18    
19     #include "statserv.h"
20    
21     /*************************************************************************/
22    
23     static Module *module_operserv;
24     static Module *module_nickserv;
25    
26     static int cb_command = -1;
27     static int cb_help = -1;
28     static int cb_help_cmds = -1;
29    
30    
31     char *s_StatServ;
32     static char *desc_StatServ;
33     static int SSOpersOnly;
34    
35     static int16 servercnt = 0; /* Number of online servers */
36    
37     /*************************************************************************/
38    
39     static void do_help(User *u);
40     static void do_servers(User *u);
41     static void do_users(User *u);
42    
43     /*************************************************************************/
44    
45     static Command cmds[] = {
46     { "HELP", do_help, NULL, -1, -1,-1 },
47     { "SERVERS", do_servers, NULL, -1, STAT_HELP_SERVERS,
48     STAT_OPER_HELP_SERVERS },
49     { "USERS", do_users, NULL, STAT_HELP_USERS, -1,-1 },
50     { NULL }
51     };
52    
53     /*************************************************************************/
54     /**************************** Database stuff *****************************/
55     /*************************************************************************/
56    
57     #define HASHFUNC(key) DEFAULT_HASHFUNC(key)
58     #define EXPIRE_CHECK(node) 0
59     #define add_serverstats static _add_serverstats
60     #define del_serverstats static _del_serverstats
61     #include "hash.h"
62     DEFINE_HASH(serverstats, ServerStats, name);
63     #undef add_serverstats
64     #undef del_serverstats
65    
66     static void *alloc_serverstats(void)
67     {
68     return scalloc(sizeof(ServerStats), 1);
69     }
70    
71     ServerStats *add_serverstats(ServerStats *ss)
72     {
73     _add_serverstats(ss);
74     return ss;
75     }
76    
77     void del_serverstats(ServerStats *ss)
78     {
79     _del_serverstats(ss);
80     free_serverstats(ss);
81     }
82    
83     ServerStats *put_serverstats(ServerStats *ss)
84     {
85     return ss;
86     }
87    
88     EXPORT_FUNC(add_serverstats)
89     EXPORT_FUNC(del_serverstats)
90     EXPORT_FUNC(get_serverstats)
91     EXPORT_FUNC(put_serverstats)
92     EXPORT_FUNC(first_serverstats)
93     EXPORT_FUNC(next_serverstats)
94    
95     /*************************************************************************/
96    
97     /* Free all memory used by database tables. */
98    
99     static void clean_dbtables(void)
100     {
101     ServerStats *ss;
102     for (ss = first_serverstats(); ss; ss = next_serverstats())
103     free_serverstats(ss);
104     }
105    
106     /*************************************************************************/
107    
108     /* Database load helper: if the server was online when the databas was last
109     * saved, set t_quit to a time just before Services started up, so servers
110     * are never seen as online until we actually receive the SERVER message.
111     */
112     static void db_put_t_quit(void *record, const void *value)
113     {
114     ServerStats *ss = (ServerStats *)record;
115     if (SS_IS_ONLINE(ss)) {
116     ss->t_quit = time(NULL) - 1;
117     if (SS_IS_ONLINE(ss)) { /* Just in case */
118     ss->t_quit = ss->t_join + 1;
119     }
120     }
121     }
122    
123     static DBField stat_servers_dbfields[] = {
124     { "name", DBTYPE_STRING, offsetof(ServerStats,name) },
125     { "t_join", DBTYPE_TIME, offsetof(ServerStats,t_join) },
126     { "t_quit", DBTYPE_TIME, offsetof(ServerStats,t_quit),
127     .put = db_put_t_quit},
128     { "quit_message", DBTYPE_STRING, offsetof(ServerStats,quit_message) },
129     { NULL }
130     };
131     static DBTable stat_servers_dbtable = {
132     .name = "stat-servers",
133     .newrec = alloc_serverstats,
134     .freerec = (void *)free_serverstats,
135     .insert = (void *)add_serverstats,
136     .first = (void *)first_serverstats,
137     .next = (void *)next_serverstats,
138     .fields = stat_servers_dbfields,
139     };
140    
141     /*************************************************************************/
142     /****************************** Statistics *******************************/
143     /*************************************************************************/
144    
145     /* Introduce the StatServ pseudoclient. */
146    
147     static int introduce_statserv(const char *nick)
148     {
149     if (!nick || irc_stricmp(nick, s_StatServ) == 0) {
150     send_pseudo_nick(s_StatServ, desc_StatServ, PSEUDO_INVIS);
151     if (nick)
152     return 1;
153     }
154     return 0;
155     }
156    
157     /*************************************************************************/
158    
159     /* Main StatServ routine. */
160    
161     static int statserv(const char *source, const char *target, char *buf)
162     {
163     const char *cmd;
164     const char *s;
165     User *u;
166    
167     if (irc_stricmp(target, s_StatServ) != 0)
168     return 0;
169    
170     u = get_user(source);
171     if (!u) {
172     module_log("user record for %s not found", source);
173     notice(s_StatServ, source, getstring(NULL,INTERNAL_ERROR));
174     return 1;
175     }
176    
177     if (SSOpersOnly && !is_oper(u)) {
178     notice_lang(s_StatServ, u, ACCESS_DENIED);
179     return 1;
180     }
181    
182     cmd = strtok(buf, " ");
183     if (!cmd) {
184     return 1;
185     } else if (stricmp(cmd, "\1PING") == 0) {
186     if (!(s = strtok_remaining()))
187     s = "\1";
188     notice(s_StatServ, source, "\1PING %s", s);
189     } else {
190     if (call_callback_2(cb_command, u, cmd) <= 0)
191     run_cmd(s_StatServ, u, THIS_MODULE, cmd);
192     }
193     return 1;
194     }
195    
196     /*************************************************************************/
197    
198     /* Return a /WHOIS response for StatServ. */
199    
200     static int statserv_whois(const char *source, char *who, char *extra)
201     {
202     if (irc_stricmp(who, s_StatServ) != 0)
203     return 0;
204     send_cmd(ServerName, "311 %s %s %s %s * :%s", source, who,
205     ServiceUser, ServiceHost, desc_StatServ);
206     send_cmd(ServerName, "312 %s %s %s :%s", source, who,
207     ServerName, ServerDesc);
208     send_cmd(ServerName, "313 %s %s :is a network service", source, who);
209     send_cmd(ServerName, "318 %s %s End of /WHOIS response.", source, who);
210     return 1;
211     }
212    
213     /*************************************************************************/
214    
215     /* Callback for NickServ REGISTER/LINK check; we disallow
216     * registration/linking of the StatServ pseudoclient nickname.
217     */
218    
219     static int do_reglink_check(const User *u, const char *nick,
220     const char *pass, const char *email)
221     {
222     return irc_stricmp(nick, s_StatServ) == 0;
223     }
224    
225     /*************************************************************************/
226     /************************* Server info display ***************************/
227     /*************************************************************************/
228    
229     /* Return a help message. */
230    
231     static void do_help(User *u)
232     {
233     char *cmd = strtok_remaining();
234    
235     if (!cmd) {
236     notice_help(s_StatServ, u, STAT_HELP);
237     } else if (stricmp(cmd, "COMMANDS") == 0) {
238     notice_help(s_StatServ, u, STAT_HELP_COMMANDS);
239     call_callback_2(cb_help_cmds, u, 0);
240     } else if (call_callback_2(cb_help, u, cmd) > 0) {
241     return;
242     } else {
243     help_cmd(s_StatServ, u, THIS_MODULE, cmd);
244     }
245     }
246    
247     /*************************************************************************/
248    
249     static void do_servers(User *u)
250     {
251     ServerStats *ss = NULL;
252     const char *cmd = strtok(NULL, " ");
253     char *mask = strtok(NULL, " ");
254     int count = 0, nservers = 0;
255    
256     if (!cmd)
257     cmd = "";
258    
259     if (stricmp(cmd, "STATS") == 0) {
260     ServerStats *ss_lastquit = NULL;
261     int onlinecount = 0;
262     char lastquit_buf[BUFSIZE];
263    
264     for (ss = first_serverstats(); ss; ss = next_serverstats()) {
265     nservers++;
266     if (ss->t_quit > 0
267     && (!ss_lastquit
268     || ss->t_quit > ss_lastquit->t_quit))
269     ss_lastquit = ss;
270     if (SS_IS_ONLINE(ss))
271     onlinecount++;
272     }
273    
274     notice_lang(s_StatServ, u, STAT_SERVERS_STATS_TOTAL, nservers);
275     notice_lang(s_StatServ, u, STAT_SERVERS_STATS_ON_OFFLINE,
276     onlinecount, (onlinecount*100)/nservers,
277     nservers-onlinecount,
278     ((nservers-onlinecount)*100)/nservers);
279     if (ss_lastquit) {
280     strftime_lang(lastquit_buf, sizeof(lastquit_buf), u->ngi,
281     STRFTIME_DATE_TIME_FORMAT, ss_lastquit->t_quit);
282     notice_lang(s_StatServ, u, STAT_SERVERS_LASTQUIT_WAS,
283     ss_lastquit->name, lastquit_buf);
284     }
285    
286    
287     } else if (stricmp(cmd, "LIST") == 0) {
288     int matchcount = 0;
289    
290     notice_lang(s_StatServ, u, STAT_SERVERS_LIST_HEADER);
291     for (ss = first_serverstats(); ss; ss = next_serverstats()) {
292     if (mask && !match_wild_nocase(mask, ss->name))
293     continue;
294     matchcount++;
295     if (!SS_IS_ONLINE(ss))
296     continue;
297     count++;
298     notice_lang(s_StatServ, u, STAT_SERVERS_LIST_FORMAT,
299     ss->name, ss->usercnt,
300     !usercnt ? 0 : (ss->usercnt*100)/usercnt,
301     ss->opercnt,
302     !opcnt ? 0 : (ss->opercnt*100)/opcnt);
303     }
304     notice_lang(s_StatServ, u, STAT_SERVERS_LIST_RESULTS,
305     count, matchcount);
306    
307     } else if (stricmp(cmd, "VIEW") == 0) {
308     char *param = strtok(NULL, " ");
309     char join_buf[BUFSIZE];
310     char quit_buf[BUFSIZE];
311     int is_online;
312     int limitto = 0; /* 0 == none; 1 == online; 2 == offline */
313    
314     if (param) {
315     if (stricmp(param, "ONLINE") == 0) {
316     limitto = 1;
317     } else if (stricmp(param, "OFFLINE") == 0) {
318     limitto = 2;
319     }
320     }
321    
322     for (ss = first_serverstats(); ss; ss = next_serverstats()) {
323     nservers++;
324     if (mask && !match_wild_nocase(mask, ss->name))
325     continue;
326     is_online = SS_IS_ONLINE(ss);
327     if (limitto && !((is_online && limitto == 1) ||
328     (!is_online && limitto == 2)))
329     continue;
330    
331     count++;
332     strftime_lang(join_buf, sizeof(join_buf), u->ngi,
333     STRFTIME_DATE_TIME_FORMAT, ss->t_join);
334     if (ss->t_quit != 0) {
335     strftime_lang(quit_buf, sizeof(quit_buf), u->ngi,
336     STRFTIME_DATE_TIME_FORMAT, ss->t_quit);
337     }
338    
339     notice_lang(s_StatServ, u,
340     is_online ? STAT_SERVERS_VIEW_HEADER_ONLINE
341     : STAT_SERVERS_VIEW_HEADER_OFFLINE,
342     ss->name);
343     notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_LASTJOIN, join_buf);
344     if (ss->t_quit > 0)
345     notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_LASTQUIT,
346     quit_buf);
347     if (ss->quit_message)
348     notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_QUITMSG,
349     ss->quit_message);
350     if (is_online)
351     notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_USERS_OPERS,
352     ss->usercnt,
353     !usercnt ? 0 : (ss->usercnt*100)/usercnt,
354     ss->opercnt,
355     !opcnt ? 0 : (ss->opercnt*100)/opcnt);
356     }
357     notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_RESULTS, count, nservers);
358    
359     } else if (!is_services_admin(u)) {
360     if (is_oper(u))
361     notice_lang(s_StatServ, u, PERMISSION_DENIED);
362     else
363     syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_SYNTAX);
364    
365     /* Only Services admins have access from here on! */
366    
367     } else if (stricmp(cmd, "DELETE") == 0) {
368     if (!mask) {
369     syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_DELETE_SYNTAX);
370     } else if (!(ss = get_serverstats(mask))) {
371     notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask);
372     } else if (SS_IS_ONLINE(ss)) {
373     notice_lang(s_StatServ, u, STAT_SERVERS_REMOVE_SERV_FIRST, mask);
374     } else {
375     del_serverstats(ss);
376     ss = NULL;
377     notice_lang(s_StatServ, u, STAT_SERVERS_DELETE_DONE, mask);
378     }
379    
380     } else if (stricmp(cmd, "COPY") == 0) {
381     const char *newname = strtok(NULL, " ");
382     ServerStats *newss;
383     if (!mask || !newname) {
384     syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_COPY_SYNTAX);
385     } else if (!(ss = get_serverstats(mask))) {
386     notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask);
387     } else if ((newss = get_serverstats(newname)) != NULL) {
388     put_serverstats(newss);
389     notice_lang(s_StatServ, u, STAT_SERVERS_SERVER_EXISTS, newname);
390     } else {
391     newss = new_serverstats(newname);
392     newss->t_join = ss->t_join;
393     newss->t_quit = ss->t_quit;
394     if (ss->quit_message) {
395     newss->quit_message = sstrdup(ss->quit_message);
396     }
397     add_serverstats(newss);
398     put_serverstats(newss);
399     notice_lang(s_StatServ, u, STAT_SERVERS_COPY_DONE, mask, newname);
400     }
401    
402     } else if (stricmp(cmd, "RENAME") == 0) {
403     const char *newname = strtok(NULL, " ");
404     ServerStats *newss;
405     if (!mask || !newname) {
406     syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_RENAME_SYNTAX);
407     } else if (!(ss = get_serverstats(mask))) {
408     notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask);
409     } else if ((newss = get_serverstats(newname)) != NULL) {
410     put_serverstats(newss);
411     notice_lang(s_StatServ, u, STAT_SERVERS_SERVER_EXISTS, newname);
412     } else if (SS_IS_ONLINE(ss)) {
413     notice_lang(s_StatServ, u, STAT_SERVERS_REMOVE_SERV_FIRST, mask);
414     } else {
415     newss = new_serverstats(newname);
416     newss->t_join = ss->t_join;
417     newss->t_quit = ss->t_quit;
418     if (ss->quit_message) {
419     newss->quit_message = sstrdup(ss->quit_message);
420     }
421     del_serverstats(ss);
422     ss = NULL;
423     add_serverstats(newss);
424     put_serverstats(newss);
425     notice_lang(s_StatServ, u, STAT_SERVERS_RENAME_DONE,
426     mask, newname);
427     }
428    
429     } else {
430     syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_SYNTAX);
431     }
432    
433     put_serverstats(ss);
434     }
435    
436     /*************************************************************************/
437    
438     static void do_users(User *u)
439     {
440     const char *cmd = strtok(NULL, " ");
441     int avgusers, avgopers;
442    
443     if (!cmd)
444     cmd = "";
445    
446     if (stricmp(cmd, "STATS") == 0) {
447     notice_lang(s_StatServ, u, STAT_USERS_TOTUSERS, usercnt);
448     notice_lang(s_StatServ, u, STAT_USERS_TOTOPERS, opcnt);
449     avgusers = (usercnt + servercnt/2) / servercnt;
450     avgopers = (opcnt*10 + servercnt/2) / servercnt;
451     notice_lang(s_StatServ, u, STAT_USERS_SERVUSERS, avgusers);
452     notice_lang(s_StatServ, u, STAT_USERS_SERVOPERS,
453     avgopers/10, avgopers%10);
454     } else {
455     syntax_error(s_StatServ, u, "USERS", STAT_USERS_SYNTAX);
456     }
457     }
458    
459     /*************************************************************************/
460     /******************** ServerStats new/free (global) **********************/
461     /*************************************************************************/
462    
463     /* Create a new ServerStats structure for the given server name and return
464     * it. Always successful.
465     */
466    
467     EXPORT_FUNC(new_serverstats)
468     ServerStats *new_serverstats(const char *servername)
469     {
470     ServerStats *ss = alloc_serverstats();
471     if (ss)
472     ss->name = sstrdup(servername);
473     return ss;
474     }
475    
476     /*************************************************************************/
477    
478     /* Free a ServerStats structure and associated data. */
479    
480     EXPORT_FUNC(free_serverstats)
481     void free_serverstats(ServerStats *ss)
482     {
483     free(ss->name);
484     free(ss->quit_message);
485     free(ss);
486     }
487    
488     /*************************************************************************/
489     /************************** Callback routines ****************************/
490     /*************************************************************************/
491    
492     /* Handle a server joining. */
493    
494     static int stats_do_server(Server *server)
495     {
496     ServerStats *ss;
497    
498     servercnt++;
499    
500     ss = get_serverstats(server->name);
501     if (ss) {
502     /* Server has rejoined us */
503     ss->usercnt = 0;
504     ss->opercnt = 0;
505     ss->t_join = time(NULL);
506     } else {
507     /* Totally new server */
508     ss = new_serverstats(server->name); /* cleared to zero */
509     ss->t_join = time(NULL);
510     add_serverstats(ss);
511     }
512    
513     server->stats = ss;
514     return 0;
515     }
516    
517     /*************************************************************************/
518    
519     /* Handle a server quitting. */
520    
521     static int stats_do_squit(Server *server, const char *quit_message)
522     {
523     ServerStats *ss = server->stats;
524    
525     servercnt--;
526     ss->t_quit = time(NULL);
527     free(ss->quit_message);
528     ss->quit_message = *quit_message ? sstrdup(quit_message) : NULL;
529     put_serverstats(ss);
530     return 0;
531     }
532    
533     /*************************************************************************/
534    
535     /* Handle a user joining. */
536    
537     static int stats_do_newuser(User *user)
538     {
539     if (user->server)
540     user->server->stats->usercnt++;
541     return 0;
542     }
543    
544     /*************************************************************************/
545    
546     /* Handle a user quitting. */
547    
548     static int stats_do_quit(User *user)
549     {
550     if (user->server) {
551     ServerStats *ss = user->server->stats;
552     if (!ss) {
553     module_log("BUG! no serverstats for %s in do_quit(%s)",
554     user->server->name, user->nick);
555     return 0;
556     }
557     ss->usercnt--;
558     if (is_oper(user))
559     ss->opercnt--;
560     }
561     return 0;
562     }
563    
564     /*************************************************************************/
565    
566     /* Handle a user mode change. */
567    
568     static int stats_do_umode(User *user, int modechar, int add)
569     {
570     if (user->server) {
571     if (modechar == 'o') {
572     ServerStats *ss = user->server->stats;
573     if (!ss) {
574     module_log("BUG! no serverstats for %s in do_quit(%s)",
575     user->server->name, user->nick);
576     return 0;
577     }
578     if (add)
579     ss->opercnt++;
580     else
581     ss->opercnt--;
582     }
583     }
584     return 0;
585     }
586    
587     /*************************************************************************/
588    
589     /* OperServ STATS ALL callback. */
590    
591     static int do_stats_all(User *user, const char *s_OperServ)
592     {
593     int32 count, mem;
594     ServerStats *ss;
595    
596     count = mem = 0;
597     for (ss = first_serverstats(); ss; ss = next_serverstats()) {
598     count++;
599     mem += sizeof(*ss) + strlen(ss->name)+1;
600     if (ss->quit_message)
601     mem += strlen(ss->quit_message)+1;
602     }
603     notice_lang(s_OperServ, user, OPER_STATS_ALL_STATSERV_MEM,
604     count, (mem+512) / 1024);
605    
606     return 0;
607     }
608    
609     /*************************************************************************/
610     /**************************** Module functions ***************************/
611     /*************************************************************************/
612    
613     ConfigDirective module_config[] = {
614     { "SSOpersOnly", { { CD_SET, 0, &SSOpersOnly } } },
615     { "StatServName", { { CD_STRING, CF_DIRREQ, &s_StatServ },
616     { CD_STRING, 0, &desc_StatServ } } },
617     { NULL }
618     };
619    
620     /*************************************************************************/
621    
622     static int do_load_module(Module *mod, const char *modname)
623     {
624     if (strcmp(modname, "operserv/main") == 0) {
625     module_operserv = mod;
626     if (!add_callback(mod, "STATS ALL", do_stats_all))
627     module_log("Unable to register OperServ STATS ALL callback");
628     }
629     if (strcmp(modname, "nickserv/main") == 0) {
630     module_nickserv = mod;
631     if (!add_callback(mod, "REGISTER/LINK check", do_reglink_check))
632     module_log("Unable to register NickServ REGISTER/LINK check"
633     " callback");
634     }
635     return 0;
636     }
637    
638     /*************************************************************************/
639    
640     static int do_unload_module(Module *mod)
641     {
642     if (mod == module_operserv) {
643     remove_callback(mod, "STATS ALL", do_stats_all);
644     module_operserv = NULL;
645     }
646     if (mod == module_nickserv) {
647     remove_callback(mod, "REGISTER/LINK check", do_reglink_check);
648     module_nickserv = NULL;
649     }
650     return 0;
651     }
652    
653     /*************************************************************************/
654    
655     static int do_reconfigure(int after_configure)
656     {
657     static char old_s_StatServ[NICKMAX];
658     static char *old_desc_StatServ = NULL;
659    
660     if (!after_configure) {
661     /* Before reconfiguration: save old values. */
662     strbcpy(old_s_StatServ, s_StatServ);
663     old_desc_StatServ = strdup(desc_StatServ);
664     } else {
665     /* After reconfiguration: handle value changes. */
666     if (strcmp(old_s_StatServ, s_StatServ) != 0)
667     send_nickchange(old_s_StatServ, s_StatServ);
668     if (!old_desc_StatServ || strcmp(old_desc_StatServ,desc_StatServ) != 0)
669     send_namechange(s_StatServ, desc_StatServ);
670     free(old_desc_StatServ);
671     old_desc_StatServ = NULL;
672     } /* if (!after_configure) */
673     return 0;
674     }
675    
676     /*************************************************************************/
677    
678     int init_module(void)
679     {
680     Module *tmpmod;
681    
682    
683     if (!new_commandlist(THIS_MODULE)
684     || !register_commands(THIS_MODULE, cmds)
685     ) {
686     module_log("Unable to register commands");
687     exit_module(0);
688     return 0;
689     }
690    
691     cb_command = register_callback("command");
692     cb_help = register_callback("HELP");
693     cb_help_cmds = register_callback("HELP COMMANDS");
694     if (cb_command < 0 || cb_help < 0 || cb_help_cmds < 0) {
695     module_log("Unable to register callbacks");
696     exit_module(0);
697     return 0;
698     }
699    
700     if (!add_callback(NULL, "load module", do_load_module)
701     || !add_callback(NULL, "unload module", do_unload_module)
702     || !add_callback(NULL, "reconfigure", do_reconfigure)
703     || !add_callback(NULL, "introduce_user", introduce_statserv)
704     || !add_callback(NULL, "m_privmsg", statserv)
705     || !add_callback(NULL, "m_whois", statserv_whois)
706     || !add_callback(NULL, "server create", stats_do_server)
707     || !add_callback(NULL, "server delete", stats_do_squit)
708     || !add_callback(NULL, "user create", stats_do_newuser)
709     || !add_callback(NULL, "user delete", stats_do_quit)
710     || !add_callback(NULL, "user MODE", stats_do_umode)
711     ) {
712     module_log("Unable to add callbacks");
713     exit_module(0);
714     return 0;
715     }
716    
717     tmpmod = find_module("nickserv/main");
718     if (tmpmod)
719     do_load_module(tmpmod, "nickserv/main");
720    
721     if (!register_dbtable(&stat_servers_dbtable)) {
722     module_log("Unable to register database table");
723     exit_module(0);
724     return 0;
725     }
726    
727     if (linked)
728     introduce_statserv(NULL);
729    
730     return 1;
731     }
732    
733     /*************************************************************************/
734    
735     int exit_module(int shutdown_unused)
736     {
737     if (linked)
738     send_cmd(s_StatServ, "QUIT :");
739    
740     unregister_dbtable(&stat_servers_dbtable);
741     clean_dbtables();
742    
743     if (module_nickserv)
744     do_unload_module(module_nickserv);
745     if (module_operserv)
746     do_unload_module(module_operserv);
747    
748     remove_callback(NULL, "user MODE", stats_do_umode);
749     remove_callback(NULL, "user delete", stats_do_quit);
750     remove_callback(NULL, "user create", stats_do_newuser);
751     remove_callback(NULL, "server delete", stats_do_squit);
752     remove_callback(NULL, "server create", stats_do_server);
753     remove_callback(NULL, "m_whois", statserv_whois);
754     remove_callback(NULL, "m_privmsg", statserv);
755     remove_callback(NULL, "introduce_user", introduce_statserv);
756     remove_callback(NULL, "reconfigure", do_reconfigure);
757     remove_callback(NULL, "unload module", do_unload_module);
758     remove_callback(NULL, "load module", do_load_module);
759    
760     unregister_callback(cb_help_cmds);
761     unregister_callback(cb_help);
762     unregister_callback(cb_command);
763    
764     unregister_commands(THIS_MODULE, cmds);
765     del_commandlist(THIS_MODULE);
766    
767     return 1;
768     }
769    
770     /*************************************************************************/
771    
772     /*
773     * Local variables:
774     * c-file-style: "stroustrup"
775     * c-file-offsets: ((case-label . *) (statement-case-intro . *))
776     * indent-tabs-mode: nil
777     * End:
778     *
779     * vim: expandtab shiftwidth=4:
780     */