]> jfr.im git - irc/quakenet/newserv.git/blame - dbapi2/dbapi2.c
Merge pull request #1 from meeb/meeb
[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
4d47ff97
CP
154static void dbcall(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *function, const char *format, const char *types, ...) {
155 va_list ap;
156 char buf[QUERYBUFLEN];
157
158 va_start(ap, types);
159 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
160 va_end(ap);
161
162 db->__call(db, cb, data, function, buf);
163}
164
165static void dbsimplecall(const DBAPIConn *db, const char *function, const char *format, const char *types, ...) {
166 va_list ap;
167 char buf[QUERYBUFLEN];
168
169 va_start(ap, types);
170 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
171 va_end(ap);
172
173 db->__call(db, NULL, NULL, function, buf);
174}
175
39c33a3d
CP
176DBAPIConn *dbapi2open(const char *provider, const char *database) {
177 int i, found = -1;
404d307c
CP
178 DBAPIConn *db;
179 DBAPIProvider *p;
180
39c33a3d
CP
181 if(provider) {
182 for(i=0;i<MAX_PROVIDERS;i++) {
183 if(providerobjs[i] && !strcmp(provider, providerobjs[i]->__providerdata->name)) {
184 found = i;
185 break;
186 }
187 }
188 if(found == -1) {
189 Error("dbapi2", ERR_WARNING, "Couldn't find forced database provider %s", provider);
190 return NULL;
191 }
192 } else {
193 for(i=0;i<MAX_PROVIDERS;i++) {
194 if(providerobjs[i]) {
195 found = i;
196 break;
197 }
404d307c 198 }
404d307c 199
39c33a3d
CP
200 if(found == -1) {
201 Error("dbapi2", ERR_WARNING, "No database providers found.");
202 return NULL;
203 }
404d307c
CP
204 }
205
adf9e9ea 206 p = providerobjs[found];
404d307c
CP
207
208 db = calloc(1, sizeof(DBAPIConn));
209 if(!db)
210 return NULL;
211
212 db->close = dbclose;
e4b54524
CP
213 db->query = dbsafequery;
214 db->createtable = dbsafecreatetable;
215 db->squery = dbsafesimplequery;
6df405fb 216 db->loadtable = dbloadtable;
404d307c 217 db->escapestring = p->escapestring;
4277aef7 218 db->tablename = p->tablename;
e4b54524
CP
219 db->unsafequery = dbunsafequery;
220 db->unsafesquery = dbunsafesimplequery;
221 db->unsafecreatetable = dbunsafecreatetable;
4d47ff97
CP
222 db->call = dbcall;
223 db->scall = dbsimplecall;
404d307c 224
adf9e9ea 225 db->__query = p->query;
404d307c
CP
226 db->__close = p->close;
227 db->__quotestring = p->quotestring;
24e1aba8 228 db->__createtable = p->createtable;
6df405fb 229 db->__loadtable = p->loadtable;
4d47ff97 230 db->__call = p->call;
404d307c
CP
231
232 strlcpy(db->name, database, DBNAME_LEN);
233
234 db->handle = p->new(db);
235 if(!db->handle) {
236 free(db);
237 Error("dbapi2", ERR_WARNING, "Unable to initialise database %s, provider: %s", database, p->__providerdata->name);
238 return NULL;
239 }
240
39c33a3d
CP
241 Error("dbapi2", ERR_INFO, "Database %s opened with provider %s.", database, p->__providerdata->name);
242
404d307c
CP
243 return db;
244}
245
4a532778 246static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap) {
404d307c
CP
247 StringBuf b;
248 const char *p;
2a58dbe9 249 static char convbuf[VSNPF_MAXARGS][VSNPF_MAXARGLEN+10];
404d307c
CP
250 int arg, argcount;
251
252 if(size == 0)
253 return;
254
255 {
256 int i;
257
258 int d;
259 char *s;
260 double g;
261 unsigned int u;
262 size_t l;
263 int fallthrough;
315d88e4
CP
264 time_t t;
265 unsigned long ul;
266 long _l;
404d307c 267
2a58dbe9 268 for(i=0;i<VSNPF_MAXARGS;i++)
404d307c
CP
269 convbuf[i][0] = '\0';
270
271 argcount=0;
272 for(;*types;types++) {
273 char *cb = convbuf[argcount];
274
2a58dbe9 275 if(argcount++ >= VSNPF_MAXARGS) {
404d307c 276 /* calls exit(0) */
915ef131 277 Error("dbapi2", ERR_STOP, "Maximum arguments reached in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c
CP
278 }
279
280 fallthrough = 0;
281 switch(*types) {
282 case 's':
283 s = va_arg(ap, char *);
dcd214e1
CP
284 if(s)
285 l = strlen(s);
404d307c
CP
286 fallthrough = 1;
287
288 /* falling through */
289 case 'S':
290 if(!fallthrough) {
291 s = va_arg(ap, char *);
292 l = va_arg(ap, size_t);
293 }
294
dcd214e1
CP
295 if(!s) {
296 strlcpy(cb, "NULL", sizeof(convbuf[0]));
297 } else if((l > (VSNPF_MAXARGLEN / 2)) || !db->__quotestring(db, cb, sizeof(convbuf[0]), s, l)) {
298 /* now... this is a guess, but we should catch it most of the time */
2667bceb 299 Error("dbapi2", ERR_STOP, "Long string truncated, format: '%s', database: %s", format, db->name);
2a58dbe9 300 l = VSNPF_MAXARGLEN;
404d307c
CP
301 }
302
2a58dbe9
CP
303 break;
304 case 'R':
305 s = va_arg(ap, char *);
306
307 strlcpy(cb, s, sizeof(convbuf[0]));
308 break;
309 case 'T':
310 s = va_arg(ap, char *);
311
312 strlcpy(cb, db->tablename(db, s), sizeof(convbuf[0]));
404d307c
CP
313 break;
314 case 'd':
315 d = va_arg(ap, int);
2a58dbe9 316 snprintf(cb, VSNPF_MAXARGLEN, "%d", d);
404d307c
CP
317 break;
318 case 'u':
319 u = va_arg(ap, unsigned int);
2a58dbe9 320 snprintf(cb, VSNPF_MAXARGLEN, "%u", u);
404d307c 321 break;
315d88e4
CP
322 case 't':
323 t = va_arg(ap, time_t);
324 snprintf(cb, VSNPF_MAXARGLEN, "%jd", (intmax_t)t);
325 break;
326 case 'D':
327 _l = va_arg(ap, long);
328 snprintf(cb, VSNPF_MAXARGLEN, "%ld", _l);
329 break;
330 case 'U':
331 ul = va_arg(ap, unsigned long);
332 snprintf(cb, VSNPF_MAXARGLEN, "%lu", ul);
333 break;
404d307c
CP
334 case 'g':
335 g = va_arg(ap, double);
2a58dbe9 336 snprintf(cb, VSNPF_MAXARGLEN, "%.1f", g);
404d307c
CP
337 break;
338 default:
339 /* calls exit(0) */
340 Error("dbapi2", ERR_STOP, "Bad format specifier '%c' supplied in dbvsnprintf, format: '%s', database: %s", *types, format, db->name);
341 }
342 }
343 }
344
345 sbinit(&b, buf, size);
346
347 for(arg=0,p=format;*p;p++) {
1c2c4255
GB
348 if (*p == '\\' && *(p + 1) == '?')
349 continue;
350
351 if((p != format && *(p - 1) == '\\') || *p != '?') {
404d307c
CP
352 if(!sbaddchar(&b, *p))
353 break;
354 continue;
355 }
e54cd6b5 356
915ef131
CP
357 if(arg >= argcount)
358 Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c 359
915ef131
CP
360 if(!sbaddstr(&b, convbuf[arg]))
361 Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name);
404d307c
CP
362
363 arg++;
364 }
365
366 sbterminate(&b);
367}