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

# Content
1 /* 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 */