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 |
*/ |