· 7 years ago · Dec 28, 2018, 10:40 PM
1/*
2 * Module skeleton, by Carsten V. Munk 2001 <stskeeps@tspre.org>
3 * May be used, modified, or changed by anyone, no license applies.
4 * You may relicense this, to any license
5 */
6
7 /* for compile, use:
8 EXLIBS="-lmysqlclient" make
9 */
10
11#define USE_MYSQL
12#ifdef USE_MYSQL
13
14#define MYCONF "wwwstats"
15#define DEFAULT_MYSQL_INTERVAL 900
16#define list_add list_add_MYSQL
17#include <mysql/mysql.h>
18#undef list_add
19#endif
20#include "unrealircd.h"
21#include "threads.h"
22#include <sys/socket.h>
23#include <sys/un.h>
24
25struct chanStats_s {
26 aChannel *chan;
27 char chname[2*CHANNELLEN+1];
28 int msg;
29 int exists;
30 struct chanStats_s *next;
31};
32
33struct channelInfo_s {
34 int hashnum;
35 aChannel *chan;
36 int messages;
37};
38
39struct asendInfo_s {
40 int sock;
41 char *buf;
42 int bufsize;
43 char *tmpbuf;
44};
45
46typedef struct chanStats_s chanStats;
47typedef struct channelInfo_s channelInfo;
48typedef struct asendInfo_s asendInfo;
49
50int counter;
51time_t init_time;
52
53int stats_socket;
54THREAD thr;
55MUTEX chans_mutex;
56int chans_mutex_ai;
57char send_buf[4096];
58struct sockaddr_un stats_addr;
59#ifdef USE_MYSQL
60THREAD mysql_thr;
61MYSQL *stats_db;
62#endif
63
64char* wwwstats_msg(aClient *sptr, aChannel *chptr, char *msg, int notice);
65void wwwstats_thr(void*);
66void asend_sprintf(asendInfo *info, char *fmt, ...);
67void append_int_param(asendInfo *info, char *param, int value);
68int getChannelInfo(channelInfo *prev);
69aChannel *getChanByName(char *name);
70void removeExpiredChannels();
71char *tmp_escape(char *d, const char *a);
72void appendChannel(aChannel *ch, int messages);
73#ifdef USE_MYSQL
74void saveChannels(time_t act_time);
75void saveStats(time_t act_time);
76int mysql_query_sprintf(char *buf, char *fmt, ...);
77void wwwstats_mysql_thr(void *d);
78void loadChannels(void);
79void send_mysql_error(void);
80#endif
81int wwwstats_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
82int wwwstats_configposttest(int *errs);
83int wwwstats_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
84
85chanStats *chans, *chans_last;
86
87// config file stuff, based on Gottem's module
88
89#ifdef USE_MYSQL
90static char *mysql_user;
91static char *mysql_pass;
92static char *mysql_db;
93static char *mysql_host;
94static int use_mysql;
95static int mysql_interval;
96#endif
97static char *socket_path;
98int socket_hpath=0;
99
100int wwwstats_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
101 ConfigEntry *cep; // For looping through our bl0cc
102 int errors = 0; // Error count
103 int i; // iter8or m8
104
105#ifdef USE_MYSQL
106 int mysql_huser=0, mysql_hpass=0, mysql_hdb=0, mysql_en=0, mysql_hhost=0;
107#endif
108
109 // Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
110 if(type != CONFIG_MAIN)
111 return 0; // Returning 0 means idgaf bout dis
112
113 // Check for valid config entries first
114 if(!ce || !ce->ce_varname)
115 return 0;
116
117 // If it isn't our bl0ck, idc
118 if(strcmp(ce->ce_varname, MYCONF))
119 return 0;
120
121 // Loop dat shyte fam
122 for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
123 // Do we even have a valid name l0l?
124 if(!cep->ce_varname) {
125 config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
126 errors++; // Increment err0r count fam
127 continue; // Next iteration imo tbh
128 }
129
130#ifdef USE_MYSQL
131 if(!strcmp(cep->ce_varname, "mysql-user")) {
132 if(!cep->ce_vardata) {
133 config_error("%s:%i: %s::%s must be a string", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
134 errors++; // Increment err0r count fam
135 continue;
136 }
137 mysql_huser=1;
138 continue;
139 }
140
141 if(!strcmp(cep->ce_varname, "mysql-pass")) {
142 if(!cep->ce_vardata) {
143 config_error("%s:%i: %s::%s must be a string", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
144 errors++; // Increment err0r count fam
145 continue;
146 }
147 mysql_hpass=1;
148 continue;
149 }
150
151 if(!strcmp(cep->ce_varname, "mysql-db")) {
152 if(!cep->ce_vardata) {
153 config_error("%s:%i: %s::%s must be a string", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
154 errors++; // Increment err0r count fam
155 continue;
156 }
157 mysql_hdb=1;
158 continue;
159 }
160
161 if(!strcmp(cep->ce_varname, "mysql-host")) {
162 if(!cep->ce_vardata) {
163 config_error("%s:%i: %s::%s must be a string", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
164 errors++; // Increment err0r count fam
165 continue;
166 }
167 mysql_hhost=1;
168 continue;
169 }
170
171 if(!strcmp(cep->ce_varname, "mysql-interval")) {
172 if(!cep->ce_vardata) {
173 config_error("%s:%i: %s::%s must be an integer between 1 and 1000 (minutes)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
174 errors++; // Increment err0r count fam
175 continue; // Next iteration imo tbh
176 }
177 // Should be an integer yo
178 for(i = 0; cep->ce_vardata[i]; i++) {
179 if(!isdigit(cep->ce_vardata[i])) {
180 config_error("%s:%i: %s::%s must be an integer between 1 and 1000 (minutes)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
181 errors++; // Increment err0r count fam
182 break;
183 }
184 }
185 if(!errors && (atoi(cep->ce_vardata) < 1 || atoi(cep->ce_vardata) > 1000)) {
186 config_error("%s:%i: %s::%s must be an integer between 1 and 1000 (minutes)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
187 errors++; // Increment err0r count fam
188 }
189 continue;
190 }
191
192 if(!strcmp(cep->ce_varname, "use-mysql")) { // no value expected
193 mysql_en = 1;
194 continue;
195 }
196#endif
197
198 if(!strcmp(cep->ce_varname, "socket-path")) {
199 if(!cep->ce_vardata) {
200 config_error("%s:%i: %s::%s must be a path", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
201 errors++; // Increment err0r count fam
202 continue;
203 }
204 socket_hpath = 1;
205 continue;
206 }
207
208 // Anything else is unknown to us =]
209 config_warn("%s:%i: unknown item %s::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname); // So display just a warning
210 }
211
212 if(mysql_en && (!mysql_huser || !mysql_hpass || !mysql_hdb || !mysql_hhost)){
213 config_warn("m_wwwstats: error: your mysql configuration is incomplete! Please either correct or disable it!");
214 errors++;
215 }
216
217 *errs = errors;
218 return errors ? -1 : 1; // Returning 1 means "all good", -1 means we shat our panties
219}
220
221int wwwstats_configposttest(int *errs) {
222 if(!socket_hpath){
223 config_warn("m_wwwstats: warning: socket path not specified! Socket won't be created.");
224 }
225 return 1;
226}
227
228// "Run" the config (everything should be valid at this point)
229int wwwstats_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
230 ConfigEntry *cep; // For looping through our bl0cc
231
232 // Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
233 if(type != CONFIG_MAIN)
234 return 0; // Returning 0 means idgaf bout dis
235
236 // Check for valid config entries first
237 if(!ce || !ce->ce_varname)
238 return 0;
239
240 // If it isn't our bl0cc, idc
241 if(strcmp(ce->ce_varname, MYCONF))
242 return 0;
243
244 // Loop dat shyte fam
245 for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
246 // Do we even have a valid name l0l?
247 if(!cep->ce_varname)
248 continue; // Next iteration imo tbh
249
250#ifdef USE_MYSQL
251 if(cep->ce_vardata && !strcmp(cep->ce_varname, "mysql-user")) {
252 mysql_user = strdup(cep->ce_vardata);
253 continue;
254 }
255
256 if(cep->ce_vardata && !strcmp(cep->ce_varname, "mysql-pass")) {
257 mysql_pass = strdup(cep->ce_vardata);
258 continue;
259 }
260
261 if(cep->ce_vardata && !strcmp(cep->ce_varname, "mysql-db")) {
262 mysql_db = strdup(cep->ce_vardata);
263 continue;
264 }
265
266 if(cep->ce_vardata && !strcmp(cep->ce_varname, "mysql-host")) {
267 mysql_host = strdup(cep->ce_vardata);
268 continue;
269 }
270
271 if(!strcmp(cep->ce_varname, "mysql-interval")) {
272 mysql_interval = atoi(cep->ce_vardata);
273 continue;
274 }
275
276 if(!strcmp(cep->ce_varname, "use-mysql")) {
277 use_mysql = 1;
278 continue;
279 }
280#endif
281
282 if(cep->ce_vardata && !strcmp(cep->ce_varname, "socket-path")) {
283 socket_path = strdup(cep->ce_vardata);
284 continue;
285 }
286 }
287#ifdef USE_MYSQL
288 if(mysql_interval == 0) mysql_interval = DEFAULT_MYSQL_INTERVAL;
289#endif
290 return 1; // We good
291}
292
293ModuleHeader MOD_HEADER(m_wwwstats)
294 = {
295 "m_wwwstats", /* Name of module */
296 "$Id: v1.07 2018/12/28 rocket/k4be$", /* Version */
297 "Provides data for network stats", /* Short description of module */
298 "3.2-b8-1",
299 NULL
300 };
301
302// Configuration testing-related hewks go in testing phase obv
303MOD_TEST(m_wwwstats) {
304 // We have our own config block so we need to checkem config obv m9
305 // Priorities don't really matter here
306 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, wwwstats_configtest);
307 HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, wwwstats_configposttest);
308 return MOD_SUCCESS;
309}
310
311/* This is called on module init, before Server Ready */
312MOD_INIT(m_wwwstats)
313{
314 /*
315 * We call our add_Command crap here
316 */
317 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, wwwstats_configrun);
318 HookAddPChar(modinfo->handle, HOOKTYPE_PRE_CHANMSG, 0, wwwstats_msg);
319
320 return MOD_SUCCESS;
321}
322
323/* Is first run when server is 100% ready */
324MOD_LOAD(m_wwwstats)
325{
326 #ifdef USE_MYSQL
327 MYSQL_RES *res;
328 MYSQL_ROW row;
329 #endif
330
331 if(socket_path){
332 stats_addr.sun_family = AF_UNIX;
333 strcpy(stats_addr.sun_path, socket_path);
334 unlink(stats_addr.sun_path);
335 }
336
337 #ifdef USE_MYSQL
338 stats_db = mysql_init(NULL);
339 if(!stats_db) send_mysql_error();
340 #endif
341 IRCCreateMutex(chans_mutex);
342 chans_mutex_ai = 0;
343 counter = 0;
344
345 chans = NULL;
346 chans_last = NULL;
347
348 if(socket_path){
349 stats_socket = socket(PF_UNIX, SOCK_STREAM, 0);
350 bind(stats_socket, (struct sockaddr*) &stats_addr, SUN_LEN(&stats_addr));
351 chmod(socket_path, 0777);
352 listen(stats_socket, 5);
353 }
354
355 #ifdef USE_MYSQL
356 if(use_mysql && mysql_host && mysql_user && mysql_pass && mysql_db){
357 mysql_real_connect(stats_db, mysql_host, mysql_user, mysql_pass, mysql_db, 0, NULL, 0);
358
359 mysql_query(stats_db, "CREATE TABLE IF NOT EXISTS `chanlist` (`id` int(11) NOT NULL AUTO_INCREMENT, `date` int(11), `name` char(64), `topic` text, `users` int(11), `messages` int(11), PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`,`users`,`messages`), KEY `name_3` (`name`), KEY `date` (`date`) )");
360 mysql_query(stats_db, "CREATE TABLE IF NOT EXISTS `stat` (`id` int(11) NOT NULL AUTO_INCREMENT, `date` int(11), `clients` int(11), `servers` int(11), `messages` int(11), `channels` int(11), PRIMARY KEY (`id`), UNIQUE KEY `changes` (`clients`,`servers`,`messages`,`channels`), KEY `date` (`date`) )");
361
362 mysql_query(stats_db, "SELECT messages FROM stat ORDER BY id DESC LIMIT 1");
363 res = mysql_use_result(stats_db);
364 if(!res) send_mysql_error(); else {
365 if((row = mysql_fetch_row(res))) {
366 counter = strtoul(row[0], NULL, 10);
367 }
368 mysql_free_result(res);
369 }
370
371 loadChannels();
372 }
373
374 #endif
375 IRCCreateThread(thr, wwwstats_thr, NULL);
376 #ifdef USE_MYSQL
377 if(stats_db) IRCCreateThread(mysql_thr, wwwstats_mysql_thr, NULL);
378 #endif
379
380 return MOD_SUCCESS;
381}
382
383/* Called when module is unloaded */
384MOD_UNLOAD(m_wwwstats)
385{
386 time_t act_time;
387 chanStats *next;
388
389 pthread_cancel(thr);
390 pthread_join(thr, NULL);
391 #ifdef USE_MYSQL
392 if(stats_db){
393 pthread_cancel(mysql_thr);
394 pthread_join(mysql_thr, NULL);
395 }
396 #endif
397
398 close(stats_socket);
399 unlink(stats_addr.sun_path);
400
401 act_time = time(NULL);
402
403 #ifdef USE_MYSQL
404 saveStats(act_time);
405 saveChannels(act_time);
406 #endif
407
408// for(;chans;chans=chans->next) free(chans);
409 for(;chans;chans=next) {
410 next=chans->next;
411 free(chans);
412 }
413 #ifdef USE_MYSQL
414 if(stats_db) mysql_close(stats_db);
415 if(mysql_user) free(mysql_user);
416 if(mysql_pass) free(mysql_pass);
417 if(mysql_db) free(mysql_db);
418 if(mysql_host) free(mysql_host);
419 #endif
420
421 if(socket_path) free(socket_path);
422
423 return MOD_SUCCESS;
424}
425
426char* wwwstats_msg(aClient *sptr, aChannel *chptr, char *msg, int notice) {
427 chanStats *lp;
428 #ifdef USE_MYSQL
429 char name[2*CHANNELLEN+1];
430 char buf[2048];
431 MYSQL_RES *res;
432 MYSQL_ROW row;
433 #endif
434 int c_msg;
435 counter++;
436 for(lp=chans; lp; lp=lp->next) if(lp->chan==chptr) break;
437
438 if(lp) lp->msg++;
439 else {
440 c_msg = 1;
441 #ifdef USE_MYSQL
442 if(use_mysql){
443 tmp_escape(name, chptr->chname);
444 mysql_query_sprintf(buf, "SELECT MAX(messages) FROM chanlist WHERE name='%s' GROUP BY name", name);
445 res = mysql_use_result(stats_db);
446 if(!res) send_mysql_error(); else {
447 if((row = mysql_fetch_row(res)))
448 if(row[0]) c_msg = strtoul(row[0], NULL, 10);
449 mysql_free_result(res);
450 }
451 }
452 #endif
453// sendto_realops("wwwstats: added channel %s, %d msgs", name, c_msg);
454 appendChannel(chptr, c_msg);
455 }
456 return msg;
457}
458
459void wwwstats_thr(void *d) {
460 char buf[2000];
461 char topic[2*TOPICLEN+1];
462 char name[2*CHANNELLEN+1];
463 int i;
464 int sock;
465 channelInfo chinfo;
466 asendInfo asinfo;
467 struct sockaddr_un cli_addr;
468
469 socklen_t slen = sizeof(cli_addr);
470
471 asinfo.buf = send_buf;
472 asinfo.bufsize = sizeof(send_buf);
473 asinfo.tmpbuf = buf;
474
475 aClient *acptr;
476
477 while(1) {
478 sock = accept(stats_socket, (struct sockaddr*) &cli_addr, &slen);
479 if(sock<0) break;
480 asinfo.sock = sock;
481 send_buf[0] = 0;
482 append_int_param(&asinfo, "clients", IRCstats.clients);
483 append_int_param(&asinfo, "channels", IRCstats.channels);
484 append_int_param(&asinfo, "operators", IRCstats.operators);
485 append_int_param(&asinfo, "servers", IRCstats.servers);
486 append_int_param(&asinfo, "messages", counter);
487
488 i=0;
489
490 list_for_each_entry(acptr, &global_server_list, client_node){
491 if (IsULine(acptr) && HIDE_ULINES)
492 continue;
493 asend_sprintf(&asinfo, "$stats['serv'][%d]['name'] = '%s';\n", i, acptr->name);
494 asend_sprintf(&asinfo, "$stats['serv'][%d]['users'] = %ld;\n", i, acptr->serv->users);
495 i++;
496 }
497
498
499 IRCMutexLock(chans_mutex);
500 if(!chans_mutex_ai) removeExpiredChannels();
501 chans_mutex_ai++;
502 IRCMutexUnlock(chans_mutex);
503 chinfo.chan = NULL;
504
505 i=0;
506 while(getChannelInfo(&chinfo)) {
507 if(!PubChannel(chinfo.chan)) continue;
508 asend_sprintf(&asinfo, "$stats['chan'][%d]['name'] = '%s';\n", i, tmp_escape(name, chinfo.chan->chname));
509 asend_sprintf(&asinfo, "$stats['chan'][%d]['users'] = %d;\n", i, chinfo.chan->users);
510 asend_sprintf(&asinfo, "$stats['chan'][%d]['messages'] = %d;\n", i, chinfo.messages);
511 if(chinfo.chan->topic) asend_sprintf(&asinfo, "$stats['chan'][%d]['topic'] = '%s';\n", i, tmp_escape(topic, chinfo.chan->topic));
512 i++;
513 }
514
515 IRCMutexLock(chans_mutex);
516 chans_mutex_ai--;
517 IRCMutexUnlock(chans_mutex);
518
519 if(send_buf[0]) {
520 send(sock, send_buf, strlen(send_buf), 0);
521 send_buf[0] = 0;
522 }
523
524 close(sock);
525 }
526}
527
528#ifdef USE_MYSQL
529
530void wwwstats_mysql_thr(void *d) {
531 time_t prev_time;
532 time_t act_time;
533 prev_time = 0;
534
535 while(1) {
536 act_time = time(NULL);
537 if((act_time-prev_time)>=mysql_interval) {
538 saveStats(act_time);
539
540 IRCMutexLock(chans_mutex);
541 if(!chans_mutex_ai) removeExpiredChannels();
542 chans_mutex_ai++;
543 IRCMutexUnlock(chans_mutex);
544
545 saveChannels(act_time);
546
547 IRCMutexLock(chans_mutex);
548 chans_mutex_ai--;
549 IRCMutexUnlock(chans_mutex);
550
551 prev_time = act_time;
552 }
553 sleep(10);
554 }
555}
556
557void saveChannels(time_t act_time) {
558 char buf[2*(TOPICLEN+CHANNELLEN)+256];
559 char name[2*CHANNELLEN+1];
560 char topic[2*TOPICLEN+1];
561 channelInfo chinfo;
562
563 if(!use_mysql) return;
564
565 chinfo.chan = NULL;
566 while(getChannelInfo(&chinfo)) {
567 tmp_escape(name, chinfo.chan->chname);
568 if(chinfo.chan->topic) tmp_escape(topic, chinfo.chan->topic);
569 else topic[0] = 0;
570 mysql_query_sprintf(buf, "INSERT IGNORE INTO chanlist VALUES (NULL, %d, '%s', '%s', %d, %d)", act_time, name, topic, chinfo.chan->users, chinfo.messages);
571 }
572}
573
574void loadChannels(void){ // channel will be added automatically when a message comes
575 char buf[2*CHANNELLEN+20];
576 char name[2*CHANNELLEN+1];
577 aChannel *ch;
578 MYSQL_RES *res;
579 MYSQL_ROW row;
580
581 channelInfo chinfo;
582 unsigned long cnt;
583
584 if(!use_mysql) return;
585
586 chinfo.chan = NULL;
587 while(getChannelInfo(&chinfo)) {
588 ch = chinfo.chan;
589 tmp_escape(name, chinfo.chan->chname);
590 mysql_query_sprintf(buf, "SELECT MAX(messages) FROM chanlist WHERE name = '%s'", name);
591 res = mysql_use_result(stats_db);
592 if(res && (row = mysql_fetch_row(res))){
593 cnt = 0;
594 if(row[0]) cnt = strtoul(row[0], NULL, 10);
595 if(cnt > 0){
596 appendChannel(ch, cnt);
597// sendto_realops("wwwstats: added from db: %s, %lu msgs", ch->chname, cnt);
598 }
599 mysql_free_result(res);
600 }
601 }
602}
603
604void saveStats(time_t act_time) {
605 char buf[512];
606
607 if(!use_mysql) return;
608
609 mysql_query_sprintf(buf, "INSERT IGNORE INTO stat VALUES (NULL, %d, %d, %d, %d, %d)", act_time, IRCstats.clients, IRCstats.servers, counter, IRCstats.channels);
610}
611
612void send_mysql_error(void){
613 sendto_realops("wwwstats: mysql error: %s",mysql_error(stats_db));
614}
615#endif
616
617
618void appendChannel(aChannel *ch, int messages) {
619 chanStats *lp;
620
621 lp = malloc(sizeof(chanStats));
622 lp->chan = ch;
623 lp->msg = messages;
624 strcpy(lp->chname, ch->chname);
625 lp->next = NULL;
626 if(chans_last) chans_last->next = lp;
627 chans_last = lp;
628 if(!chans) chans = lp;
629}
630
631void removeExpiredChannels() {
632 int hashnum;
633 aChannel *c;
634 chanStats *lp, *lpprev, *lpnext;
635
636 for(lp=chans; lp; lp=lp->next) lp->exists = 0;
637
638 for(hashnum=0; hashnum<CH_MAX; hashnum++) {
639 c = (aChannel*) hash_get_chan_bucket(hashnum);
640 while(c) {
641 for(lp=chans; lp; lp=lp->next) if(lp->chan==c) break;
642 if(lp) lp->exists = 1;
643 c = c->hnextch;
644 }
645 }
646
647 lpprev = NULL;
648 lpnext = NULL;
649 for(lp=chans; lp; lp=lpnext) {
650 if(!lp->exists) {
651// sendto_realops("wwwstats: deleted channel %s", lp->chname);
652 if(lpprev) lpprev->next = lp->next;
653 else chans = lp->next;
654 if(!lp->next) chans_last = lpprev;
655 lpnext = lp->next;
656 free(lp);
657 continue;
658 }
659 lpnext = lp->next;
660 lpprev = lp;
661 }
662}
663
664aChannel *getChanByName(char *name) {
665 channelInfo chinfo;
666
667 chinfo.chan = NULL;
668 while(getChannelInfo(&chinfo)) {
669 if(strcmp(chinfo.chan->chname, name)==0) return chinfo.chan;
670 }
671 return NULL;
672}
673
674
675
676int getChannelInfo(channelInfo *prev) {
677 int hashnum = 0;
678 int messages = 0;
679 aChannel *c = NULL;
680 chanStats *lp;
681
682 if(prev->chan) {
683 hashnum = prev->hashnum;
684 c = prev->chan->hnextch;
685 if(!c) hashnum++;
686 }
687
688 if(!c) for(; hashnum<CH_MAX; hashnum++) {
689 c = (aChannel*) hash_get_chan_bucket(hashnum);
690 if(c) break;
691 }
692 if(!c) return 0;
693
694 for(lp=chans; lp; lp=lp->next) if(lp->chan==c) break;
695 if(lp) messages = lp->msg;
696
697 prev->hashnum = hashnum;
698 prev->chan = c;
699 prev->messages = messages;
700 return 1;
701}
702
703
704
705#ifdef USE_MYSQL
706int mysql_query_sprintf(char *buf, char *fmt, ...) {
707 int ret;
708 va_list list;
709 va_start(list, fmt);
710 vsprintf(buf, fmt, list);
711 va_end(list);
712 ret = mysql_query(stats_db, buf);
713 if(ret){
714 sendto_realops("wwwstats: mysql query error: %s",mysql_error(stats_db));
715 }
716 return ret;
717}
718
719#endif
720
721void asend_sprintf(asendInfo *info, char *fmt, ...) {
722 int bl, tl;
723 va_list list;
724 va_start(list, fmt);
725 vsprintf(info->tmpbuf, fmt, list);
726 bl = strlen(info->tmpbuf);
727 tl = strlen(info->buf);
728 if((bl+tl)>=info->bufsize) {
729 send(info->sock, info->buf, tl, 0);
730 info->buf[0] = 0;
731 }
732
733 strcat(info->buf, info->tmpbuf);
734 va_end(list);
735}
736
737void append_int_param(asendInfo *info, char *param, int value) {
738 asend_sprintf(info, "$stats['%s'] = %d;\n", param, value);
739}
740
741char *tmp_escape(char *d, const char *a) {
742 int diff;
743 int i;
744 diff = 0;
745 for(i=0; a[i]; i++) {
746 if((a[i]=='\'') || (a[i]=='\\')) {
747 d[diff+i] = '\\';
748 diff++;
749 }
750 d[diff+i] = a[i];
751 }
752 d[diff+i] = 0;
753 return d;
754}