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