1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Sascha Schumann <sascha@schumann.cx> |
14 | Marcus Boerger <helly@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23
24 #if HAVE_DBA
25
26 #include "php_ini.h"
27 #include <stdio.h>
28 #include <fcntl.h>
29 #ifdef HAVE_SYS_FILE_H
30 #include <sys/file.h>
31 #endif
32
33 #include "php_dba.h"
34 #include "ext/standard/info.h"
35 #include "ext/standard/php_string.h"
36 #include "ext/standard/flock_compat.h"
37
38 #include "php_gdbm.h"
39 #include "php_ndbm.h"
40 #include "php_dbm.h"
41 #include "php_cdb.h"
42 #include "php_db1.h"
43 #include "php_db2.h"
44 #include "php_db3.h"
45 #include "php_db4.h"
46 #include "php_flatfile.h"
47 #include "php_inifile.h"
48 #include "php_qdbm.h"
49 #include "php_tcadb.h"
50 #include "php_lmdb.h"
51 #include "dba_arginfo.h"
52
53 PHP_MINIT_FUNCTION(dba);
54 PHP_MSHUTDOWN_FUNCTION(dba);
55 PHP_MINFO_FUNCTION(dba);
56
57 ZEND_BEGIN_MODULE_GLOBALS(dba)
58 char *default_handler;
59 dba_handler *default_hptr;
60 ZEND_END_MODULE_GLOBALS(dba)
61
62 ZEND_DECLARE_MODULE_GLOBALS(dba)
63
64 #define DBA_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(dba, v)
65
66 static PHP_GINIT_FUNCTION(dba);
67
68 zend_module_entry dba_module_entry = {
69 STANDARD_MODULE_HEADER,
70 "dba",
71 ext_functions,
72 PHP_MINIT(dba),
73 PHP_MSHUTDOWN(dba),
74 NULL,
75 NULL,
76 PHP_MINFO(dba),
77 PHP_DBA_VERSION,
78 PHP_MODULE_GLOBALS(dba),
79 PHP_GINIT(dba),
80 NULL,
81 NULL,
82 STANDARD_MODULE_PROPERTIES_EX
83 };
84
85 #ifdef COMPILE_DL_DBA
86 #ifdef ZTS
87 ZEND_TSRMLS_CACHE_DEFINE()
88 #endif
ZEND_GET_MODULE(dba)89 ZEND_GET_MODULE(dba)
90 #endif
91
92 /* {{{ macromania */
93
94 #define DBA_ID_PARS \
95 zval *id; \
96 dba_info *info = NULL; \
97 int ac = ZEND_NUM_ARGS()
98
99 /* these are used to get the standard arguments */
100
101 /* {{{ php_dba_myke_key */
102 static size_t php_dba_make_key(zval *key, char **key_str, char **key_free)
103 {
104 if (Z_TYPE_P(key) == IS_ARRAY) {
105 zval *group, *name;
106 HashPosition pos;
107 size_t len;
108
109 if (zend_hash_num_elements(Z_ARRVAL_P(key)) != 2) {
110 zend_argument_error(NULL, 1, "must have exactly two elements: \"key\" and \"name\"");
111 return 0;
112 }
113 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(key), &pos);
114 group = zend_hash_get_current_data_ex(Z_ARRVAL_P(key), &pos);
115 zend_hash_move_forward_ex(Z_ARRVAL_P(key), &pos);
116 name = zend_hash_get_current_data_ex(Z_ARRVAL_P(key), &pos);
117 convert_to_string_ex(group);
118 convert_to_string_ex(name);
119 if (Z_STRLEN_P(group) == 0) {
120 *key_str = Z_STRVAL_P(name);
121 *key_free = NULL;
122 return Z_STRLEN_P(name);
123 }
124 len = spprintf(key_str, 0, "[%s]%s", Z_STRVAL_P(group), Z_STRVAL_P(name));
125 *key_free = *key_str;
126 return len;
127 } else {
128 zval tmp;
129 size_t len;
130
131 ZVAL_COPY(&tmp, key);
132 convert_to_string(&tmp);
133
134 len = Z_STRLEN(tmp);
135 if (len) {
136 *key_free = *key_str = estrndup(Z_STRVAL(tmp), Z_STRLEN(tmp));
137 }
138 zval_ptr_dtor(&tmp);
139 return len;
140 }
141 }
142 /* }}} */
143
144 #define DBA_GET2 \
145 zval *key; \
146 char *key_str, *key_free; \
147 size_t key_len; \
148 if (zend_parse_parameters(ac, "zr", &key, &id) == FAILURE) { \
149 RETURN_THROWS(); \
150 } \
151 if ((key_len = php_dba_make_key(key, &key_str, &key_free)) == 0) {\
152 RETURN_FALSE; \
153 }
154
155 #define DBA_GET2_3 \
156 zval *key; \
157 char *key_str, *key_free; \
158 size_t key_len; \
159 zend_long skip = 0; \
160 switch(ac) { \
161 case 2: \
162 if (zend_parse_parameters(ac, "zr", &key, &id) == FAILURE) { \
163 RETURN_THROWS(); \
164 } \
165 break; \
166 case 3: \
167 if (zend_parse_parameters(ac, "zlr", &key, &skip, &id) == FAILURE) { \
168 RETURN_THROWS(); \
169 } \
170 break; \
171 default: \
172 WRONG_PARAM_COUNT; \
173 } \
174 if ((key_len = php_dba_make_key(key, &key_str, &key_free)) == 0) {\
175 RETURN_FALSE; \
176 }
177
178
179 #define DBA_FETCH_RESOURCE(info, id) \
180 if ((info = (dba_info *)zend_fetch_resource2(Z_RES_P(id), "DBA identifier", le_db, le_pdb)) == NULL) { \
181 RETURN_THROWS(); \
182 }
183
184 #define DBA_FETCH_RESOURCE_WITH_ID(info, id) \
185 if ((info = (dba_info *)zend_fetch_resource2(Z_RES_P(id), "DBA identifier", le_db, le_pdb)) == NULL) { \
186 DBA_ID_DONE; \
187 RETURN_THROWS(); \
188 }
189
190 #define DBA_ID_GET2 DBA_ID_PARS; DBA_GET2; DBA_FETCH_RESOURCE_WITH_ID(info, id)
191 #define DBA_ID_GET2_3 DBA_ID_PARS; DBA_GET2_3; DBA_FETCH_RESOURCE_WITH_ID(info, id)
192
193 #define DBA_ID_DONE \
194 if (key_free) efree(key_free)
195 /* a DBA handler must have specific routines */
196
197 #define DBA_NAMED_HND(alias, name, flags) \
198 {\
199 #alias, flags, dba_open_##name, dba_close_##name, dba_fetch_##name, dba_update_##name, \
200 dba_exists_##name, dba_delete_##name, dba_firstkey_##name, dba_nextkey_##name, \
201 dba_optimize_##name, dba_sync_##name, dba_info_##name \
202 },
203
204 #define DBA_HND(name, flags) DBA_NAMED_HND(name, name, flags)
205
206 /* check whether the user has write access */
207 #define DBA_WRITE_CHECK \
208 if(info->mode != DBA_WRITER && info->mode != DBA_TRUNC && info->mode != DBA_CREAT) { \
209 php_error_docref(NULL, E_WARNING, "You cannot perform a modification to a database without proper access"); \
210 RETURN_FALSE; \
211 }
212
213 /* the same check, but with a call to DBA_ID_DONE before returning */
214 #define DBA_WRITE_CHECK_WITH_ID \
215 if(info->mode != DBA_WRITER && info->mode != DBA_TRUNC && info->mode != DBA_CREAT) { \
216 php_error_docref(NULL, E_WARNING, "You cannot perform a modification to a database without proper access"); \
217 DBA_ID_DONE; \
218 RETURN_FALSE; \
219 }
220
221 /* }}} */
222
223 /* {{{ globals */
224
225 static dba_handler handler[] = {
226 #if DBA_GDBM
227 DBA_HND(gdbm, DBA_LOCK_EXT) /* Locking done in library if set */
228 #endif
229 #if DBA_DBM
230 DBA_HND(dbm, DBA_LOCK_ALL) /* No lock in lib */
231 #endif
232 #if DBA_NDBM
233 DBA_HND(ndbm, DBA_LOCK_ALL) /* Could be done in library: filemode = 0644 + S_ENFMT */
234 #endif
235 #if DBA_CDB
236 DBA_HND(cdb, DBA_STREAM_OPEN|DBA_LOCK_ALL) /* No lock in lib */
237 #endif
238 #if DBA_CDB_BUILTIN
239 DBA_NAMED_HND(cdb_make, cdb, DBA_STREAM_OPEN|DBA_LOCK_ALL) /* No lock in lib */
240 #endif
241 #if DBA_DB1
242 DBA_HND(db1, DBA_LOCK_ALL) /* No lock in lib */
243 #endif
244 #if DBA_DB2
245 DBA_HND(db2, DBA_LOCK_ALL) /* No lock in lib */
246 #endif
247 #if DBA_DB3
248 DBA_HND(db3, DBA_LOCK_ALL) /* No lock in lib */
249 #endif
250 #if DBA_DB4
251 DBA_HND(db4, DBA_LOCK_ALL) /* No lock in lib */
252 #endif
253 #if DBA_INIFILE
254 DBA_HND(inifile, DBA_STREAM_OPEN|DBA_LOCK_ALL|DBA_CAST_AS_FD) /* No lock in lib */
255 #endif
256 #if DBA_FLATFILE
257 DBA_HND(flatfile, DBA_STREAM_OPEN|DBA_LOCK_ALL|DBA_NO_APPEND) /* No lock in lib */
258 #endif
259 #if DBA_QDBM
260 DBA_HND(qdbm, DBA_LOCK_EXT)
261 #endif
262 #if DBA_TCADB
263 DBA_HND(tcadb, DBA_LOCK_ALL)
264 #endif
265 #if DBA_LMDB
266 DBA_HND(lmdb, DBA_LOCK_EXT)
267 #endif
268 { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
269 };
270
271 #if DBA_FLATFILE
272 #define DBA_DEFAULT "flatfile"
273 #elif DBA_DB4
274 #define DBA_DEFAULT "db4"
275 #elif DBA_DB3
276 #define DBA_DEFAULT "db3"
277 #elif DBA_DB2
278 #define DBA_DEFAULT "db2"
279 #elif DBA_DB1
280 #define DBA_DEFAULT "db1"
281 #elif DBA_GDBM
282 #define DBA_DEFAULT "gdbm"
283 #elif DBA_NBBM
284 #define DBA_DEFAULT "ndbm"
285 #elif DBA_DBM
286 #define DBA_DEFAULT "dbm"
287 #elif DBA_QDBM
288 #define DBA_DEFAULT "qdbm"
289 #elif DBA_TCADB
290 #define DBA_DEFAULT "tcadb"
291 #elif DBA_LMDB
292 #define DBA_DEFAULT "lmdb"
293 #else
294 #define DBA_DEFAULT ""
295 #endif
296 /* cdb/cdb_make and ini are no option here */
297
298 static int le_db;
299 static int le_pdb;
300 /* }}} */
301
302 /* {{{ dba_fetch_resource
303 PHPAPI void dba_fetch_resource(dba_info **pinfo, zval **id)
304 {
305 dba_info *info;
306 DBA_ID_FETCH
307 *pinfo = info;
308 }
309 */
310 /* }}} */
311
312 /* {{{ dba_get_handler
313 PHPAPI dba_handler *dba_get_handler(const char* handler_name)
314 {
315 dba_handler *hptr;
316 for (hptr = handler; hptr->name && strcasecmp(hptr->name, handler_name); hptr++);
317 return hptr;
318 }
319 */
320 /* }}} */
321
322 /* {{{ dba_close */
dba_close(dba_info * info)323 static void dba_close(dba_info *info)
324 {
325 if (info->hnd) {
326 info->hnd->close(info);
327 }
328 if (info->path) {
329 pefree(info->path, info->flags&DBA_PERSISTENT);
330 }
331 if (info->fp && info->fp != info->lock.fp) {
332 if (info->flags & DBA_PERSISTENT) {
333 php_stream_pclose(info->fp);
334 } else {
335 php_stream_close(info->fp);
336 }
337 }
338 if (info->lock.fp) {
339 if (info->flags & DBA_PERSISTENT) {
340 php_stream_pclose(info->lock.fp);
341 } else {
342 php_stream_close(info->lock.fp);
343 }
344 }
345 if (info->lock.name) {
346 pefree(info->lock.name, info->flags&DBA_PERSISTENT);
347 }
348 pefree(info, info->flags&DBA_PERSISTENT);
349 }
350 /* }}} */
351
352 /* {{{ dba_close_rsrc */
dba_close_rsrc(zend_resource * rsrc)353 static void dba_close_rsrc(zend_resource *rsrc)
354 {
355 dba_info *info = (dba_info *)rsrc->ptr;
356
357 dba_close(info);
358 }
359 /* }}} */
360
361 /* {{{ dba_close_pe_rsrc_deleter */
dba_close_pe_rsrc_deleter(zval * el,void * pDba)362 int dba_close_pe_rsrc_deleter(zval *el, void *pDba)
363 {
364 if (Z_RES_P(el)->ptr == pDba) {
365 if (Z_DELREF_P(el) == 0) {
366 return ZEND_HASH_APPLY_REMOVE;
367 } else {
368 return ZEND_HASH_APPLY_KEEP | ZEND_HASH_APPLY_STOP;
369 }
370 } else {
371 return ZEND_HASH_APPLY_KEEP;
372 }
373 }
374 /* }}} */
375
376 /* {{{ dba_close_pe_rsrc */
dba_close_pe_rsrc(zend_resource * rsrc)377 static void dba_close_pe_rsrc(zend_resource *rsrc)
378 {
379 dba_info *info = (dba_info *)rsrc->ptr;
380
381 /* closes the resource by calling dba_close_rsrc() */
382 zend_hash_apply_with_argument(&EG(persistent_list), dba_close_pe_rsrc_deleter, info);
383 }
384 /* }}} */
385
386 /* {{{ PHP_INI */
ZEND_INI_MH(OnUpdateDefaultHandler)387 ZEND_INI_MH(OnUpdateDefaultHandler)
388 {
389 dba_handler *hptr;
390
391 if (!ZSTR_LEN(new_value)) {
392 DBA_G(default_hptr) = NULL;
393 return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
394 }
395
396 for (hptr = handler; hptr->name && strcasecmp(hptr->name, ZSTR_VAL(new_value)); hptr++);
397
398 if (!hptr->name) {
399 php_error_docref(NULL, E_WARNING, "No such handler: %s", ZSTR_VAL(new_value));
400 return FAILURE;
401 }
402 DBA_G(default_hptr) = hptr;
403 return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
404 }
405
406 PHP_INI_BEGIN()
407 STD_PHP_INI_ENTRY("dba.default_handler", DBA_DEFAULT, PHP_INI_ALL, OnUpdateDefaultHandler, default_handler, zend_dba_globals, dba_globals)
PHP_INI_END()408 PHP_INI_END()
409 /* }}} */
410
411 /* {{{ PHP_GINIT_FUNCTION */
412 static PHP_GINIT_FUNCTION(dba)
413 {
414 #if defined(COMPILE_DL_DBA) && defined(ZTS)
415 ZEND_TSRMLS_CACHE_UPDATE();
416 #endif
417 dba_globals->default_handler = "";
418 dba_globals->default_hptr = NULL;
419 }
420 /* }}} */
421
422 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(dba)423 PHP_MINIT_FUNCTION(dba)
424 {
425 REGISTER_INI_ENTRIES();
426 le_db = zend_register_list_destructors_ex(dba_close_rsrc, NULL, "dba", module_number);
427 le_pdb = zend_register_list_destructors_ex(dba_close_pe_rsrc, dba_close_rsrc, "dba persistent", module_number);
428 return SUCCESS;
429 }
430 /* }}} */
431
432 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(dba)433 PHP_MSHUTDOWN_FUNCTION(dba)
434 {
435 UNREGISTER_INI_ENTRIES();
436 return SUCCESS;
437 }
438 /* }}} */
439
440 #include "zend_smart_str.h"
441
442 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(dba)443 PHP_MINFO_FUNCTION(dba)
444 {
445 dba_handler *hptr;
446 smart_str handlers = {0};
447
448 for(hptr = handler; hptr->name; hptr++) {
449 smart_str_appends(&handlers, hptr->name);
450 smart_str_appendc(&handlers, ' ');
451 }
452
453 php_info_print_table_start();
454 php_info_print_table_row(2, "DBA support", "enabled");
455 if (handlers.s) {
456 smart_str_0(&handlers);
457 php_info_print_table_row(2, "Supported handlers", ZSTR_VAL(handlers.s));
458 smart_str_free(&handlers);
459 } else {
460 php_info_print_table_row(2, "Supported handlers", "none");
461 }
462 php_info_print_table_end();
463 DISPLAY_INI_ENTRIES();
464 }
465 /* }}} */
466
467 /* {{{ php_dba_update */
php_dba_update(INTERNAL_FUNCTION_PARAMETERS,int mode)468 static void php_dba_update(INTERNAL_FUNCTION_PARAMETERS, int mode)
469 {
470 size_t val_len;
471 zval *id;
472 dba_info *info = NULL;
473 int ac = ZEND_NUM_ARGS();
474 zval *key;
475 char *val;
476 char *key_str, *key_free;
477 size_t key_len;
478
479 if (zend_parse_parameters(ac, "zsr", &key, &val, &val_len, &id) == FAILURE) {
480 RETURN_THROWS();
481 }
482
483 if ((key_len = php_dba_make_key(key, &key_str, &key_free)) == 0) {
484 RETURN_FALSE;
485 }
486
487 DBA_FETCH_RESOURCE_WITH_ID(info, id);
488
489 DBA_WRITE_CHECK_WITH_ID;
490
491 if (info->hnd->update(info, key_str, key_len, val, val_len, mode) == SUCCESS) {
492 DBA_ID_DONE;
493 RETURN_TRUE;
494 }
495
496 DBA_ID_DONE;
497 RETURN_FALSE;
498 }
499 /* }}} */
500
501 #define FREENOW if(args) {int i; for (i=0; i<ac; i++) { zval_ptr_dtor(&args[i]); } efree(args);} if(key) efree(key)
502
503 /* {{{ php_find_dbm */
php_dba_find(const char * path)504 dba_info *php_dba_find(const char* path)
505 {
506 zend_resource *le;
507 dba_info *info;
508 zend_long numitems, i;
509
510 numitems = zend_hash_next_free_element(&EG(regular_list));
511 for (i=1; i<numitems; i++) {
512 if ((le = zend_hash_index_find_ptr(&EG(regular_list), i)) == NULL) {
513 continue;
514 }
515 if (le->type == le_db || le->type == le_pdb) {
516 info = (dba_info *)(le->ptr);
517 if (!strcmp(info->path, path)) {
518 return (dba_info *)(le->ptr);
519 }
520 }
521 }
522
523 return NULL;
524 }
525 /* }}} */
526
527 /* {{{ php_dba_open */
php_dba_open(INTERNAL_FUNCTION_PARAMETERS,int persistent)528 static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, int persistent)
529 {
530 zval *args = NULL;
531 int ac = ZEND_NUM_ARGS();
532 dba_mode_t modenr;
533 dba_info *info, *other;
534 dba_handler *hptr;
535 char *key = NULL, *error = NULL;
536 size_t keylen = 0;
537 int i;
538 int lock_mode, lock_flag, lock_dbf = 0;
539 char *file_mode;
540 char mode[4], *pmode, *lock_file_mode = NULL;
541 int persistent_flag = persistent ? STREAM_OPEN_PERSISTENT : 0;
542 zend_string *opened_path = NULL;
543 char *lock_name;
544 #ifdef PHP_WIN32
545 zend_bool restarted = 0;
546 zend_bool need_creation = 0;
547 #endif
548
549 if (ac < 2) {
550 WRONG_PARAM_COUNT;
551 }
552
553 /* we pass additional args to the respective handler */
554 args = safe_emalloc(ac, sizeof(zval), 0);
555 if (zend_get_parameters_array_ex(ac, args) != SUCCESS) {
556 efree(args);
557 WRONG_PARAM_COUNT;
558 }
559
560 /* we only take string arguments */
561 for (i = 0; i < ac; i++) {
562 ZVAL_STR(&args[i], zval_get_string(&args[i]));
563 keylen += Z_STRLEN(args[i]);
564 }
565
566 /* Exception during string conversion */
567 if (EG(exception)) {
568 FREENOW;
569 RETURN_THROWS();
570 }
571
572 if (persistent) {
573 zend_resource *le;
574
575 /* calculate hash */
576 key = safe_emalloc(keylen, 1, 1);
577 key[keylen] = '\0';
578 keylen = 0;
579
580 for(i = 0; i < ac; i++) {
581 memcpy(key+keylen, Z_STRVAL(args[i]), Z_STRLEN(args[i]));
582 keylen += Z_STRLEN(args[i]);
583 }
584
585 /* try to find if we already have this link in our persistent list */
586 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), key, keylen)) != NULL) {
587 FREENOW;
588
589 if (le->type != le_pdb) {
590 RETURN_FALSE;
591 }
592
593 info = (dba_info *)le->ptr;
594
595 GC_ADDREF(le);
596 RETURN_RES(zend_register_resource(info, le_pdb));
597 return;
598 }
599 }
600
601 if (ac==2) {
602 hptr = DBA_G(default_hptr);
603 if (!hptr) {
604 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "No default handler selected");
605 FREENOW;
606 RETURN_FALSE;
607 }
608 } else {
609 for (hptr = handler; hptr->name && strcasecmp(hptr->name, Z_STRVAL(args[2])); hptr++);
610 }
611
612 if (!hptr->name) {
613 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "No such handler: %s", Z_STRVAL(args[2]));
614 FREENOW;
615 RETURN_FALSE;
616 }
617
618 /* Check mode: [rwnc][fl]?t?
619 * r: Read
620 * w: Write
621 * n: Create/Truncate
622 * c: Create
623 *
624 * d: force lock on database file
625 * l: force lock on lck file
626 * -: ignore locking
627 *
628 * t: test open database, warning if locked
629 */
630 strlcpy(mode, Z_STRVAL(args[1]), sizeof(mode));
631 pmode = &mode[0];
632 if (pmode[0] && (pmode[1]=='d' || pmode[1]=='l' || pmode[1]=='-')) { /* force lock on db file or lck file or disable locking */
633 switch (pmode[1]) {
634 case 'd':
635 lock_dbf = 1;
636 if ((hptr->flags & DBA_LOCK_ALL) == 0) {
637 lock_flag = (hptr->flags & DBA_LOCK_ALL);
638 break;
639 }
640 /* no break */
641 case 'l':
642 lock_flag = DBA_LOCK_ALL;
643 if ((hptr->flags & DBA_LOCK_ALL) == 0) {
644 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_NOTICE, "Handler %s does locking internally", hptr->name);
645 }
646 break;
647 default:
648 case '-':
649 if ((hptr->flags & DBA_LOCK_ALL) == 0) {
650 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Locking cannot be disabled for handler %s", hptr->name);
651 FREENOW;
652 RETURN_FALSE;
653 }
654 lock_flag = 0;
655 break;
656 }
657 } else {
658 lock_flag = (hptr->flags&DBA_LOCK_ALL);
659 lock_dbf = 1;
660 }
661 switch (*pmode++) {
662 case 'r':
663 modenr = DBA_READER;
664 lock_mode = (lock_flag & DBA_LOCK_READER) ? LOCK_SH : 0;
665 file_mode = "r";
666 break;
667 case 'w':
668 modenr = DBA_WRITER;
669 lock_mode = (lock_flag & DBA_LOCK_WRITER) ? LOCK_EX : 0;
670 file_mode = "r+b";
671 break;
672 case 'c': {
673 #ifdef PHP_WIN32
674 if (hptr->flags & (DBA_NO_APPEND|DBA_CAST_AS_FD)) {
675 php_stream_statbuf ssb;
676 need_creation = (SUCCESS != php_stream_stat_path(Z_STRVAL(args[0]), &ssb));
677 }
678 #endif
679 modenr = DBA_CREAT;
680 lock_mode = (lock_flag & DBA_LOCK_CREAT) ? LOCK_EX : 0;
681 if (lock_mode) {
682 if (lock_dbf) {
683 /* the create/append check will be done on the lock
684 * when the lib opens the file it is already created
685 */
686 file_mode = "r+b"; /* read & write, seek 0 */
687 #ifdef PHP_WIN32
688 if (!need_creation) {
689 lock_file_mode = "r+b";
690 } else
691 #endif
692 lock_file_mode = "a+b"; /* append */
693 } else {
694 #ifdef PHP_WIN32
695 if (!need_creation) {
696 file_mode = "r+b";
697 } else
698 #endif
699 file_mode = "a+b"; /* append */
700 lock_file_mode = "w+b"; /* create/truncate */
701 }
702 } else {
703 #ifdef PHP_WIN32
704 if (!need_creation) {
705 file_mode = "r+b";
706 } else
707 #endif
708 file_mode = "a+b";
709 }
710 /* In case of the 'a+b' append mode, the handler is responsible
711 * to handle any rewind problems (see flatfile handler).
712 */
713 break;
714 }
715 case 'n':
716 modenr = DBA_TRUNC;
717 lock_mode = (lock_flag & DBA_LOCK_TRUNC) ? LOCK_EX : 0;
718 file_mode = "w+b";
719 break;
720 default:
721 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Illegal DBA mode");
722 FREENOW;
723 RETURN_FALSE;
724 }
725 if (!lock_file_mode) {
726 lock_file_mode = file_mode;
727 }
728 if (*pmode=='d' || *pmode=='l' || *pmode=='-') {
729 pmode++; /* done already - skip here */
730 }
731 if (*pmode=='t') {
732 pmode++;
733 if (!lock_flag) {
734 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "You cannot combine modifiers - (no lock) and t (test lock)");
735 FREENOW;
736 RETURN_FALSE;
737 }
738 if (!lock_mode) {
739 if ((hptr->flags & DBA_LOCK_ALL) == 0) {
740 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Handler %s uses its own locking which doesn't support mode modifier t (test lock)", hptr->name);
741 FREENOW;
742 RETURN_FALSE;
743 } else {
744 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Handler %s doesn't uses locking for this mode which makes modifier t (test lock) obsolete", hptr->name);
745 FREENOW;
746 RETURN_FALSE;
747 }
748 } else {
749 lock_mode |= LOCK_NB; /* test =: non blocking */
750 }
751 }
752 if (*pmode) {
753 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Illegal DBA mode");
754 FREENOW;
755 RETURN_FALSE;
756 }
757
758 info = pemalloc(sizeof(dba_info), persistent);
759 memset(info, 0, sizeof(dba_info));
760 info->path = pestrdup(Z_STRVAL(args[0]), persistent);
761 info->mode = modenr;
762 info->argc = ac - 3;
763 info->argv = args + 3;
764 info->flags = (hptr->flags & ~DBA_LOCK_ALL) | (lock_flag & DBA_LOCK_ALL) | (persistent ? DBA_PERSISTENT : 0);
765 info->lock.mode = lock_mode;
766
767 /* if any open call is a locking call:
768 * check if we already habe a locking call open that should block this call
769 * the problem is some systems would allow read during write
770 */
771 if (hptr->flags & DBA_LOCK_ALL) {
772 if ((other = php_dba_find(info->path)) != NULL) {
773 if ( ( (lock_mode&LOCK_EX) && (other->lock.mode&(LOCK_EX|LOCK_SH)) )
774 || ( (other->lock.mode&LOCK_EX) && (lock_mode&(LOCK_EX|LOCK_SH)) )
775 ) {
776 error = "Unable to establish lock (database file already open)"; /* force failure exit */
777 }
778 }
779 }
780
781 #ifdef PHP_WIN32
782 restart:
783 #endif
784 if (!error && lock_mode) {
785 if (lock_dbf) {
786 lock_name = Z_STRVAL(args[0]);
787 } else {
788 spprintf(&lock_name, 0, "%s.lck", info->path);
789 if (!strcmp(file_mode, "r")) {
790 /* when in read only mode try to use existing .lck file first */
791 /* do not log errors for .lck file while in read only mode on .lck file */
792 lock_file_mode = "rb";
793 info->lock.fp = php_stream_open_wrapper(lock_name, lock_file_mode, STREAM_MUST_SEEK|IGNORE_PATH|persistent_flag, &opened_path);
794 }
795 if (!info->lock.fp) {
796 /* when not in read mode or failed to open .lck file read only. now try again in create(write) mode and log errors */
797 lock_file_mode = "a+b";
798 } else {
799 if (opened_path) {
800 info->lock.name = pestrndup(ZSTR_VAL(opened_path), ZSTR_LEN(opened_path), persistent);
801 zend_string_release_ex(opened_path, 0);
802 }
803 }
804 }
805 if (!info->lock.fp) {
806 info->lock.fp = php_stream_open_wrapper(lock_name, lock_file_mode, STREAM_MUST_SEEK|REPORT_ERRORS|IGNORE_PATH|persistent_flag, &opened_path);
807 if (info->lock.fp) {
808 if (lock_dbf) {
809 /* replace the path info with the real path of the opened file */
810 pefree(info->path, persistent);
811 info->path = pestrndup(ZSTR_VAL(opened_path), ZSTR_LEN(opened_path), persistent);
812 }
813 /* now store the name of the lock */
814 info->lock.name = pestrndup(ZSTR_VAL(opened_path), ZSTR_LEN(opened_path), persistent);
815 zend_string_release_ex(opened_path, 0);
816 }
817 }
818 if (!lock_dbf) {
819 efree(lock_name);
820 }
821 if (!info->lock.fp) {
822 dba_close(info);
823 /* stream operation already wrote an error message */
824 FREENOW;
825 RETURN_FALSE;
826 }
827 if (!php_stream_supports_lock(info->lock.fp)) {
828 error = "Stream does not support locking";
829 }
830 if (php_stream_lock(info->lock.fp, lock_mode)) {
831 error = "Unable to establish lock"; /* force failure exit */
832 }
833 }
834
835 /* centralised open stream for builtin */
836 if (!error && (hptr->flags&DBA_STREAM_OPEN)==DBA_STREAM_OPEN) {
837 if (info->lock.fp && lock_dbf) {
838 info->fp = info->lock.fp; /* use the same stream for locking and database access */
839 } else {
840 info->fp = php_stream_open_wrapper(info->path, file_mode, STREAM_MUST_SEEK|REPORT_ERRORS|IGNORE_PATH|persistent_flag, NULL);
841 }
842 if (!info->fp) {
843 dba_close(info);
844 /* stream operation already wrote an error message */
845 FREENOW;
846 RETURN_FALSE;
847 }
848 if (hptr->flags & (DBA_NO_APPEND|DBA_CAST_AS_FD)) {
849 /* Needed because some systems do not allow to write to the original
850 * file contents with O_APPEND being set.
851 */
852 if (SUCCESS != php_stream_cast(info->fp, PHP_STREAM_AS_FD, (void*)&info->fd, 1)) {
853 php_error_docref(NULL, E_WARNING, "Could not cast stream");
854 dba_close(info);
855 FREENOW;
856 RETURN_FALSE;
857 #ifdef F_SETFL
858 } else if (modenr == DBA_CREAT) {
859 int flags = fcntl(info->fd, F_GETFL);
860 fcntl(info->fd, F_SETFL, flags & ~O_APPEND);
861 #elif defined(PHP_WIN32)
862 } else if (modenr == DBA_CREAT && need_creation && !restarted) {
863 if (info->lock.fp != NULL) {
864 php_stream_free(info->lock.fp, persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE);
865 }
866 if (info->fp != info->lock.fp) {
867 php_stream_free(info->fp, persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE);
868 }
869 info->fp = NULL;
870 info->lock.fp = NULL;
871 info->fd = -1;
872
873 pefree(info->lock.name, persistent);
874
875 lock_file_mode = "r+b";
876
877 restarted = 1;
878 goto restart;
879 #endif
880 }
881 }
882 }
883
884 if (error || hptr->open(info, &error) != SUCCESS) {
885 dba_close(info);
886 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Driver initialization failed for handler: %s%s%s", hptr->name, error?": ":"", error?error:"");
887 FREENOW;
888 RETURN_FALSE;
889 }
890
891 info->hnd = hptr;
892 info->argc = 0;
893 info->argv = NULL;
894
895 if (persistent) {
896 if (zend_register_persistent_resource(key, keylen, info, le_pdb) == NULL) {
897 dba_close(info);
898 php_error_docref2(NULL, Z_STRVAL(args[0]), Z_STRVAL(args[1]), E_WARNING, "Could not register persistent resource");
899 FREENOW;
900 RETURN_FALSE;
901 }
902 }
903
904 RETVAL_RES(zend_register_resource(info, (persistent ? le_pdb : le_db)));
905 FREENOW;
906 }
907 /* }}} */
908 #undef FREENOW
909
910 /* {{{ Opens path using the specified handler in mode persistently */
PHP_FUNCTION(dba_popen)911 PHP_FUNCTION(dba_popen)
912 {
913 php_dba_open(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
914 }
915 /* }}} */
916
917 /* {{{ Opens path using the specified handler in mode*/
PHP_FUNCTION(dba_open)918 PHP_FUNCTION(dba_open)
919 {
920 php_dba_open(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
921 }
922 /* }}} */
923
924 /* {{{ Closes database */
PHP_FUNCTION(dba_close)925 PHP_FUNCTION(dba_close)
926 {
927 zval *id;
928 dba_info *info = NULL;
929
930 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &id) == FAILURE) {
931 RETURN_THROWS();
932 }
933
934 DBA_FETCH_RESOURCE(info, id);
935
936 zend_list_close(Z_RES_P(id));
937 }
938 /* }}} */
939
940 /* {{{ Checks, if the specified key exists */
PHP_FUNCTION(dba_exists)941 PHP_FUNCTION(dba_exists)
942 {
943 DBA_ID_GET2;
944
945 if(info->hnd->exists(info, key_str, key_len) == SUCCESS) {
946 DBA_ID_DONE;
947 RETURN_TRUE;
948 }
949 DBA_ID_DONE;
950 RETURN_FALSE;
951 }
952 /* }}} */
953
954 /* {{{ Fetches the data associated with key */
PHP_FUNCTION(dba_fetch)955 PHP_FUNCTION(dba_fetch)
956 {
957 char *val;
958 size_t len = 0;
959 DBA_ID_GET2_3;
960
961 if (ac==3) {
962 if (!strcmp(info->hnd->name, "cdb")) {
963 if (skip < 0) {
964 php_error_docref(NULL, E_NOTICE, "Handler %s accepts only skip values greater than or equal to zero, using skip=0", info->hnd->name);
965 skip = 0;
966 }
967 } else if (!strcmp(info->hnd->name, "inifile")) {
968 /* "-1" is comparable to 0 but allows a non restrictive
969 * access which is faster. For example 'inifile' uses this
970 * to allow faster access when the key was already found
971 * using firstkey/nextkey. However explicitly setting the
972 * value to 0 ensures the first value.
973 */
974 if (skip < -1) {
975 php_error_docref(NULL, E_NOTICE, "Handler %s accepts only skip value -1 and greater, using skip=0", info->hnd->name);
976 skip = 0;
977 }
978 } else {
979 php_error_docref(NULL, E_NOTICE, "Handler %s does not support optional skip parameter, the value will be ignored", info->hnd->name);
980 skip = 0;
981 }
982 } else {
983 skip = 0;
984 }
985 if((val = info->hnd->fetch(info, key_str, key_len, skip, &len)) != NULL) {
986 DBA_ID_DONE;
987 RETVAL_STRINGL(val, len);
988 efree(val);
989 return;
990 }
991 DBA_ID_DONE;
992 RETURN_FALSE;
993 }
994 /* }}} */
995
996 /* {{{ Splits an inifile key into an array of the form array(0=>group,1=>value_name) but returns false if input is false or null */
PHP_FUNCTION(dba_key_split)997 PHP_FUNCTION(dba_key_split)
998 {
999 zval *zkey;
1000 char *key, *name;
1001 size_t key_len;
1002
1003 if (ZEND_NUM_ARGS() != 1) {
1004 WRONG_PARAM_COUNT;
1005 }
1006 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "z", &zkey) == SUCCESS) {
1007 if (Z_TYPE_P(zkey) == IS_NULL || (Z_TYPE_P(zkey) == IS_FALSE)) {
1008 RETURN_BOOL(0);
1009 }
1010 }
1011 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) {
1012 RETURN_THROWS();
1013 }
1014 array_init(return_value);
1015 if (key[0] == '[' && (name = strchr(key, ']')) != NULL) {
1016 add_next_index_stringl(return_value, key+1, name - (key + 1));
1017 add_next_index_stringl(return_value, name+1, key_len - (name - key + 1));
1018 } else {
1019 add_next_index_stringl(return_value, "", 0);
1020 add_next_index_stringl(return_value, key, key_len);
1021 }
1022 }
1023 /* }}} */
1024
1025 /* {{{ Resets the internal key pointer and returns the first key */
PHP_FUNCTION(dba_firstkey)1026 PHP_FUNCTION(dba_firstkey)
1027 {
1028 char *fkey;
1029 size_t len;
1030 zval *id;
1031 dba_info *info = NULL;
1032
1033 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &id) == FAILURE) {
1034 RETURN_THROWS();
1035 }
1036
1037 DBA_FETCH_RESOURCE(info, id);
1038
1039 fkey = info->hnd->firstkey(info, &len);
1040
1041 if (fkey) {
1042 RETVAL_STRINGL(fkey, len);
1043 efree(fkey);
1044 return;
1045 }
1046
1047 RETURN_FALSE;
1048 }
1049 /* }}} */
1050
1051 /* {{{ Returns the next key */
PHP_FUNCTION(dba_nextkey)1052 PHP_FUNCTION(dba_nextkey)
1053 {
1054 char *nkey;
1055 size_t len;
1056 zval *id;
1057 dba_info *info = NULL;
1058
1059 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &id) == FAILURE) {
1060 RETURN_THROWS();
1061 }
1062
1063 DBA_FETCH_RESOURCE(info, id);
1064
1065 nkey = info->hnd->nextkey(info, &len);
1066
1067 if (nkey) {
1068 RETVAL_STRINGL(nkey, len);
1069 efree(nkey);
1070 return;
1071 }
1072
1073 RETURN_FALSE;
1074 }
1075 /* }}} */
1076
1077 /* {{{ Deletes the entry associated with key
1078 If inifile: remove all other key lines */
PHP_FUNCTION(dba_delete)1079 PHP_FUNCTION(dba_delete)
1080 {
1081 DBA_ID_GET2;
1082
1083 DBA_WRITE_CHECK_WITH_ID;
1084
1085 if(info->hnd->delete(info, key_str, key_len) == SUCCESS)
1086 {
1087 DBA_ID_DONE;
1088 RETURN_TRUE;
1089 }
1090 DBA_ID_DONE;
1091 RETURN_FALSE;
1092 }
1093 /* }}} */
1094
1095 /* {{{ If not inifile: Insert value as key, return false, if key exists already
1096 If inifile: Add vakue as key (next instance of key) */
PHP_FUNCTION(dba_insert)1097 PHP_FUNCTION(dba_insert)
1098 {
1099 php_dba_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1100 }
1101 /* }}} */
1102
1103 /* {{{ Inserts value as key, replaces key, if key exists already
1104 If inifile: remove all other key lines */
PHP_FUNCTION(dba_replace)1105 PHP_FUNCTION(dba_replace)
1106 {
1107 php_dba_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1108 }
1109 /* }}} */
1110
1111 /* {{{ Optimizes (e.g. clean up, vacuum) database */
PHP_FUNCTION(dba_optimize)1112 PHP_FUNCTION(dba_optimize)
1113 {
1114 zval *id;
1115 dba_info *info = NULL;
1116
1117 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &id) == FAILURE) {
1118 RETURN_THROWS();
1119 }
1120
1121 DBA_FETCH_RESOURCE(info, id);
1122
1123 DBA_WRITE_CHECK;
1124
1125 if (info->hnd->optimize(info) == SUCCESS) {
1126 RETURN_TRUE;
1127 }
1128
1129 RETURN_FALSE;
1130 }
1131 /* }}} */
1132
1133 /* {{{ Synchronizes database */
PHP_FUNCTION(dba_sync)1134 PHP_FUNCTION(dba_sync)
1135 {
1136 zval *id;
1137 dba_info *info = NULL;
1138
1139 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &id) == FAILURE) {
1140 RETURN_THROWS();
1141 }
1142
1143 DBA_FETCH_RESOURCE(info, id);
1144
1145 if (info->hnd->sync(info) == SUCCESS) {
1146 RETURN_TRUE;
1147 }
1148
1149 RETURN_FALSE;
1150 }
1151 /* }}} */
1152
1153 /* {{{ List configured database handlers */
PHP_FUNCTION(dba_handlers)1154 PHP_FUNCTION(dba_handlers)
1155 {
1156 dba_handler *hptr;
1157 zend_bool full_info = 0;
1158
1159 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &full_info) == FAILURE) {
1160 RETURN_THROWS();
1161 }
1162
1163 array_init(return_value);
1164
1165 for(hptr = handler; hptr->name; hptr++) {
1166 if (full_info) {
1167 // TODO: avoid reallocation ???
1168 char *str = hptr->info(hptr, NULL);
1169 add_assoc_string(return_value, hptr->name, str);
1170 efree(str);
1171 } else {
1172 add_next_index_string(return_value, hptr->name);
1173 }
1174 }
1175 }
1176 /* }}} */
1177
1178 /* {{{ List opened databases */
PHP_FUNCTION(dba_list)1179 PHP_FUNCTION(dba_list)
1180 {
1181 zend_ulong numitems, i;
1182 zend_resource *le;
1183 dba_info *info;
1184
1185 if (zend_parse_parameters_none() == FAILURE) {
1186 RETURN_THROWS();
1187 }
1188
1189 array_init(return_value);
1190
1191 numitems = zend_hash_next_free_element(&EG(regular_list));
1192 for (i=1; i<numitems; i++) {
1193 if ((le = zend_hash_index_find_ptr(&EG(regular_list), i)) == NULL) {
1194 continue;
1195 }
1196 if (le->type == le_db || le->type == le_pdb) {
1197 info = (dba_info *)(le->ptr);
1198 add_index_string(return_value, i, info->path);
1199 }
1200 }
1201 }
1202 /* }}} */
1203
1204 #endif /* HAVE_DBA */
1205