]> jfr.im git - irc/quakenet/newserv.git/blame - chanserv/chanservdb.c
Add jupe support
[irc/quakenet/newserv.git] / chanserv / chanservdb.c
CommitLineData
c86edd1d
Q
1/*
2 * chanservdb.c:
3 * Handles the SQL stuff for the channel service.
4 */
5
6#include "chanserv.h"
7#include <libpq-fe.h>
8#include <string.h>
9#include "../core/config.h"
10#include "../lib/sstring.h"
11#include "../parser/parser.h"
12#include "../core/events.h"
13#include <stdio.h>
14#include <sys/poll.h>
15#include <stdarg.h>
16
17typedef void (*QueryHandler)(void *);
18
19typedef struct qquery {
20 char *query; /* Text of query */
21 QueryHandler handler; /* Handler function */
22 void *udata; /* Other data associated with query */
23 struct qquery *next; /* Next (linked list) */
24} qquery;
25
26struct helpinfo {
27 unsigned int numeric;
28 sstring *commandname;
29};
30
31typedef struct tabledesc {
32 sstring *tablename; /* Name of table */
33 QueryHandler init; /* Function to be called when transaction opens */
34 QueryHandler data; /* Function to be called to load data */
35 QueryHandler fini; /* Function to be called to clean up */
36} tabledesc;
37
38qquery *nextquery, *lastquery;
39
40PGconn *dbconn;
41
42regchan **allchans;
43reguser **allusers;
44
45int sqlconnected;
46unsigned int lastchannelID;
47unsigned int lastuserID;
48unsigned int lastbanID;
49
50/* Local prototypes */
51void loadallchanbans(void *arg);
52void csdb_handler(int fd, short revents);
53void csdb_queuequery(QueryHandler handler, void *udata, char *format, ...);
54void csdb_handlestats(int hooknum, void *arg);
55void loadmessages_part1(void *arg);
56void loadmessages_part2(void *arg);
57void loadcommandsummary_real(void *arg);
58void csdb_dohelp_real(void *arg);
59
60/* Generic loading functions */
61void loadall(char *, QueryHandler, QueryHandler, QueryHandler);
62void doloadall(void *arg);
63
64/* User loading functions */
65void loadsomeusers(void *arg);
66void loadusersdone(void *arg);
67
68/* Channel loading functions */
69void loadsomechannels(void *arg);
70void loadchannelsdone(void *arg);
71
72/* Chanuser loading functions */
73void loadchanusersinit(void *arg);
74void loadsomechanusers(void *arg);
75void loadchanusersdone(void *arg);
76
77/* Chanban loading functions */
78void loadsomechanbans(void *arg);
79void loadchanbansdone(void *arg);
80
81int chanservdbinit() {
82 sstring *dbhost,*dbusername,*dbpassword,*dbdatabase,*dbport;
83 char connectstr[1000];
84
85 sqlconnected=0;
86
87 registerhook(HOOK_CORE_STATSREQUEST, csdb_handlestats);
88
89 dbhost=getcopyconfigitem("chanserv","dbhost","localhost",HOSTLEN);
90 dbusername=getcopyconfigitem("chanserv","dbusername","chanserv",20);
91 dbpassword=getcopyconfigitem("chanserv","dbpassword","moo",20);
92 dbdatabase=getcopyconfigitem("chanserv","dbdatabase","chanserv",20);
93 dbport=getcopyconfigitem("chanserv","dbport","3306",8);
94 sprintf(connectstr,"dbname=%s user=%s password=%s",
95/* dbhost->content, dbport->content, */
96 dbdatabase->content, dbusername->content, dbpassword->content);
97
98 Error("chanserv",ERR_INFO,"Attempting database connection: %s",connectstr);
99
100 /* Blocking connect for now.. */
101 dbconn=PQconnectdb(connectstr);
102
103 if (!dbconn || (PQstatus(dbconn)!=CONNECTION_OK)) {
104 Error("chanserv",ERR_ERROR,"Unable to connect to database!");
105 return 1;
106 }
107
108 freesstring(dbhost);
109 freesstring(dbusername);
110 freesstring(dbpassword);
111 freesstring(dbdatabase);
112 freesstring(dbport);
113
114 sqlconnected=1;
115
116 /* Set up the tables */
117 /* User table */
118 PQclear(PQexec(dbconn,"CREATE TABLE users ("
119 "ID INT NOT NULL,"
120 "username VARCHAR(16) NOT NULL,"
121 "created INT NOT NULL,"
122 "lastauth INT NOT NULL,"
123 "lastemailchng INT NOT NULL,"
124 "flags INT NOT NULL,"
125 "language INT NOT NULL,"
126 "suspendby INT NOT NULL,"
127 "suspendexp INT NOT NULL,"
128 "password VARCHAR(11) NOT NULL,"
129 "masterpass VARCHAR(11),"
130 "email VARCHAR(100),"
131 "lastuserhost VARCHAR(75),"
132 "suspendreason VARCHAR(250),"
133 "comment VARCHAR(250),"
134 "info VARCHAR(100),"
135 "PRIMARY KEY (ID))"));
136
137 PQclear(PQexec(dbconn,"CREATE INDEX user_username_index ON users (username)"));
138
139 /* Channel table */
140 PQclear(PQexec(dbconn,"CREATE TABLE channels ("
141 "ID INT NOT NULL,"
142 "name VARCHAR(250) NOT NULL,"
143 "flags INT NOT NULL,"
144 "forcemodes INT NOT NULL,"
145 "denymodes INT NOT NULL,"
146 "chanlimit INT NOT NULL,"
147 "autolimit INT NOT NULL,"
148 "banstyle INT NOT NULL,"
149 "created INT NOT NULL,"
150 "lastactive INT NOT NULL,"
151 "statsreset INT NOT NULL,"
152 "banduration INT NOT NULL,"
153 "founder INT NOT NULL,"
154 "addedby INT NOT NULL,"
155 "suspendby INT NOT NULL,"
156 "chantype SMALLINT NOT NULL,"
157 "totaljoins INT NOT NULL,"
158 "tripjoins INT NOT NULL,"
159 "maxusers INT NOT NULL,"
160 "tripusers INT NOT NULL,"
161 "welcome VARCHAR(500),"
162 "topic VARCHAR(250),"
163 "chankey VARCHAR(23),"
164 "suspendreason VARCHAR(250),"
165 "comment VARCHAR(250),"
166 "PRIMARY KEY (ID))"));
167
168 /* Chanuser table */
169 PQclear(PQexec(dbconn,"CREATE TABLE chanusers ("
170 "userID INT NOT NULL,"
171 "channelID INT NOT NULL,"
172 "flags INT NOT NULL,"
173 "changetime INT NOT NULL,"
174 "usetime INT NOT NULL,"
175 "info VARCHAR(100) NOT NULL,"
176 "PRIMARY KEY (userID, channelID))"));
177
178 PQclear(PQexec(dbconn,"CREATE INDEX chanusers_userID_index on chanusers (userID)"));
179 PQclear(PQexec(dbconn,"CREATE INDEX chanusers_channelID_index on chanusers (channelID)"));
180
181 PQclear(PQexec(dbconn,"CREATE TABLE bans ("
182 "banID INT NOT NULL," /* Unique number for the ban to make
183 DELETEs process in finite time.. */
184 "channelID INT NOT NULL,"
185 "userID INT NOT NULL," /* Who set the ban.. */
186 "hostmask VARCHAR(100) NOT NULL," /* needs to be at least USERLEN+NICKLEN+HOSTLEN+2 */
187 "expiry INT NOT NULL,"
188 "reason VARCHAR(200),"
189 "PRIMARY KEY(banID))"));
190
191 PQclear(PQexec(dbconn,"CREATE INDEX bans_channelID_index on bans (channelID)"));
192
193 PQclear(PQexec(dbconn,"CREATE TABLE languages ("
194 "languageID INT NOT NULL,"
195 "code VARCHAR(2) NOT NULL,"
196 "name VARCHAR(30) NOT NULL)"));
197
198 PQclear(PQexec(dbconn,"CREATE TABLE messages ("
199 "languageID INT NOT NULL,"
200 "messageID INT NOT NULL,"
201 "message VARCHAR(250) NOT NULL,"
202 "PRIMARY KEY (languageID, messageID))"));
203
204 PQclear(PQexec(dbconn,"CREATE TABLE help ("
205 "commandID INT NOT NULL,"
206 "command VARCHAR(30) NOT NULL,"
207 "languageID INT NOT NULL,"
208 "summary VARCHAR(200) NOT NULL,"
209 "fullinfo TEXT NOT NULL,"
210 "PRIMARY KEY (commandID, languageID))"));
211
212 PQclear(PQexec(dbconn,"CREATE TABLE email ("
213 "userID INT NOT NULL,"
214 "emailtype INT NOT NULL,"
215 "prevEmail VARCHAR(100),"
216 "mailID VARCHAR(128))"));
217
218 PQsetnonblocking(dbconn, 1);
219
220 registerhandler(PQsocket(dbconn), POLLIN, csdb_handler);
221
222 lastuserID=lastchannelID=0;
223
224 loadall("users",NULL,loadsomeusers,loadusersdone);
225 loadall("channels",NULL,loadsomechannels,loadchannelsdone);
226 loadall("chanusers",loadchanusersinit,loadsomechanusers,loadchanusersdone);
227 loadall("bans",NULL,loadsomechanbans,loadchanbansdone);
228
229 loadmessages();
230
231 return 0;
232}
233
234void csdb_handler(int fd, short revents) {
235 PGresult *res;
236 qquery *qqp;
237
238 if (revents & POLLIN) {
239 PQconsumeInput(dbconn);
240
241 if (!PQisBusy(dbconn)) {
242 /* Query is complete */
243 if (nextquery->handler)
244 (nextquery->handler)(nextquery->udata);
245
246 while ((res=PQgetResult(dbconn))) {
247 switch (PQresultStatus(res)) {
248 case PGRES_TUPLES_OK:
249 Error("chanserv",ERR_WARNING,"Unhandled tuples output (query=%s)",nextquery->query);
250 break;
251
252 case PGRES_NONFATAL_ERROR:
253 case PGRES_FATAL_ERROR:
254 Error("chanserv",ERR_WARNING,"Unhandled error response (query=%s)",nextquery->query);
255 break;
256
257 default:
258 break;
259 }
260
261 PQclear(res);
262 }
263
264 /* Free the query and advance */
265 qqp=nextquery;
266 if (nextquery==lastquery) {
267 lastquery=NULL;
268 }
269 nextquery=nextquery->next;
270 free(qqp->query);
271 free(qqp);
272
273 if (nextquery) {
274 /* Submit the next query */
275 PQsendQuery(dbconn, nextquery->query);
276 PQflush(dbconn);
277 }
278 }
279 }
280}
281
282void csdb_queuequery(QueryHandler handler, void *udata, char *format, ...) {
283 char querybuf[8192];
284 va_list va;
285 int len;
286 qquery *qp;
287
288 va_start(va, format);
289 len=vsnprintf(querybuf, 8191, format, va);
290 va_end(va);
291
292 qp=(qquery *)malloc(sizeof(qquery));
293 qp->query=malloc(len+1);
294
295 strcpy(qp->query, querybuf);
296 qp->udata=udata;
297 qp->handler=handler;
298 qp->next=NULL;
299
300 if (lastquery) {
301 lastquery->next=qp;
302 lastquery=qp;
303 } else {
304 lastquery=nextquery=qp;
305 PQsendQuery(dbconn, nextquery->query);
306 PQflush(dbconn);
307 }
308}
309
310void csdb_handlestats(int hooknum, void *arg) {
311 int level=(int)arg;
312 int i=0;
313 qquery *qqp;
314 char message[100];
315
316 if (level>10) {
317 if (nextquery)
318 for (qqp=nextquery;qqp;qqp=qqp->next)
319 i++;
320
321 sprintf(message,"ChanServ: %6d database queries queued.",i);
322
323 triggerhook(HOOK_CORE_STATSREPLY, message);
324 }
325}
326
327void chanservdbclose() {
328 qquery *qqp, *nqqp;
329
330 if (sqlconnected) {
331 deregisterhandler(PQsocket(dbconn), 0);
332 }
333
334 /* Throw all the queued queries away.. */
335 for (qqp=nextquery;qqp;qqp=nqqp) {
336 nqqp=qqp->next;
337 if (qqp->handler == csdb_dohelp_real) {
338 free(qqp->udata);
339 }
340 free(qqp->query);
341 free(qqp);
342 }
343
344 deregisterhook(HOOK_CORE_STATSREQUEST, csdb_handlestats);
345 PQfinish(dbconn);
346}
347
348/*
349 * loadall():
350 * Generic function to handle load of an entire table..
351 */
352
353void loadall(char *table, QueryHandler init, QueryHandler data, QueryHandler fini) {
354 tabledesc *thedesc;
355
356 thedesc=malloc(sizeof(tabledesc));
357
358 thedesc->tablename=getsstring(table,100);
359 thedesc->init=init;
360 thedesc->data=data;
361 thedesc->fini=fini;
362
363 csdb_queuequery(doloadall, thedesc, "SELECT count(*) FROM %s",thedesc->tablename->content);
364}
365
366void doloadall(void *arg) {
367 PGresult *pgres;
368 int i,count;
369 tabledesc *thedesc=arg;
370
371 pgres=PQgetResult(dbconn);
372
373 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
374 Error("chanserv",ERR_ERROR,"Error getting row count for %s.",thedesc->tablename->content);
375 return;
376 }
377
378 if (PQnfields(pgres)!=1) {
379 Error("chanserv",ERR_ERROR,"Count query format error for %s.",thedesc->tablename->content);
380 return;
381 }
382
383 count=strtoul(PQgetvalue(pgres,0,0),NULL,10);
384
385 PQclear(pgres);
386
387 Error("chanserv",ERR_INFO,"Found %d entries in table %s, scheduling load.",count,
388 thedesc->tablename->content);
389
390 csdb_queuequery(thedesc->init, NULL, "BEGIN");
391 csdb_queuequery(NULL, NULL, "DECLARE mycurs CURSOR FOR SELECT * from %s",
392 thedesc->tablename->content);
393
394 for (i=0;(count-i)>1000;i+=1000) {
395 csdb_queuequery(thedesc->data, NULL, "FETCH 1000 FROM mycurs");
396 }
397
398 csdb_queuequery(thedesc->data, NULL, "FETCH ALL FROM mycurs");
399
400 csdb_queuequery(NULL, NULL, "CLOSE mycurs");
401 csdb_queuequery(thedesc->fini, NULL, "COMMIT");
402
403 /* Free structures.. */
404 freesstring(thedesc->tablename);
405 free(thedesc);
406}
407
408/*
409 * loadsomeusers():
410 * Loads some users in from the SQL DB
411 */
412
413void loadsomeusers(void *arg) {
414 PGresult *pgres;
415 reguser *rup;
416 unsigned int i,num;
417
418 pgres=PQgetResult(dbconn);
419
420 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
421 Error("chanserv",ERR_ERROR,"Error loading user DB");
422 return;
423 }
424
425 if (PQnfields(pgres)!=16) {
426 Error("chanserv",ERR_ERROR,"User DB format error");
427 return;
428 }
429
430 num=PQntuples(pgres);
431
432 lastuserID=0;
433
434 for(i=0;i<num;i++) {
435 rup=getreguser();
436 rup->status=0;
437 rup->ID=strtoul(PQgetvalue(pgres,i,0),NULL,10);
438 strncpy(rup->username,PQgetvalue(pgres,i,1),NICKLEN); rup->username[NICKLEN]='\0';
439 rup->created=strtoul(PQgetvalue(pgres,i,2),NULL,10);
440 rup->lastauth=strtoul(PQgetvalue(pgres,i,3),NULL,10);
441 rup->lastemailchange=strtoul(PQgetvalue(pgres,i,4),NULL,10);
442 rup->flags=strtoul(PQgetvalue(pgres,i,5),NULL,10);
443 rup->languageid=strtoul(PQgetvalue(pgres,i,6),NULL,10);
444 rup->suspendby=strtoul(PQgetvalue(pgres,i,7),NULL,10);
445 rup->suspendexp=strtoul(PQgetvalue(pgres,i,8),NULL,10);
446 strncpy(rup->password,PQgetvalue(pgres,i,9),PASSLEN); rup->password[PASSLEN]='\0';
447 strncpy(rup->masterpass,PQgetvalue(pgres,i,10),PASSLEN); rup->masterpass[PASSLEN]='\0';
448 rup->email=getsstring(PQgetvalue(pgres,i,11),100);
449 rup->lastuserhost=getsstring(PQgetvalue(pgres,i,12),75);
450 rup->suspendreason=getsstring(PQgetvalue(pgres,i,13),250);
451 rup->comment=getsstring(PQgetvalue(pgres,i,14),250);
452 rup->info=getsstring(PQgetvalue(pgres,i,15),100);
453 rup->knownon=NULL;
454 rup->nicks=NULL;
455 rup->checkshd=NULL;
456 rup->stealcount=0;
457 rup->fakeuser=NULL;
458 addregusertohash(rup);
459
460 if (rup->ID > lastuserID) {
461 lastuserID=rup->ID;
462 }
463 }
464
465 PQclear(pgres);
466}
467
468void loadusersdone(void *arg) {
469 Error("chanserv",ERR_INFO,"Load users done (highest ID was %d)",lastuserID);
470}
471
472/*
473 * Channel loading functions
474 */
475
476void loadsomechannels(void *arg) {
477 PGresult *pgres;
478 regchan *rcp;
479 int i,num;
480 chanindex *cip;
481 time_t now=time(NULL);
482
483 pgres=PQgetResult(dbconn);
484
485 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
486 Error("chanserv",ERR_ERROR,"Error loading channel DB");
487 return;
488 }
489
490 if (PQnfields(pgres)!=25) {
491 Error("chanserv",ERR_ERROR,"Channel DB format error");
492 return;
493 }
494
495 num=PQntuples(pgres);
496
497 for(i=0;i<num;i++) {
498 cip=findorcreatechanindex(PQgetvalue(pgres,i,1));
499 if (cip->exts[chanservext]) {
500 Error("chanserv",ERR_WARNING,"%s in database twice - this WILL cause problems later.",cip->name->content);
501 continue;
502 }
503 rcp=getregchan();
504 cip->exts[chanservext]=rcp;
505
506 rcp->ID=strtoul(PQgetvalue(pgres,i,0),NULL,10);
507 rcp->index=cip;
508 rcp->flags=strtoul(PQgetvalue(pgres,i,2),NULL,10);
509 rcp->status=0; /* Non-DB field */
510 rcp->lastbancheck=0;
511 rcp->lastcountersync=now;
512 rcp->lastpart=0;
513 rcp->bans=NULL;
514 rcp->forcemodes=strtoul(PQgetvalue(pgres,i,3),NULL,10);
515 rcp->denymodes=strtoul(PQgetvalue(pgres,i,4),NULL,10);
516 rcp->limit=strtoul(PQgetvalue(pgres,i,5),NULL,10);
517 rcp->autolimit=strtoul(PQgetvalue(pgres,i,6),NULL,10);
518 rcp->banstyle=strtoul(PQgetvalue(pgres,i,7),NULL,10);
519 rcp->created=strtoul(PQgetvalue(pgres,i,8),NULL,10);
520 rcp->lastactive=strtoul(PQgetvalue(pgres,i,9),NULL,10);
521 rcp->statsreset=strtoul(PQgetvalue(pgres,i,10),NULL,10);
522 rcp->banduration=strtoul(PQgetvalue(pgres,i,11),NULL,10);
523 rcp->founder=strtol(PQgetvalue(pgres,i,12),NULL,10);
524 rcp->addedby=strtol(PQgetvalue(pgres,i,13),NULL,10);
525 rcp->suspendby=strtol(PQgetvalue(pgres,i,14),NULL,10);
526 rcp->chantype=strtoul(PQgetvalue(pgres,i,15),NULL,10);
527 rcp->totaljoins=strtoul(PQgetvalue(pgres,i,16),NULL,10);
528 rcp->tripjoins=strtoul(PQgetvalue(pgres,i,17),NULL,10);
529 rcp->maxusers=strtoul(PQgetvalue(pgres,i,18),NULL,10);
530 rcp->tripusers=strtoul(PQgetvalue(pgres,i,19),NULL,10);
531 rcp->welcome=getsstring(PQgetvalue(pgres,i,20),500);
532 rcp->topic=getsstring(PQgetvalue(pgres,i,21),TOPICLEN);
533 rcp->key=getsstring(PQgetvalue(pgres,i,22),KEYLEN);
534 rcp->suspendreason=getsstring(PQgetvalue(pgres,i,23),250);
535 rcp->comment=getsstring(PQgetvalue(pgres,i,24),250);
536 rcp->checksched=NULL;
537 memset(rcp->regusers,0,REGCHANUSERHASHSIZE*sizeof(reguser *));
538
539 if (rcp->ID > lastchannelID)
540 lastchannelID=rcp->ID;
541
542 if (CIsAutoLimit(rcp))
543 rcp->limit=0;
544 }
545
546 PQclear(pgres);
547}
548
549void loadchannelsdone(void *arg) {
550 Error("chanserv",ERR_INFO,"Channel load done (highest ID was %d)",lastchannelID);
551}
552
553void loadchanusersinit(void *arg) {
554 int i;
555 chanindex *cip;
556 reguser *rup;
557 regchan *rcp;
558
559 allchans=(regchan **)malloc((lastchannelID+1)*sizeof(regchan *));
560 memset(allchans,0,(lastchannelID+1)*sizeof(regchan *));
561 for (i=0;i<CHANNELHASHSIZE;i++) {
562 for (cip=chantable[i];cip;cip=cip->next) {
563 if (cip->exts[chanservext]) {
564 rcp=(regchan *)cip->exts[chanservext];
565 allchans[rcp->ID]=rcp;
566 }
567 }
568 }
569
570 allusers=(reguser **)malloc((lastuserID+1)*sizeof(reguser *));
571 memset(allusers,0,(lastuserID+1)*sizeof(reguser *));
572 for (i=0;i<REGUSERHASHSIZE;i++) {
573 for (rup=regusernicktable[i];rup;rup=rup->nextbyname) {
574 allusers[rup->ID]=rup;
575 }
576 }
577}
578
579void loadsomechanusers(void *arg) {
580 PGresult *pgres;
581 regchanuser *rcup;
582 int i,num;
583 regchan *rcp;
584 reguser *rup;
585 int uid,cid;
586 int total=0;
587
588 /* Set up the allchans and allusers arrays */
589 pgres=PQgetResult(dbconn);
590
591 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
592 Error("chanserv",ERR_ERROR,"Error loading chanusers.");
593 return;
594 }
595
596 if (PQnfields(pgres)!=6) {
597 Error("chanserv",ERR_ERROR,"Chanusers format error");
598 return;
599 }
600
601 num=PQntuples(pgres);
602
603 for(i=0;i<num;i++) {
604 uid=strtol(PQgetvalue(pgres,i,0),NULL,10);
605 cid=strtol(PQgetvalue(pgres,i,1),NULL,10);
606
607 if (uid>lastuserID || !(rup=allusers[uid])) {
608 Error("chanserv",ERR_WARNING,"Skipping channeluser for unknown user %d",uid);
609 continue;
610 }
611
612 if (cid>lastchannelID || !(rcp=allchans[cid])) {
613 Error("chanserv",ERR_WARNING,"Skipping channeluser for unknown chan %d",cid);
614 continue;
615 }
616
617 if (rup==NULL || rcp==NULL) {
618 Error("chanserv",ERR_ERROR,"Can't add user %s on channel %s",
619 PQgetvalue(pgres,i,0),PQgetvalue(pgres,i,1));
620 } else {
621 rcup=getregchanuser();
622 rcup->user=rup;
623 rcup->chan=rcp;
624 rcup->flags=strtol(PQgetvalue(pgres,i,2),NULL,10);
625 rcup->changetime=strtol(PQgetvalue(pgres,i,3),NULL,10);
626 rcup->usetime=strtol(PQgetvalue(pgres,i,4),NULL,10);
627 rcup->info=getsstring(PQgetvalue(pgres,i,5),100);
628 addregusertochannel(rcup);
629 total++;
630 }
631 }
632
633 PQclear(pgres);
634}
635
636void loadchanusersdone(void *arg) {
637 Error("chanserv",ERR_INFO,"Channel user load done.");
638}
639
640void loadsomechanbans(void *arg) {
641 PGresult *pgres;
642 regban *rbp;
643 int i,num;
644 regchan *rcp;
645 int uid,cid,bid;
646 time_t expiry,now;
647 int total=0;
648
649 pgres=PQgetResult(dbconn);
650
651 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
652 Error("chanserv",ERR_ERROR,"Error loading bans.");
653 return;
654 }
655
656 if (PQnfields(pgres)!=6) {
657 Error("chanserv",ERR_ERROR,"Ban format error");
658 return;
659 }
660
661 num=PQntuples(pgres);
662
663 now=time(NULL);
664
665 for(i=0;i<num;i++) {
666 bid=strtoul(PQgetvalue(pgres,i,0),NULL,10);
667 cid=strtoul(PQgetvalue(pgres,i,1),NULL,10);
668 uid=strtoul(PQgetvalue(pgres,i,2),NULL,10);
669 expiry=strtoul(PQgetvalue(pgres,i,4),NULL,10);
670
671 if (cid>lastchannelID || !(rcp=allchans[cid])) {
672 Error("chanserv",ERR_WARNING,"Skipping ban for unknown chan %d",cid);
673 continue;
674 }
675
676 rbp=getregban();
677 rbp->setby=uid;
678 rbp->ID=bid;
679 rbp->expiry=expiry;
680 rbp->reason=getsstring(PQgetvalue(pgres,i,5),200);
681 rbp->cbp=makeban(PQgetvalue(pgres,i,3));
682 rbp->next=rcp->bans;
683 rcp->bans=rbp;
684
685 total++;
686
687 if (bid>lastbanID)
688 lastbanID=bid;
689 }
690
691 PQclear(pgres);
692}
693
694void loadchanbansdone(void *arg) {
695 free(allusers);
696 free(allchans);
697
698 Error("chanserv",ERR_INFO,"Channel ban load done.");
699
700 triggerhook(HOOK_CHANSERV_DBLOADED, NULL);
701}
702
703void loadmessages() {
704 csdb_queuequery(loadmessages_part1, NULL, "SELECT * from languages");
705}
706
707void loadmessages_part1(void *arg) {
708 int i,j;
709 PGresult *pgres;
710 int num;
711
712 /* Firstly, clear up any stale messages */
713 for (i=0;i<MAXLANG;i++) {
714 if (cslanguages[i]) {
715 freesstring(cslanguages[i]->name);
716 free(cslanguages[i]);
717 }
718 for (j=0;j<MAXMESSAGES;j++) {
719 if (csmessages[i][j]) {
720 freesstring(csmessages[i][j]);
721 csmessages[i][j]=NULL;
722 }
723 }
724 }
725
726 pgres=PQgetResult(dbconn);
727
728 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
729 Error("chanserv",ERR_ERROR,"Error loading language list.");
730 return;
731 }
732
733 if (PQnfields(pgres)!=3) {
734 Error("chanserv",ERR_ERROR,"Language list format error.");
735 return;
736 }
737
738 num=PQntuples(pgres);
739
740 for (i=0;i<num;i++) {
741 j=strtol(PQgetvalue(pgres,i,0),NULL,10);
742 if (j<MAXLANG && j>=0) {
743 cslanguages[j]=(cslang *)malloc(sizeof(cslang));
744
745 strncpy(cslanguages[j]->code,PQgetvalue(pgres,i,1),2); cslanguages[j]->code[2]='\0';
746 cslanguages[j]->name=getsstring(PQgetvalue(pgres,i,2),30);
747 }
748 }
749
750 PQclear(pgres);
751
752 if (i>MAXLANG)
753 Error("chanserv",ERR_ERROR,"Found too many languages (%d > %d)",i,MAXLANG);
754
755 csdb_queuequery(loadmessages_part2, NULL, "SELECT * from messages");
756}
757
758void loadmessages_part2(void *arg) {
759 PGresult *pgres;
760 int i,j,k,num;
761
762 pgres=PQgetResult(dbconn);
763
764 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
765 Error("chanserv",ERR_ERROR,"Error loading message list.");
766 return;
767 }
768
769 if (PQnfields(pgres)!=3) {
770 Error("chanserv",ERR_ERROR,"Message list format error.");
771 return;
772 }
773
774 num=PQntuples(pgres);
775
776 for (i=0;i<num;i++) {
777 k=strtol(PQgetvalue(pgres,i,0),NULL,10);
778 j=strtol(PQgetvalue(pgres,i,1),NULL,10);
779
780 if (k<0 || k >= MAXLANG) {
781 Error("chanserv",ERR_WARNING,"Language ID out of range on message: %d",k);
782 continue;
783 }
784
785 if (j<0 || j >= MAXMESSAGES) {
786 Error("chanserv",ERR_WARNING,"Message ID out of range on message: %d",j);
787 continue;
788 }
789
790 csmessages[k][j]=getsstring(PQgetvalue(pgres,i,2),250);
791 }
792
793 PQclear(pgres);
794}
795
796void loadcommandsummary(Command *cmd) {
797 csdb_queuequery(loadcommandsummary_real, (void *)cmd,
798 "SELECT languageID,summary from help where lower(command) = lower('%s')",cmd->command->content);
799}
800
801void loadcommandsummary_real(void *arg) {
802 int i,j,num;
803 PGresult *pgres;
804 cmdsummary *cs;
805 Command *cmd=arg;
806
807 /* Clear up old text first */
808 cs=cmd->ext;
809 for (i=0;i<MAXLANG;i++) {
810 if (cs->bylang[i])
811 freesstring(cs->bylang[i]);
812 }
813
814 pgres=PQgetResult(dbconn);
815
816 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
817 Error("chanserv",ERR_ERROR,"Error loading command summary.");
818 return;
819 }
820
821 if (PQnfields(pgres)!=2) {
822 Error("chanserv",ERR_ERROR,"Command summary format error.");
823 return;
824 }
825
826 num=PQntuples(pgres);
827
828 for (i=0;i<num;i++) {
829 j=strtol(PQgetvalue(pgres,i,0),NULL,10);
830 if (j<MAXLANG && j>=0) {
831 cs->bylang[j]=getsstring(PQgetvalue(pgres,i,1),200);
832 }
833 }
834
835 PQclear(pgres);
836}
837
838void csdb_updateauthinfo(reguser *rup) {
839 char eschost[150];
840
841 PQescapeString(eschost,rup->lastuserhost->content,rup->lastuserhost->length);
842 csdb_queuequery(NULL, NULL, "UPDATE users SET lastauth=%lu,lastuserhost='%s' WHERE ID=%u",
843 rup->lastauth,eschost,rup->ID);
844}
845
846void csdb_updatelastjoin(regchanuser *rcup) {
847 csdb_queuequery(NULL, NULL, "UPDATE chanusers SET usetime=%lu WHERE userID=%u and channelID=%u",
848 rcup->usetime, rcup->user->ID, rcup->chan->ID);
849}
850
851void csdb_updatetopic(regchan *rcp) {
852 char esctopic[TOPICLEN*2+5];
853
854 if (rcp->topic) {
855 PQescapeString(esctopic,rcp->topic->content,rcp->topic->length);
856 } else {
857 esctopic[0]='\0';
858 }
859 csdb_queuequery(NULL, NULL, "UPDATE channels SET topic='%s' WHERE ID=%u",esctopic,rcp->ID);
860}
861
862void csdb_updatechannel(regchan *rcp) {
863 char escwelcome[510];
864 char esctopic[510];
865 char esckey[70];
866 char escreason[510];
867 char esccomment[510];
868 char escname[1000];
869
870 PQescapeString(escname, rcp->index->name->content, rcp->index->name->length);
871
872 if (rcp->welcome)
873 PQescapeString(escwelcome, rcp->welcome->content,
874 rcp->welcome->length);
875 else
876 escwelcome[0]='\0';
877
878 if (rcp->topic)
879 PQescapeString(esctopic, rcp->topic->content, rcp->topic->length);
880 else
881 esctopic[0]='\0';
882
883 if (rcp->key)
884 PQescapeString(esckey, rcp->key->content, rcp->key->length);
885 else
886 esckey[0]='\0';
887
888 if (rcp->suspendreason)
889 PQescapeString(escreason, rcp->suspendreason->content,
890 rcp->suspendreason->length);
891 else
892 escreason[0]='\0';
893
894 if (rcp->comment)
895 PQescapeString(esccomment, rcp->comment->content,
896 rcp->comment->length);
897 else
898 esccomment[0]='\0';
899
900 csdb_queuequery(NULL, NULL, "UPDATE channels SET name='%s', flags=%d, forcemodes=%d,"
901 "denymodes=%d, chanlimit=%d, autolimit=%d, banstyle=%d,"
902 "lastactive=%lu,statsreset=%lu, banduration=%lu, founder=%u,"
903 "addedby=%u, suspendby=%u, chantype=%d, totaljoins=%u,"
904 "tripjoins=%u, maxusers=%u, tripusers=%u,"
905 "welcome='%s', topic='%s', chankey='%s', suspendreason='%s',"
906 "comment='%s' WHERE ID=%u",escname,rcp->flags,rcp->forcemodes,
907 rcp->denymodes,rcp->limit,rcp->autolimit, rcp->banstyle,
908 rcp->lastactive,rcp->statsreset,rcp->banduration,
909 rcp->founder, rcp->addedby, rcp->suspendby,
910 rcp->chantype,rcp->totaljoins,rcp->tripjoins,
911 rcp->maxusers,rcp->tripusers,
912 escwelcome,esctopic,esckey,escreason,esccomment,rcp->ID);
913}
914
915void csdb_updatechannelcounters(regchan *rcp) {
916 csdb_queuequery(NULL, NULL, "UPDATE channels SET "
917 "lastactive=%lu, totaljoins=%u,"
918 "tripjoins=%u, maxusers=%u, tripusers=%u "
919 "WHERE ID=%u",
920 rcp->lastactive,
921 rcp->totaljoins,rcp->tripjoins,
922 rcp->maxusers,rcp->tripusers,
923 rcp->ID);
924}
925
926void csdb_createchannel(regchan *rcp) {
927 char escwelcome[510];
928 char esctopic[510];
929 char esckey[70];
930 char escreason[510];
931 char esccomment[510];
932 char escname[510];
933
934 PQescapeString(escname, rcp->index->name->content, rcp->index->name->length);
935
936 if (rcp->welcome)
937 PQescapeString(escwelcome, rcp->welcome->content,
938 rcp->welcome->length);
939 else
940 escwelcome[0]='\0';
941
942 if (rcp->topic)
943 PQescapeString(esctopic, rcp->topic->content, rcp->topic->length);
944 else
945 esctopic[0]='\0';
946
947 if (rcp->key)
948 PQescapeString(esckey, rcp->key->content, rcp->key->length);
949 else
950 esckey[0]='\0';
951
952 if (rcp->suspendreason)
953 PQescapeString(escreason, rcp->suspendreason->content,
954 rcp->suspendreason->length);
955 else
956 escreason[0]='\0';
957
958 if (rcp->comment)
959 PQescapeString(esccomment, rcp->comment->content,
960 rcp->comment->length);
961 else
962 esccomment[0]='\0';
963
964 csdb_queuequery(NULL, NULL, "INSERT INTO channels (ID, name, flags, forcemodes, denymodes,"
965 "chanlimit, autolimit, banstyle, created, lastactive, statsreset, "
966 "banduration, founder, addedby, suspendby, chantype, totaljoins, tripjoins,"
967 "maxusers, tripusers, welcome, topic, chankey, suspendreason, "
968 "comment) VALUES (%u,'%s',%d,%d,%d,%d,%d,%d,%lu,%lu,%lu,%lu,%u,"
969 "%u,%u,%d,%u,%u,%u,%u,'%s','%s','%s','%s','%s')",
970 rcp->ID, escname, rcp->flags,rcp->forcemodes,
971 rcp->denymodes,rcp->limit,rcp->autolimit, rcp->banstyle, rcp->created,
972 rcp->lastactive,rcp->statsreset,rcp->banduration,
973 rcp->founder, rcp->addedby, rcp->suspendby,
974 rcp->chantype,rcp->totaljoins,rcp->tripjoins,
975 rcp->maxusers,rcp->tripusers,
976 escwelcome,esctopic,esckey,escreason,esccomment);
977}
978
979void csdb_deletechannel(regchan *rcp) {
980 csdb_queuequery(NULL,NULL,"DELETE FROM channels WHERE ID=%u",rcp->ID);
981 csdb_queuequery(NULL,NULL,"DELETE FROM chanusers WHERE channelID=%u",rcp->ID);
982 csdb_queuequery(NULL,NULL,"DELETE FROM bans WHERE channelID=%u",rcp->ID);
983}
984
985void csdb_deleteuser(reguser *rup) {
986 csdb_queuequery(NULL,NULL,"DELETE FROM users WHERE ID=%u",rup->ID);
987 csdb_queuequery(NULL,NULL,"DELETE FROM chanusers WHERE userID=%u",rup->ID);
988}
989
990void csdb_updateuser(reguser *rup) {
991 char escpassword[25];
992 char escmasterpass[25];
993 char escemail[210];
994 char esclastuserhost[160];
995 char escreason[510];
996 char esccomment[510];
997 char escinfo[210];
998
999 PQescapeString(escpassword, rup->password, strlen(rup->password));
1000 PQescapeString(escmasterpass, rup->masterpass, strlen(rup->masterpass));
1001
1002 if (rup->email)
1003 PQescapeString(escemail, rup->email->content, rup->email->length);
1004 else
1005 escemail[0]='\0';
1006
1007 if (rup->lastuserhost)
1008 PQescapeString(esclastuserhost, rup->lastuserhost->content, rup->lastuserhost->length);
1009 else
1010 esclastuserhost[0]='\0';
1011
1012 if (rup->suspendreason)
1013 PQescapeString(escreason, rup->suspendreason->content, rup->suspendreason->length);
1014 else
1015 escreason[0]='\0';
1016
1017 if (rup->comment)
1018 PQescapeString(esccomment, rup->comment->content, rup->comment->length);
1019 else
1020 esccomment[0]='\0';
1021
1022 if (rup->info)
1023 PQescapeString(escinfo, rup->info->content, rup->info->length);
1024 else
1025 escinfo[0]='\0';
1026
1027 csdb_queuequery(NULL, NULL, "UPDATE users SET lastauth=%lu, lastemailchng=%lu, flags=%u,"
1028 "language=%u, suspendby=%u, suspendexp=%lu, password='%s', masterpass='%s', email='%s',"
1029 "lastuserhost='%s', suspendreason='%s', comment='%s', info='%s' WHERE ID=%u",
1030 rup->lastauth, rup->lastemailchange, rup->flags, rup->languageid, rup->suspendby, rup->suspendexp,
1031 escpassword, escmasterpass, escemail, esclastuserhost, escreason, esccomment, escinfo,
1032 rup->ID);
1033}
1034
1035void csdb_createuser(reguser *rup) {
1036 char escpassword[25];
1037 char escmasterpass[25];
1038 char escemail[210];
1039 char esclastuserhost[160];
1040 char escreason[510];
1041 char esccomment[510];
1042 char escusername[35];
1043 char escinfo[210];
1044
1045 PQescapeString(escusername, rup->username, strlen(rup->username));
1046 PQescapeString(escpassword, rup->password, strlen(rup->password));
1047 PQescapeString(escmasterpass, rup->masterpass, strlen(rup->masterpass));
1048
1049 if (rup->email)
1050 PQescapeString(escemail, rup->email->content, rup->email->length);
1051 else
1052 escemail[0]='\0';
1053
1054 if (rup->lastuserhost)
1055 PQescapeString(esclastuserhost, rup->lastuserhost->content, rup->lastuserhost->length);
1056 else
1057 esclastuserhost[0]='\0';
1058
1059 if (rup->suspendreason)
1060 PQescapeString(escreason, rup->suspendreason->content, rup->suspendreason->length);
1061 else
1062 escreason[0]='\0';
1063
1064 if (rup->comment)
1065 PQescapeString(esccomment, rup->comment->content, rup->comment->length);
1066 else
1067 esccomment[0]='\0';
1068
1069 if (rup->info)
1070 PQescapeString(escinfo, rup->info->content, rup->info->length);
1071 else
1072 escinfo[0]='\0';
1073
1074 csdb_queuequery(NULL, NULL, "INSERT INTO users (ID, username, created, lastauth, lastemailchng, "
1075 "flags, language, suspendby, suspendexp, password, masterpass, email, lastuserhost, "
1076 "suspendreason, comment, info) VALUES (%u,'%s',%lu,%lu,%lu,%u,%u,%u,%lu,'%s','%s',"
1077 "'%s','%s','%s','%s','%s')",
1078 rup->ID, escusername, rup->created, rup->lastauth, rup->lastemailchange, rup->flags,
1079 rup->languageid, rup->suspendby, rup->suspendexp,
1080 escpassword, escmasterpass, escemail, esclastuserhost, escreason, esccomment, escinfo);
1081}
1082
1083
1084void csdb_updatechanuser(regchanuser *rcup) {
1085 char escinfo[210];
1086
1087 if (rcup->info)
1088 PQescapeString(escinfo, rcup->info->content, rcup->info->length);
1089 else
1090 escinfo[0]='\0';
1091
1092 csdb_queuequery(NULL, NULL, "UPDATE chanusers SET flags=%u, changetime=%lu, "
1093 "usetime=%lu, info='%s' WHERE channelID=%u and userID=%u",
1094 rcup->flags, rcup->changetime, rcup->usetime, escinfo, rcup->chan->ID,rcup->user->ID);
1095}
1096
1097void csdb_createchanuser(regchanuser *rcup) {
1098 char escinfo[210];
1099
1100 if (rcup->info)
1101 PQescapeString(escinfo, rcup->info->content, rcup->info->length);
1102 else
1103 escinfo[0]='\0';
1104
1105 csdb_queuequery(NULL, NULL, "INSERT INTO chanusers VALUES(%u, %u, %u, %lu, %lu, '%s')",
1106 rcup->user->ID, rcup->chan->ID, rcup->flags, rcup->changetime,
1107 rcup->usetime, escinfo);
1108}
1109
1110void csdb_deletechanuser(regchanuser *rcup) {
1111 csdb_queuequery(NULL, NULL, "DELETE FROM chanusers WHERE channelid=%u AND userID=%u",
1112 rcup->chan->ID, rcup->user->ID);
1113}
1114
1115void csdb_createban(regchan *rcp, regban *rbp) {
1116 char escreason[500];
1117 char banstr[100];
1118 char escban[200];
1119
1120 strcpy(banstr,bantostring(rbp->cbp));
1121 PQescapeString(escban,banstr,strlen(banstr));
1122
1123 if (rbp->reason)
1124 PQescapeString(escreason, rbp->reason->content, rbp->reason->length);
1125 else
1126 escreason[0]='\0';
1127
1128 csdb_queuequery(NULL, NULL, "INSERT INTO bans (banID, channelID, userID, hostmask, "
1129 "expiry, reason) VALUES (%u,%u,%u,'%s',%lu,'%s')", rbp->ID, rcp->ID,
1130 rbp->setby, escban, rbp->expiry, escreason);
1131}
1132
1133void csdb_deleteban(regban *rbp) {
1134 csdb_queuequery(NULL, NULL, "DELETE FROM bans WHERE banID=%u", rbp->ID);
1135}
1136
1137/* Help stuff */
1138
1139void csdb_dohelp(nick *np, Command *cmd) {
1140 struct helpinfo *hip;
1141
1142 hip=(struct helpinfo *)malloc(sizeof(struct helpinfo));
1143
1144 hip->numeric=np->numeric;
1145 hip->commandname=getsstring(cmd->command->content, cmd->command->length);
1146
1147 csdb_queuequery(csdb_dohelp_real, (void *)hip,
1148 "SELECT languageID, fullinfo from help where lower(command)=lower('%s')",cmd->command->content);
1149}
1150
1151void csdb_dohelp_real(void *arg) {
1152 struct helpinfo *hip=arg;
1153 nick *np=getnickbynumeric(hip->numeric);
1154 reguser *rup;
1155 char *result;
1156 PGresult *pgres;
1157 int i,j,num,lang=0;
1158
1159 pgres=PQgetResult(dbconn);
1160
1161 if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
1162 Error("chanserv",ERR_ERROR,"Error loading help text.");
1163 return;
1164 }
1165
1166 if (PQnfields(pgres)!=2) {
1167 Error("chanserv",ERR_ERROR,"Help text format error.");
1168 return;
1169 }
1170
1171 num=PQntuples(pgres);
1172
1173 if (!np) {
1174 PQclear(pgres);
1175 freesstring(hip->commandname);
1176 free(hip);
1177 return;
1178 }
1179
1180 if ((rup=getreguserfromnick(np)))
1181 lang=rup->languageid;
1182
1183 result=NULL;
1184
1185 for (i=0;i<num;i++) {
1186 j=strtoul(PQgetvalue(pgres,i,0),NULL,10);
1187 if ((j==0 && result==NULL) || (j==lang)) {
1188 result=PQgetvalue(pgres,i,1);
1189 if(strlen(result)==0)
1190 result=NULL;
1191 }
1192 }
1193
1194 if (!result)
1195 chanservstdmessage(np, QM_NOHELP, hip->commandname->content);
1196 else
1197 chanservsendmessage(np, result);
1198
1199 freesstring(hip->commandname);
1200 free(hip);
1201}
1202
1203void csdb_createmail(reguser *rup, int type) {
1204 char sqlquery[6000];
1205 char escemail[210];
1206
1207 if (type == QMAIL_NEWEMAIL) {
1208 if (rup->email) {
1209 PQescapeString(escemail, rup->email->content, rup->email->length);
1210 sprintf(sqlquery, "INSERT INTO email (userID, emailType, prevEmail) "
1211 "VALUES (%u,%u,'%s')", rup->ID, type, escemail);
1212 }
1213 } else {
1214 sprintf(sqlquery, "INSERT INTO email (userID, emailType) VALUES (%u,%u)", rup->ID, type);
1215 }
1216
1217 csdb_queuequery(NULL, NULL, "%s", sqlquery);
1218}