]>
jfr.im git - irc/quakenet/newserv.git/blob - chanserv/database/chanservdb.c
3 * Handles the SQL stuff for the channel service.
6 #include "../chanserv.h"
7 #include "../../pqsql/pqsql.h"
8 #include "../../core/config.h"
9 #include "../../lib/sstring.h"
10 #include "../../parser/parser.h"
11 #include "../../core/events.h"
12 #include "../../core/nsmalloc.h"
22 typedef struct tabledesc
{
23 sstring
*tablename
; /* Name of table */
24 PQQueryHandler init
; /* Function to be called when transaction opens */
25 PQQueryHandler data
; /* Function to be called to load data */
26 PQQueryHandler fini
; /* Function to be called to clean up */
34 unsigned int lastchannelID
;
35 unsigned int lastuserID
;
36 unsigned int lastbanID
;
38 /* Local prototypes */
39 void csdb_handlestats(int hooknum
, void *arg
);
40 void loadmessages_part1(PGconn
*, void *);
41 void loadmessages_part2(PGconn
*, void *);
42 void loadcommandsummary_real(PGconn
*, void *);
44 /* Generic loading functions */
45 void loadall(char *, PQQueryHandler
, PQQueryHandler
, PQQueryHandler
);
46 void doloadall(PGconn
*, void *);
48 /* User loading functions */
49 void loadsomeusers(PGconn
*, void *);
50 void loadusersdone(PGconn
*, void *);
52 /* Channel loading functions */
53 void loadsomechannels(PGconn
*, void *);
54 void loadchannelsdone(PGconn
*, void *);
56 /* Chanuser loading functions */
57 void loadchanusersinit(PGconn
*, void *);
58 void loadsomechanusers(PGconn
*, void *);
59 void loadchanusersdone(PGconn
*, void *);
61 /* Chanban loading functions */
62 void loadsomechanbans(PGconn
*, void *);
63 void loadchanbansdone(PGconn
*, void *);
65 /* Free sstrings in the structures */
66 void csdb_freestuff();
68 static void setuptables() {
69 /* Set up the tables */
71 pqcreatequery("CREATE TABLE users ("
73 "username VARCHAR(16) NOT NULL,"
74 "created INT NOT NULL,"
75 "lastauth INT NOT NULL,"
76 "lastemailchng INT NOT NULL,"
78 "language INT NOT NULL,"
79 "suspendby INT NOT NULL,"
80 "suspendexp INT NOT NULL,"
81 "password VARCHAR(11) NOT NULL,"
82 "masterpass VARCHAR(11),"
84 "lastuserhost VARCHAR(75),"
85 "suspendreason VARCHAR(250),"
86 "comment VARCHAR(250),"
90 pqcreatequery("CREATE INDEX user_username_index ON users (username)");
93 pqcreatequery("CREATE TABLE channels ("
95 "name VARCHAR(250) NOT NULL,"
97 "forcemodes INT NOT NULL,"
98 "denymodes INT NOT NULL,"
99 "chanlimit INT NOT NULL,"
100 "autolimit INT NOT NULL,"
101 "banstyle INT NOT NULL,"
102 "created INT NOT NULL,"
103 "lastactive INT NOT NULL,"
104 "statsreset INT NOT NULL,"
105 "banduration INT NOT NULL,"
106 "founder INT NOT NULL,"
107 "addedby INT NOT NULL,"
108 "suspendby INT NOT NULL,"
109 "chantype SMALLINT NOT NULL,"
110 "totaljoins INT NOT NULL,"
111 "tripjoins INT NOT NULL,"
112 "maxusers INT NOT NULL,"
113 "tripusers INT NOT NULL,"
114 "welcome VARCHAR(500),"
115 "topic VARCHAR(250),"
116 "chankey VARCHAR(23),"
117 "suspendreason VARCHAR(250),"
118 "comment VARCHAR(250),"
120 "PRIMARY KEY (ID))");
123 pqcreatequery("CREATE TABLE chanusers ("
124 "userID INT NOT NULL,"
125 "channelID INT NOT NULL,"
126 "flags INT NOT NULL,"
127 "changetime INT NOT NULL,"
128 "usetime INT NOT NULL,"
129 "info VARCHAR(100) NOT NULL,"
130 "PRIMARY KEY (userID, channelID))");
132 pqcreatequery("CREATE INDEX chanusers_userID_index on chanusers (userID)");
133 pqcreatequery("CREATE INDEX chanusers_channelID_index on chanusers (channelID)");
135 pqcreatequery("CREATE TABLE bans ("
136 "banID INT NOT NULL," /* Unique number for the ban to make
137 DELETEs process in finite time.. */
138 "channelID INT NOT NULL,"
139 "userID INT NOT NULL," /* Who set the ban.. */
140 "hostmask VARCHAR(100) NOT NULL," /* needs to be at least USERLEN+NICKLEN+HOSTLEN+2 */
141 "expiry INT NOT NULL,"
142 "reason VARCHAR(200),"
143 "PRIMARY KEY(banID))");
145 pqcreatequery("CREATE INDEX bans_channelID_index on bans (channelID)");
147 pqcreatequery("CREATE TABLE languages ("
148 "languageID INT NOT NULL,"
149 "code VARCHAR(2) NOT NULL,"
150 "name VARCHAR(30) NOT NULL)");
152 pqcreatequery("CREATE TABLE messages ("
153 "languageID INT NOT NULL,"
154 "messageID INT NOT NULL,"
155 "message VARCHAR(250) NOT NULL,"
156 "PRIMARY KEY (languageID, messageID))");
158 pqcreatequery("CREATE TABLE help ("
159 "commandID INT NOT NULL,"
160 "command VARCHAR(30) NOT NULL,"
161 "languageID INT NOT NULL,"
162 "summary VARCHAR(200) NOT NULL,"
163 "fullinfo TEXT NOT NULL,"
164 "PRIMARY KEY (commandID, languageID))");
166 pqcreatequery("CREATE TABLE email ("
167 "userID INT NOT NULL,"
168 "emailtype INT NOT NULL,"
169 "prevEmail VARCHAR(100),"
170 "mailID VARCHAR(128))");
174 chanservext
=registerchanext("chanserv");
175 chanservaext
=registerauthnameext("chanserv");
177 /* Set up the allocators and hashes */
181 /* And the messages */
184 if (pqconnected() && (chanservext
!=-1) && (chanservaext
!=-1)) {
185 registerhook(HOOK_CORE_STATSREQUEST
, csdb_handlestats
);
189 lastuserID
=lastchannelID
=0;
191 loadall("users",NULL
,loadsomeusers
,loadusersdone
);
192 loadall("channels",NULL
,loadsomechannels
,loadchannelsdone
);
193 loadall("chanusers",loadchanusersinit
,loadsomechanusers
,loadchanusersdone
);
194 loadall("bans",NULL
,loadsomechanbans
,loadchanbansdone
);
202 releasechanext(chanservext
);
204 if (chanservaext
!=-1)
205 releaseauthnameext(chanservaext
);
208 nsfreeall(POOL_CHANSERVDB
);
211 void csdb_handlestats(int hooknum
, void *arg
) {
212 /* long level=(long)arg; */
214 /* Keeping options open here */
217 void chanservdbclose() {
219 deregisterhook(HOOK_CORE_STATSREQUEST
, csdb_handlestats
);
225 * Generic function to handle load of an entire table..
228 void loadall(char *table
, PQQueryHandler init
, PQQueryHandler data
, PQQueryHandler fini
) {
231 thedesc
=malloc(sizeof(tabledesc
));
233 thedesc
->tablename
=getsstring(table
,100);
238 pqasyncquery(doloadall
, thedesc
, "SELECT count(*) FROM %s",thedesc
->tablename
->content
);
241 void doloadall(PGconn
*dbconn
, void *arg
) {
244 tabledesc
*thedesc
=arg
;
246 pgres
=PQgetResult(dbconn
);
248 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
249 Error("chanserv",ERR_ERROR
,"Error getting row count for %s.",thedesc
->tablename
->content
);
253 if (PQnfields(pgres
)!=1) {
254 Error("chanserv",ERR_ERROR
,"Count query format error for %s.",thedesc
->tablename
->content
);
258 count
=strtoul(PQgetvalue(pgres
,0,0),NULL
,10);
262 Error("chanserv",ERR_INFO
,"Found %d entries in table %s, scheduling load.",count
,
263 thedesc
->tablename
->content
);
265 pqasyncquery(thedesc
->init
, NULL
, "BEGIN");
266 pqquery("DECLARE mycurs CURSOR FOR SELECT * from %s",
267 thedesc
->tablename
->content
);
269 for (i
=0;(count
-i
)>1000;i
+=1000) {
270 pqasyncquery(thedesc
->data
, NULL
, "FETCH 1000 FROM mycurs");
273 pqasyncquery(thedesc
->data
, NULL
, "FETCH ALL FROM mycurs");
275 pqquery("CLOSE mycurs");
276 pqasyncquery(thedesc
->fini
, NULL
, "COMMIT");
278 /* Free structures.. */
279 freesstring(thedesc
->tablename
);
285 * Loads some users in from the SQL DB
288 void loadsomeusers(PGconn
*dbconn
, void *arg
) {
293 pgres
=PQgetResult(dbconn
);
295 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
296 Error("chanserv",ERR_ERROR
,"Error loading user DB");
300 if (PQnfields(pgres
)!=16) {
301 Error("chanserv",ERR_ERROR
,"User DB format error");
305 num
=PQntuples(pgres
);
310 rup
->ID
=strtoul(PQgetvalue(pgres
,i
,0),NULL
,10);
311 strncpy(rup
->username
,PQgetvalue(pgres
,i
,1),NICKLEN
); rup
->username
[NICKLEN
]='\0';
312 rup
->created
=strtoul(PQgetvalue(pgres
,i
,2),NULL
,10);
313 rup
->lastauth
=strtoul(PQgetvalue(pgres
,i
,3),NULL
,10);
314 rup
->lastemailchange
=strtoul(PQgetvalue(pgres
,i
,4),NULL
,10);
315 rup
->flags
=strtoul(PQgetvalue(pgres
,i
,5),NULL
,10);
316 rup
->languageid
=strtoul(PQgetvalue(pgres
,i
,6),NULL
,10);
317 rup
->suspendby
=strtoul(PQgetvalue(pgres
,i
,7),NULL
,10);
318 rup
->suspendexp
=strtoul(PQgetvalue(pgres
,i
,8),NULL
,10);
319 strncpy(rup
->password
,PQgetvalue(pgres
,i
,9),PASSLEN
); rup
->password
[PASSLEN
]='\0';
320 strncpy(rup
->masterpass
,PQgetvalue(pgres
,i
,10),PASSLEN
); rup
->masterpass
[PASSLEN
]='\0';
321 rup
->email
=getsstring(PQgetvalue(pgres
,i
,11),100);
322 rup
->lastuserhost
=getsstring(PQgetvalue(pgres
,i
,12),75);
323 rup
->suspendreason
=getsstring(PQgetvalue(pgres
,i
,13),250);
324 rup
->comment
=getsstring(PQgetvalue(pgres
,i
,14),250);
325 rup
->info
=getsstring(PQgetvalue(pgres
,i
,15),100);
331 addregusertohash(rup
);
333 if (rup
->ID
> lastuserID
) {
341 void loadusersdone(PGconn
*conn
, void *arg
) {
342 Error("chanserv",ERR_INFO
,"Load users done (highest ID was %d)",lastuserID
);
346 * Channel loading functions
349 void loadsomechannels(PGconn
*dbconn
, void *arg
) {
354 time_t now
=time(NULL
);
356 pgres
=PQgetResult(dbconn
);
358 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
359 Error("chanserv",ERR_ERROR
,"Error loading channel DB");
363 if (PQnfields(pgres
)!=26) {
364 Error("chanserv",ERR_ERROR
,"Channel DB format error");
368 num
=PQntuples(pgres
);
371 cip
=findorcreatechanindex(PQgetvalue(pgres
,i
,1));
372 if (cip
->exts
[chanservext
]) {
373 Error("chanserv",ERR_WARNING
,"%s in database twice - this WILL cause problems later.",cip
->name
->content
);
377 cip
->exts
[chanservext
]=rcp
;
379 rcp
->ID
=strtoul(PQgetvalue(pgres
,i
,0),NULL
,10);
381 rcp
->flags
=strtoul(PQgetvalue(pgres
,i
,2),NULL
,10);
382 rcp
->status
=0; /* Non-DB field */
384 rcp
->lastcountersync
=now
;
387 rcp
->forcemodes
=strtoul(PQgetvalue(pgres
,i
,3),NULL
,10);
388 rcp
->denymodes
=strtoul(PQgetvalue(pgres
,i
,4),NULL
,10);
389 rcp
->limit
=strtoul(PQgetvalue(pgres
,i
,5),NULL
,10);
390 rcp
->autolimit
=strtoul(PQgetvalue(pgres
,i
,6),NULL
,10);
391 rcp
->banstyle
=strtoul(PQgetvalue(pgres
,i
,7),NULL
,10);
392 rcp
->created
=strtoul(PQgetvalue(pgres
,i
,8),NULL
,10);
393 rcp
->lastactive
=strtoul(PQgetvalue(pgres
,i
,9),NULL
,10);
394 rcp
->statsreset
=strtoul(PQgetvalue(pgres
,i
,10),NULL
,10);
395 rcp
->banduration
=strtoul(PQgetvalue(pgres
,i
,11),NULL
,10);
396 rcp
->founder
=strtol(PQgetvalue(pgres
,i
,12),NULL
,10);
397 rcp
->addedby
=strtol(PQgetvalue(pgres
,i
,13),NULL
,10);
398 rcp
->suspendby
=strtol(PQgetvalue(pgres
,i
,14),NULL
,10);
399 rcp
->chantype
=strtoul(PQgetvalue(pgres
,i
,15),NULL
,10);
400 rcp
->totaljoins
=strtoul(PQgetvalue(pgres
,i
,16),NULL
,10);
401 rcp
->tripjoins
=strtoul(PQgetvalue(pgres
,i
,17),NULL
,10);
402 rcp
->maxusers
=strtoul(PQgetvalue(pgres
,i
,18),NULL
,10);
403 rcp
->tripusers
=strtoul(PQgetvalue(pgres
,i
,19),NULL
,10);
404 rcp
->welcome
=getsstring(PQgetvalue(pgres
,i
,20),500);
405 rcp
->topic
=getsstring(PQgetvalue(pgres
,i
,21),TOPICLEN
);
406 rcp
->key
=getsstring(PQgetvalue(pgres
,i
,22),KEYLEN
);
407 rcp
->suspendreason
=getsstring(PQgetvalue(pgres
,i
,23),250);
408 rcp
->comment
=getsstring(PQgetvalue(pgres
,i
,24),250);
409 rcp
->checksched
=NULL
;
410 rcp
->ltimestamp
=strtoul(PQgetvalue(pgres
,i
,25),NULL
,10);
411 memset(rcp
->regusers
,0,REGCHANUSERHASHSIZE
*sizeof(reguser
*));
413 if (rcp
->ID
> lastchannelID
)
414 lastchannelID
=rcp
->ID
;
416 if (CIsAutoLimit(rcp
))
423 void loadchannelsdone(PGconn
*dbconn
, void *arg
) {
424 Error("chanserv",ERR_INFO
,"Channel load done (highest ID was %d)",lastchannelID
);
427 void loadchanusersinit(PGconn
*dbconn
, void *arg
) {
432 allchans
=(regchan
**)malloc((lastchannelID
+1)*sizeof(regchan
*));
433 memset(allchans
,0,(lastchannelID
+1)*sizeof(regchan
*));
434 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
435 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
436 if (cip
->exts
[chanservext
]) {
437 rcp
=(regchan
*)cip
->exts
[chanservext
];
438 allchans
[rcp
->ID
]=rcp
;
444 void loadsomechanusers(PGconn
*dbconn
, void *arg
) {
454 /* Set up the allchans array */
455 pgres
=PQgetResult(dbconn
);
457 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
458 Error("chanserv",ERR_ERROR
,"Error loading chanusers.");
462 if (PQnfields(pgres
)!=6) {
463 Error("chanserv",ERR_ERROR
,"Chanusers format error");
467 num
=PQntuples(pgres
);
470 uid
=strtol(PQgetvalue(pgres
,i
,0),NULL
,10);
471 cid
=strtol(PQgetvalue(pgres
,i
,1),NULL
,10);
473 if (!(anp
=findauthname(uid
)) || !(rup
=anp
->exts
[chanservaext
])) {
474 Error("chanserv",ERR_WARNING
,"Skipping channeluser for unknown user %d",uid
);
478 if (cid
>lastchannelID
|| !(rcp
=allchans
[cid
])) {
479 Error("chanserv",ERR_WARNING
,"Skipping channeluser for unknown chan %d",cid
);
483 if (rup
==NULL
|| rcp
==NULL
) {
484 Error("chanserv",ERR_ERROR
,"Can't add user %s on channel %s",
485 PQgetvalue(pgres
,i
,0),PQgetvalue(pgres
,i
,1));
487 rcup
=getregchanuser();
490 rcup
->flags
=strtol(PQgetvalue(pgres
,i
,2),NULL
,10);
491 rcup
->changetime
=strtol(PQgetvalue(pgres
,i
,3),NULL
,10);
492 rcup
->usetime
=strtol(PQgetvalue(pgres
,i
,4),NULL
,10);
493 rcup
->info
=getsstring(PQgetvalue(pgres
,i
,5),100);
494 addregusertochannel(rcup
);
502 void loadchanusersdone(PGconn
*dbconn
, void *arg
) {
503 Error("chanserv",ERR_INFO
,"Channel user load done.");
506 void loadsomechanbans(PGconn
*dbconn
, void *arg
) {
515 pgres
=PQgetResult(dbconn
);
517 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
518 Error("chanserv",ERR_ERROR
,"Error loading bans.");
522 if (PQnfields(pgres
)!=6) {
523 Error("chanserv",ERR_ERROR
,"Ban format error");
527 num
=PQntuples(pgres
);
532 bid
=strtoul(PQgetvalue(pgres
,i
,0),NULL
,10);
533 cid
=strtoul(PQgetvalue(pgres
,i
,1),NULL
,10);
534 uid
=strtoul(PQgetvalue(pgres
,i
,2),NULL
,10);
535 expiry
=strtoul(PQgetvalue(pgres
,i
,4),NULL
,10);
537 if (cid
>lastchannelID
|| !(rcp
=allchans
[cid
])) {
538 Error("chanserv",ERR_WARNING
,"Skipping ban for unknown chan %d",cid
);
546 rbp
->reason
=getsstring(PQgetvalue(pgres
,i
,5),200);
547 rbp
->cbp
=makeban(PQgetvalue(pgres
,i
,3));
560 void loadchanbansdone(PGconn
*dbconn
, void *arg
) {
563 Error("chanserv",ERR_INFO
,"Channel ban load done, highest ID was %d",lastbanID
);
566 triggerhook(HOOK_CHANSERV_DBLOADED
, NULL
);
569 void loadmessages() {
570 pqasyncquery(loadmessages_part1
, NULL
, "SELECT * from languages");
573 void loadmessages_part1(PGconn
*dbconn
, void *arg
) {
578 /* Firstly, clear up any stale messages */
579 for (i
=0;i
<MAXLANG
;i
++) {
580 if (cslanguages
[i
]) {
581 freesstring(cslanguages
[i
]->name
);
582 free(cslanguages
[i
]);
584 for (j
=0;j
<MAXMESSAGES
;j
++) {
585 if (csmessages
[i
][j
]) {
586 freesstring(csmessages
[i
][j
]);
587 csmessages
[i
][j
]=NULL
;
592 pgres
=PQgetResult(dbconn
);
594 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
595 Error("chanserv",ERR_ERROR
,"Error loading language list.");
599 if (PQnfields(pgres
)!=3) {
600 Error("chanserv",ERR_ERROR
,"Language list format error.");
604 num
=PQntuples(pgres
);
606 for (i
=0;i
<num
;i
++) {
607 j
=strtol(PQgetvalue(pgres
,i
,0),NULL
,10);
608 if (j
<MAXLANG
&& j
>=0) {
609 cslanguages
[j
]=(cslang
*)malloc(sizeof(cslang
));
611 strncpy(cslanguages
[j
]->code
,PQgetvalue(pgres
,i
,1),2); cslanguages
[j
]->code
[2]='\0';
612 cslanguages
[j
]->name
=getsstring(PQgetvalue(pgres
,i
,2),30);
619 Error("chanserv",ERR_ERROR
,"Found too many languages (%d > %d)",i
,MAXLANG
);
621 pqasyncquery(loadmessages_part2
, NULL
, "SELECT * from messages");
624 void loadmessages_part2(PGconn
*dbconn
, void *arg
) {
628 pgres
=PQgetResult(dbconn
);
630 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
631 Error("chanserv",ERR_ERROR
,"Error loading message list.");
635 if (PQnfields(pgres
)!=3) {
636 Error("chanserv",ERR_ERROR
,"Message list format error.");
640 num
=PQntuples(pgres
);
642 for (i
=0;i
<num
;i
++) {
643 k
=strtol(PQgetvalue(pgres
,i
,0),NULL
,10);
644 j
=strtol(PQgetvalue(pgres
,i
,1),NULL
,10);
646 if (k
<0 || k
>= MAXLANG
) {
647 Error("chanserv",ERR_WARNING
,"Language ID out of range on message: %d",k
);
651 if (j
<0 || j
>= MAXMESSAGES
) {
652 Error("chanserv",ERR_WARNING
,"Message ID out of range on message: %d",j
);
656 csmessages
[k
][j
]=getsstring(PQgetvalue(pgres
,i
,2),250);
662 void loadcommandsummary(Command
*cmd
) {
663 pqasyncquery(loadcommandsummary_real
, (void *)cmd
,
664 "SELECT languageID,summary from help where lower(command) = lower('%s')",cmd
->command
->content
);
667 void loadcommandsummary_real(PGconn
*dbconn
, void *arg
) {
673 /* Clear up old text first */
675 for (i
=0;i
<MAXLANG
;i
++) {
677 freesstring(cs
->bylang
[i
]);
680 pgres
=PQgetResult(dbconn
);
682 if (PQresultStatus(pgres
) != PGRES_TUPLES_OK
) {
683 Error("chanserv",ERR_ERROR
,"Error loading command summary.");
687 if (PQnfields(pgres
)!=2) {
688 Error("chanserv",ERR_ERROR
,"Command summary format error.");
692 num
=PQntuples(pgres
);
694 for (i
=0;i
<num
;i
++) {
695 j
=strtol(PQgetvalue(pgres
,i
,0),NULL
,10);
696 if (j
<MAXLANG
&& j
>=0) {
697 cs
->bylang
[j
]=getsstring(PQgetvalue(pgres
,i
,1),200);
704 void csdb_freestuff() {
706 chanindex
*cip
, *ncip
;
712 for (i
=0;i
<REGUSERHASHSIZE
;i
++) {
713 for (rup
=regusernicktable
[i
];rup
;rup
=rup
->nextbyname
) {
714 freesstring(rup
->email
);
715 freesstring(rup
->lastuserhost
);
716 freesstring(rup
->suspendreason
);
717 freesstring(rup
->comment
);
718 freesstring(rup
->info
);
720 for (rcup
=rup
->knownon
;rcup
;rcup
=rcup
->nextbyuser
)
721 freesstring(rcup
->info
);
725 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
726 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
728 if ((rcp
=cip
->exts
[chanservext
])) {
729 freesstring(rcp
->welcome
);
730 freesstring(rcp
->topic
);
731 freesstring(rcp
->key
);
732 freesstring(rcp
->suspendreason
);
733 freesstring(rcp
->comment
);
734 for (rbp
=rcp
->bans
;rbp
;rbp
=rbp
->next
) {
735 freesstring(rbp
->reason
);
736 freechanban(rbp
->cbp
);
738 cip
->exts
[chanservext
]=NULL
;
739 releasechanindex(cip
);