]>
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 | ||
39c33a3d CP |
154 | DBAPIConn *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 | 221 | static 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 | } |