]>
Commit | Line | Data |
---|---|---|
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 |
20 | MODULE_VERSION(""); |
21 | ||
404d307c CP |
22 | struct DBAPIProviderData { |
23 | char name[PROVIDER_NAME_LEN+1]; | |
24 | }; | |
25 | ||
26 | static DBAPIProvider *providerobjs[MAX_PROVIDERS]; | |
27 | static struct DBAPIProviderData providerdata[MAX_PROVIDERS]; | |
28 | ||
24e1aba8 CP |
29 | static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap); |
30 | ||
404d307c CP |
31 | void _init(void) { |
32 | memset(providerobjs, 0, sizeof(providerobjs)); | |
33 | } | |
34 | ||
35 | void _fini(void) { | |
36 | /* everything should be unregistered already */ | |
37 | } | |
38 | ||
39 | int 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 | ||
60 | void deregisterdbprovider(int handle) { | |
61 | if(handle < 0) | |
62 | return; | |
63 | ||
64 | providerobjs[handle] = NULL; | |
65 | } | |
66 | ||
67 | static void dbclose(DBAPIConn *db) { | |
68 | db->__close(db); | |
4a532778 | 69 | free((DBAPIConn *)db); |
404d307c CP |
70 | } |
71 | ||
e4b54524 | 72 | static 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 | 87 | static 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 | 102 | static 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 | ||
117 | static 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 | ||
128 | static 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 | ||
139 | static 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 |
150 | static 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 |
154 | static 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 | ||
165 | static 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 |
176 | DBAPIConn *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 | 246 | static 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++) { | |
348 | if(*p != '?') { | |
349 | if(!sbaddchar(&b, *p)) | |
350 | break; | |
351 | continue; | |
352 | } | |
e54cd6b5 | 353 | |
915ef131 CP |
354 | if(arg >= argcount) |
355 | Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name); | |
404d307c | 356 | |
915ef131 CP |
357 | if(!sbaddstr(&b, convbuf[arg])) |
358 | Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name); | |
404d307c CP |
359 | |
360 | arg++; | |
361 | } | |
362 | ||
363 | sbterminate(&b); | |
364 | } |