]> jfr.im git - irc/quakenet/newserv.git/blame - dbapi2/dbapi2.c
merge
[irc/quakenet/newserv.git] / dbapi2 / dbapi2.c
CommitLineData
404d307c
CP
1#define MAX_PROVIDERS 10
2#define PROVIDER_NAME_LEN 100
24e1aba8 3#define QUERYBUFLEN 8192*2
404d307c 4
2a58dbe9
CP
5#define VSNPF_MAXARGS 20
6#define VSNPF_MAXARGLEN 2048
404d307c
CP
7
8#include <string.h>
9#include <stdlib.h>
10#include <stdarg.h>
315d88e4
CP
11#include <time.h>
12#include <stdint.h>
404d307c
CP
13
14#include "../core/error.h"
15#include "../lib/strlfunc.h"
16#include "../lib/stringbuf.h"
8f072e42 17#include "../lib/version.h"
404d307c
CP
18#include "dbapi2.h"
19
8f072e42
CP
20MODULE_VERSION("");
21
404d307c
CP
22struct DBAPIProviderData {
23 char name[PROVIDER_NAME_LEN+1];
24};
25
26static DBAPIProvider *providerobjs[MAX_PROVIDERS];
27static struct DBAPIProviderData providerdata[MAX_PROVIDERS];
28
24e1aba8
CP
29static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap);
30
404d307c
CP
31void _init(void) {
32 memset(providerobjs, 0, sizeof(providerobjs));
33}
34
35void _fini(void) {
36 /* everything should be unregistered already */
37}
38
39int registerdbprovider(const char *name, DBAPIProvider *provider) {
40 int i;
41
42 for(i=0;i<MAX_PROVIDERS;i++) {
adf9e9ea 43 if(providerobjs[i])
404d307c
CP
44 continue;
45
46 providerobjs[i] = provider;
47 providerobjs[i]->__providerdata = &providerdata[i];
48
49 strlcpy(providerobjs[i]->__providerdata->name, name, PROVIDER_NAME_LEN);
50
39c33a3d
CP
51 Error("dbapi2", ERR_INFO, "Database API registered: %s", name);
52
404d307c
CP
53 return i;
54 }
55
56 Error("dbapi2", ERR_WARNING, "No remaining space for new database provider: %s", name);
57 return -1;
58}
59
60void deregisterdbprovider(int handle) {
61 if(handle < 0)
62 return;
63
64 providerobjs[handle] = NULL;
65}
66
67static void dbclose(DBAPIConn *db) {
68 db->__close(db);
4a532778 69 free((DBAPIConn *)db);
404d307c
CP
70}
71
e4b54524 72static void dbunsafequery(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) {
adf9e9ea 73 va_list ap;
24e1aba8 74 char buf[QUERYBUFLEN];
915ef131 75 size_t ret;
adf9e9ea
CP
76
77 va_start(ap, format);
915ef131 78 ret = vsnprintf(buf, sizeof(buf), format, ap);
adf9e9ea 79 va_end(ap);
24e1aba8 80
915ef131 81 if(ret >= sizeof(buf))
e4b54524 82 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafequery, format: '%s', database: %s", format, db->name);
915ef131 83
24e1aba8 84 db->__query(db, cb, data, buf);
adf9e9ea
CP
85}
86
e4b54524 87static void dbunsafecreatetable(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) {
24e1aba8
CP
88 va_list ap;
89 char buf[QUERYBUFLEN];
915ef131 90 size_t ret;
24e1aba8 91
24e1aba8 92 va_start(ap, format);
915ef131 93 ret = vsnprintf(buf, sizeof(buf), format, ap);
24e1aba8
CP
94 va_end(ap);
95
915ef131 96 if(ret >= sizeof(buf))
e4b54524 97 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafecreatetable, format: '%s', database: %s", format, db->name);
915ef131 98
24e1aba8
CP
99 db->__createtable(db, cb, data, buf);
100}
101
e4b54524 102static void dbunsafesimplequery(const DBAPIConn *db, const char *format, ...) {
adf9e9ea 103 va_list ap;
2667bceb
CP
104 char buf[QUERYBUFLEN];
105 size_t ret;
adf9e9ea
CP
106
107 va_start(ap, format);
2667bceb 108 ret = vsnprintf(buf, sizeof(buf), format, ap);
24e1aba8 109 va_end(ap);
2667bceb
CP
110
111 if(ret >= sizeof(buf))
112 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafequery, format: '%s', database: %s", format, db->name);
113
114 db->__query(db, NULL, NULL, buf);
24e1aba8
CP
115}
116
117static void dbsafequery(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, const char *types, ...) {
118 va_list ap;
119 char buf[QUERYBUFLEN];
120
121 va_start(ap, types);
122 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
123 va_end(ap);
124
125 db->__query(db, cb, data, buf);
126}
127
128static void dbsafecreatetable(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, const char *types, ...) {
129 va_list ap;
130 char buf[QUERYBUFLEN];
131
132 va_start(ap, types);
133 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
134 va_end(ap);
135
136 db->__createtable(db, cb, data, buf);
137}
138
139static void dbsafesimplequery(const DBAPIConn *db, const char *format, const char *types, ...) {
140 va_list ap;
2667bceb 141 char buf[QUERYBUFLEN];
24e1aba8
CP
142
143 va_start(ap, types);
2667bceb 144 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
adf9e9ea 145 va_end(ap);
2667bceb
CP
146
147 db->__query(db, NULL, NULL, buf);
adf9e9ea
CP
148}
149
6df405fb
CP
150static void dbloadtable(const DBAPIConn *db, DBAPIQueryCallback init, DBAPIQueryCallback data, DBAPIQueryCallback fini, DBAPIUserData tag, const char *tablename) {
151 db->__loadtable(db, init, data, fini, tag, db->tablename(db, tablename));
152}
153
39c33a3d
CP
154DBAPIConn *dbapi2open(const char *provider, const char *database) {
155 int i, found = -1;
404d307c
CP
156 DBAPIConn *db;
157 DBAPIProvider *p;
158
39c33a3d
CP
159 if(provider) {
160 for(i=0;i<MAX_PROVIDERS;i++) {
161 if(providerobjs[i] && !strcmp(provider, providerobjs[i]->__providerdata->name)) {
162 found = i;
163 break;
164 }
165 }
166 if(found == -1) {
167 Error("dbapi2", ERR_WARNING, "Couldn't find forced database provider %s", provider);
168 return NULL;
169 }
170 } else {
171 for(i=0;i<MAX_PROVIDERS;i++) {
172 if(providerobjs[i]) {
173 found = i;
174 break;
175 }
404d307c 176 }
404d307c 177
39c33a3d
CP
178 if(found == -1) {
179 Error("dbapi2", ERR_WARNING, "No database providers found.");
180 return NULL;
181 }
404d307c
CP
182 }
183
adf9e9ea 184 p = providerobjs[found];
404d307c
CP
185
186 db = calloc(1, sizeof(DBAPIConn));
187 if(!db)
188 return NULL;
189
190 db->close = dbclose;
e4b54524
CP
191 db->query = dbsafequery;
192 db->createtable = dbsafecreatetable;
193 db->squery = dbsafesimplequery;
6df405fb 194 db->loadtable = dbloadtable;
404d307c 195 db->escapestring = p->escapestring;
4277aef7 196 db->tablename = p->tablename;
e4b54524
CP
197 db->unsafequery = dbunsafequery;
198 db->unsafesquery = dbunsafesimplequery;
199 db->unsafecreatetable = dbunsafecreatetable;
404d307c 200
adf9e9ea 201 db->__query = p->query;
404d307c
CP
202 db->__close = p->close;
203 db->__quotestring = p->quotestring;
24e1aba8 204 db->__createtable = p->createtable;
6df405fb 205 db->__loadtable = p->loadtable;
404d307c
CP
206
207 strlcpy(db->name, database, DBNAME_LEN);
208
209 db->handle = p->new(db);
210 if(!db->handle) {
211 free(db);
212 Error("dbapi2", ERR_WARNING, "Unable to initialise database %s, provider: %s", database, p->__providerdata->name);
213 return NULL;
214 }
215
39c33a3d
CP
216 Error("dbapi2", ERR_INFO, "Database %s opened with provider %s.", database, p->__providerdata->name);
217
404d307c
CP
218 return db;
219}
220
4a532778 221static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap) {
404d307c
CP
222 StringBuf b;
223 const char *p;
2a58dbe9 224 static char convbuf[VSNPF_MAXARGS][VSNPF_MAXARGLEN+10];
404d307c
CP
225 int arg, argcount;
226
227 if(size == 0)
228 return;
229
230 {
231 int i;
232
233 int d;
234 char *s;
235 double g;
236 unsigned int u;
237 size_t l;
238 int fallthrough;
315d88e4
CP
239 time_t t;
240 unsigned long ul;
241 long _l;
404d307c 242
2a58dbe9 243 for(i=0;i<VSNPF_MAXARGS;i++)
404d307c
CP
244 convbuf[i][0] = '\0';
245
246 argcount=0;
247 for(;*types;types++) {
248 char *cb = convbuf[argcount];
249
2a58dbe9 250 if(argcount++ >= VSNPF_MAXARGS) {
404d307c 251 /* calls exit(0) */
915ef131 252 Error("dbapi2", ERR_STOP, "Maximum arguments reached in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c
CP
253 }
254
255 fallthrough = 0;
256 switch(*types) {
257 case 's':
258 s = va_arg(ap, char *);
259 l = strlen(s);
260 fallthrough = 1;
261
262 /* falling through */
263 case 'S':
264 if(!fallthrough) {
265 s = va_arg(ap, char *);
266 l = va_arg(ap, size_t);
267 }
268
269 /* now... this is a guess, but we should catch it most of the time */
2a58dbe9 270 if((l > (VSNPF_MAXARGLEN / 2)) || !db->__quotestring(db, cb, sizeof(convbuf[0]), s, l)) {
2667bceb 271 Error("dbapi2", ERR_STOP, "Long string truncated, format: '%s', database: %s", format, db->name);
2a58dbe9 272 l = VSNPF_MAXARGLEN;
404d307c
CP
273 }
274
2a58dbe9
CP
275 break;
276 case 'R':
277 s = va_arg(ap, char *);
278
279 strlcpy(cb, s, sizeof(convbuf[0]));
280 break;
281 case 'T':
282 s = va_arg(ap, char *);
283
284 strlcpy(cb, db->tablename(db, s), sizeof(convbuf[0]));
404d307c
CP
285 break;
286 case 'd':
287 d = va_arg(ap, int);
2a58dbe9 288 snprintf(cb, VSNPF_MAXARGLEN, "%d", d);
404d307c
CP
289 break;
290 case 'u':
291 u = va_arg(ap, unsigned int);
2a58dbe9 292 snprintf(cb, VSNPF_MAXARGLEN, "%u", u);
404d307c 293 break;
315d88e4
CP
294 case 't':
295 t = va_arg(ap, time_t);
296 snprintf(cb, VSNPF_MAXARGLEN, "%jd", (intmax_t)t);
297 break;
298 case 'D':
299 _l = va_arg(ap, long);
300 snprintf(cb, VSNPF_MAXARGLEN, "%ld", _l);
301 break;
302 case 'U':
303 ul = va_arg(ap, unsigned long);
304 snprintf(cb, VSNPF_MAXARGLEN, "%lu", ul);
305 break;
404d307c
CP
306 case 'g':
307 g = va_arg(ap, double);
2a58dbe9 308 snprintf(cb, VSNPF_MAXARGLEN, "%.1f", g);
404d307c
CP
309 break;
310 default:
311 /* calls exit(0) */
312 Error("dbapi2", ERR_STOP, "Bad format specifier '%c' supplied in dbvsnprintf, format: '%s', database: %s", *types, format, db->name);
313 }
314 }
315 }
316
317 sbinit(&b, buf, size);
318
319 for(arg=0,p=format;*p;p++) {
320 if(*p != '?') {
321 if(!sbaddchar(&b, *p))
322 break;
323 continue;
324 }
e54cd6b5 325
915ef131
CP
326 if(arg >= argcount)
327 Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c 328
915ef131
CP
329 if(!sbaddstr(&b, convbuf[arg]))
330 Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c
CP
331
332 arg++;
333 }
334
335 sbterminate(&b);
336}