]>
Commit | Line | Data |
---|---|---|
1 | #define MAX_PROVIDERS 10 | |
2 | #define PROVIDER_NAME_LEN 100 | |
3 | #define QUERYBUFLEN 8192*2 | |
4 | ||
5 | #define VSNPF_MAXARGS 20 | |
6 | #define VSNPF_MAXARGLEN 2048 | |
7 | ||
8 | #include <string.h> | |
9 | #include <stdlib.h> | |
10 | #include <stdarg.h> | |
11 | #include <time.h> | |
12 | #include <stdint.h> | |
13 | ||
14 | #include "../core/error.h" | |
15 | #include "../lib/strlfunc.h" | |
16 | #include "../lib/stringbuf.h" | |
17 | #include "../lib/version.h" | |
18 | #include "dbapi2.h" | |
19 | ||
20 | MODULE_VERSION(""); | |
21 | ||
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 | ||
29 | static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap); | |
30 | ||
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++) { | |
43 | if(providerobjs[i]) | |
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 | ||
51 | Error("dbapi2", ERR_INFO, "Database API registered: %s", name); | |
52 | ||
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); | |
69 | free((DBAPIConn *)db); | |
70 | } | |
71 | ||
72 | static void dbunsafequery(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) { | |
73 | va_list ap; | |
74 | char buf[QUERYBUFLEN]; | |
75 | size_t ret; | |
76 | ||
77 | va_start(ap, format); | |
78 | ret = vsnprintf(buf, sizeof(buf), format, ap); | |
79 | va_end(ap); | |
80 | ||
81 | if(ret >= sizeof(buf)) | |
82 | Error("dbapi2", ERR_STOP, "Query truncated in dbunsafequery, format: '%s', database: %s", format, db->name); | |
83 | ||
84 | db->__query(db, cb, data, buf); | |
85 | } | |
86 | ||
87 | static void dbunsafecreatetable(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) { | |
88 | va_list ap; | |
89 | char buf[QUERYBUFLEN]; | |
90 | size_t ret; | |
91 | ||
92 | va_start(ap, format); | |
93 | ret = vsnprintf(buf, sizeof(buf), format, ap); | |
94 | va_end(ap); | |
95 | ||
96 | if(ret >= sizeof(buf)) | |
97 | Error("dbapi2", ERR_STOP, "Query truncated in dbunsafecreatetable, format: '%s', database: %s", format, db->name); | |
98 | ||
99 | db->__createtable(db, cb, data, buf); | |
100 | } | |
101 | ||
102 | static void dbunsafesimplequery(const DBAPIConn *db, const char *format, ...) { | |
103 | va_list ap; | |
104 | char buf[QUERYBUFLEN]; | |
105 | size_t ret; | |
106 | ||
107 | va_start(ap, format); | |
108 | ret = vsnprintf(buf, sizeof(buf), format, ap); | |
109 | va_end(ap); | |
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); | |
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; | |
141 | char buf[QUERYBUFLEN]; | |
142 | ||
143 | va_start(ap, types); | |
144 | dbvsnprintf(db, buf, sizeof(buf), format, types, ap); | |
145 | va_end(ap); | |
146 | ||
147 | db->__query(db, NULL, NULL, buf); | |
148 | } | |
149 | ||
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 | ||
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 | ||
176 | DBAPIConn *dbapi2open(const char *provider, const char *database) { | |
177 | int i, found = -1; | |
178 | DBAPIConn *db; | |
179 | DBAPIProvider *p; | |
180 | ||
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 | } | |
198 | } | |
199 | ||
200 | if(found == -1) { | |
201 | Error("dbapi2", ERR_WARNING, "No database providers found."); | |
202 | return NULL; | |
203 | } | |
204 | } | |
205 | ||
206 | p = providerobjs[found]; | |
207 | ||
208 | db = calloc(1, sizeof(DBAPIConn)); | |
209 | if(!db) | |
210 | return NULL; | |
211 | ||
212 | db->close = dbclose; | |
213 | db->query = dbsafequery; | |
214 | db->createtable = dbsafecreatetable; | |
215 | db->squery = dbsafesimplequery; | |
216 | db->loadtable = dbloadtable; | |
217 | db->escapestring = p->escapestring; | |
218 | db->tablename = p->tablename; | |
219 | db->unsafequery = dbunsafequery; | |
220 | db->unsafesquery = dbunsafesimplequery; | |
221 | db->unsafecreatetable = dbunsafecreatetable; | |
222 | db->call = dbcall; | |
223 | db->scall = dbsimplecall; | |
224 | ||
225 | db->__query = p->query; | |
226 | db->__close = p->close; | |
227 | db->__quotestring = p->quotestring; | |
228 | db->__createtable = p->createtable; | |
229 | db->__loadtable = p->loadtable; | |
230 | db->__call = p->call; | |
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 | ||
241 | Error("dbapi2", ERR_INFO, "Database %s opened with provider %s.", database, p->__providerdata->name); | |
242 | ||
243 | return db; | |
244 | } | |
245 | ||
246 | static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap) { | |
247 | StringBuf b; | |
248 | const char *p; | |
249 | static char convbuf[VSNPF_MAXARGS][VSNPF_MAXARGLEN+10]; | |
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; | |
264 | time_t t; | |
265 | unsigned long ul; | |
266 | long _l; | |
267 | ||
268 | for(i=0;i<VSNPF_MAXARGS;i++) | |
269 | convbuf[i][0] = '\0'; | |
270 | ||
271 | argcount=0; | |
272 | for(;*types;types++) { | |
273 | char *cb = convbuf[argcount]; | |
274 | ||
275 | if(argcount++ >= VSNPF_MAXARGS) { | |
276 | /* calls exit(0) */ | |
277 | Error("dbapi2", ERR_STOP, "Maximum arguments reached in dbvsnprintf, format: '%s', database: %s", format, db->name); | |
278 | } | |
279 | ||
280 | fallthrough = 0; | |
281 | switch(*types) { | |
282 | case 's': | |
283 | s = va_arg(ap, char *); | |
284 | if(s) | |
285 | l = strlen(s); | |
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 | ||
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 */ | |
299 | Error("dbapi2", ERR_STOP, "Long string truncated, format: '%s', database: %s", format, db->name); | |
300 | l = VSNPF_MAXARGLEN; | |
301 | } | |
302 | ||
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])); | |
313 | break; | |
314 | case 'd': | |
315 | d = va_arg(ap, int); | |
316 | snprintf(cb, VSNPF_MAXARGLEN, "%d", d); | |
317 | break; | |
318 | case 'u': | |
319 | u = va_arg(ap, unsigned int); | |
320 | snprintf(cb, VSNPF_MAXARGLEN, "%u", u); | |
321 | break; | |
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; | |
334 | case 'g': | |
335 | g = va_arg(ap, double); | |
336 | snprintf(cb, VSNPF_MAXARGLEN, "%.1f", g); | |
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 == '\\' && *(p + 1) == '?') | |
349 | continue; | |
350 | ||
351 | if((p != format && *(p - 1) == '\\') || *p != '?') { | |
352 | if(!sbaddchar(&b, *p)) | |
353 | break; | |
354 | continue; | |
355 | } | |
356 | ||
357 | if(arg >= argcount) | |
358 | Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name); | |
359 | ||
360 | if(!sbaddstr(&b, convbuf[arg])) | |
361 | Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name); | |
362 | ||
363 | arg++; | |
364 | } | |
365 | ||
366 | sbterminate(&b); | |
367 | } |