1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2009 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.0 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_0.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 | **** WARNING **** | 16 | | 17 | This module makes use of unRAR - free utility for RAR archives. | 18 | Its license states that you MUST NOT use its code to develop | 19 | a RAR (WinRAR) compatible archiver. | 20 | Please, read unRAR license for full information. | 21 | unRAR & RAR copyrights are owned by Eugene Roshal | 22 +----------------------------------------------------------------------+ 23 | Author: Antony Dovgal <tony@daylessday.org> | 24 | Author: Gustavo Lopes <cataphract@php.net> | 25 +----------------------------------------------------------------------+ 26*/ 27 28/* $Id$ */ 29 30#ifdef HAVE_CONFIG_H 31#include "config.h" 32#endif 33 34#ifdef __cplusplus 35extern "C" { 36#endif 37 38#define _GNU_SOURCE 39#include <string.h> 40 41#ifdef PHP_WIN32 42# include <math.h> 43#endif 44 45#include <wchar.h> 46 47#include <php.h> 48#include <php_ini.h> 49#include <zend_exceptions.h> 50#include <ext/standard/info.h> 51#include <ext/spl/spl_exceptions.h> 52 53#if HAVE_RAR 54 55#include "php_rar.h" 56 57/* {{{ Function prototypes for functions with internal linkage */ 58static void _rar_fix_wide(wchar_t *str, size_t max_size); 59static int _rar_unrar_volume_user_callback(char* dst_buffer, 60 zend_fcall_info *fci, 61 zend_fcall_info_cache *cache 62 TSRMLS_DC); 63static int _rar_make_userdata_fcall(zval *callable, 64 zend_fcall_info *fci, 65 zend_fcall_info_cache *cache TSRMLS_DC); 66/* }}} */ 67 68/* {{{ Functions with external linkage */ 69#if !defined(HAVE_STRNLEN) || !HAVE_STRNLEN 70size_t _rar_strnlen(const char *s, size_t maxlen) /* {{{ */ 71{ 72 char *r = memchr(s, '\0', maxlen); 73 return r ? r-s : maxlen; 74} 75/* }}} */ 76#endif 77 78/* From unicode.cpp 79 * I can't use that one directy because it takes a const wchar, not wchar_t. 80 * And I shouldn't because it's not a public API. 81 */ 82void _rar_wide_to_utf(const wchar_t *src, char *dest, size_t dest_size) /* {{{ */ 83{ 84 long dsize= (long) dest_size; 85 dsize--; 86 while (*src != 0 && --dsize >= 0) { 87 uint c =*(src++); 88 if (c < 0x80) 89 *(dest++) = (char) c; 90 else if (c < 0x800 && --dsize >= 0) { 91 *(dest++) = (char) (0xc0 | (c >> 6)); 92 *(dest++) = (0x80 | (c & 0x3f)); 93 } 94 else if (c < 0x10000 && (dsize -= 2) >= 0) { 95 *(dest++) = (char) (0xe0 | (c >> 12)); 96 *(dest++) = (0x80 | ((c >> 6) & 0x3f)); 97 *(dest++) = (0x80 | (c & 0x3f)); 98 } 99 else if (c < 0x200000 && (dsize -= 3) >= 0) { 100 *(dest++) = (char) (0xf0 | (c >> 18)); 101 *(dest++) = (0x80 | ((c >> 12) & 0x3f)); 102 *(dest++) = (0x80 | ((c >> 6) & 0x3f)); 103 *(dest++) = (0x80 | (c & 0x3f)); 104 } 105 } 106 *dest = 0; 107} 108/* }}} */ 109 110/* idem */ 111void _rar_utf_to_wide(const char *src, wchar_t *dest, size_t dest_size) /* {{{ */ 112{ 113 long dsize = (long) dest_size; 114 dsize--; 115 while (*src != 0) { 116 uint c = (unsigned char) *(src++), 117 d; 118 if (c < 0x80) 119 d = c; 120 else if ((c >> 5) == 6) { 121 if ((*src & 0xc0) != 0x80) 122 break; 123 d=((c & 0x1f) << 6)|(*src & 0x3f); 124 src++; 125 } 126 else if ((c>>4)==14) { 127 if ((src[0] & 0xc0) != 0x80 || (src[1] & 0xc0) != 0x80) 128 break; 129 d = ((c & 0xf) << 12) | ((src[0] & 0x3f) << 6) | (src[1] & 0x3f); 130 src += 2; 131 } 132 else if ((c>>3)==30) { 133 if ((src[0] & 0xc0) != 0x80 || (src[1] & 0xc0) != 0x80 || (src[2] & 0xc0) != 0x80) 134 break; 135 d = ((c & 7) << 18) | ((src[0] & 0x3f) << 12) | ((src[1] & 0x3f) << 6) | (src[2] & 0x3f); 136 src += 3; 137 } 138 else 139 break; 140 if (--dsize < 0) 141 break; 142 if (d > 0xffff) { 143 if (--dsize < 0 || d > 0x10ffff) 144 break; 145 *(dest++) = (wchar_t) (((d - 0x10000) >> 10) + 0xd800); 146 *(dest++) = (d & 0x3ff) + 0xdc00; 147 } 148 else 149 *(dest++) = (wchar_t) d; 150 } 151 *dest = 0; 152} 153/* }}} */ 154 155void _rar_destroy_userdata(rar_cb_user_data *udata) /* {{{ */ 156{ 157 assert(udata != NULL); 158 159 if (udata->password != NULL) { 160 efree(udata->password); 161 } 162 163 if (udata->callable != NULL) 164 zval_ptr_dtor(&udata->callable); 165 166 udata->password = NULL; 167 udata->callable = NULL; 168} 169/* }}} */ 170 171int _rar_find_file(struct RAROpenArchiveDataEx *open_data, /* IN */ 172 const char *const utf_file_name, /* IN */ 173 rar_cb_user_data *cb_udata, /* IN, must be managed outside */ 174 void **arc_handle, /* OUT: where to store rar archive handle */ 175 int *found, /* OUT */ 176 struct RARHeaderDataEx *header_data /* OUT, can be null */ 177 ) /* {{{ */ 178{ 179 wchar_t *file_name = NULL; 180 size_t utf_file_name_len = strlen(utf_file_name); 181 int ret; 182 183 file_name = ecalloc(utf_file_name_len + 1, sizeof *file_name); 184 _rar_utf_to_wide(utf_file_name, file_name, utf_file_name_len + 1); 185 ret = _rar_find_file_w(open_data, file_name, cb_udata, arc_handle, found, 186 header_data); 187 efree(file_name); 188 return ret; 189} 190/* }}} */ 191 192/* WARNING: It's the caller who must close the archive and manage the lifecycle 193of cb_udata (must be valid while the archive is opened). */ 194/* 195 * This function opens a RAR file and looks for the file with the 196 * name utf_file_name. 197 * If the operation is sucessful, arc_handle is populated with the RAR file 198 * handle, found is set to TRUE if the file is found and FALSE if it is not 199 * found; additionaly, the optional header_data is populated with the first 200 * header that corresponds to the request file. If the file is not found and 201 * header_data is specified, its values are undefined. 202 * Note that even when the file is not found, the caller must still close 203 * the archive. 204 */ 205int _rar_find_file_w(struct RAROpenArchiveDataEx *open_data, /* IN */ 206 const wchar_t *const file_name, /* IN */ 207 rar_cb_user_data *cb_udata, /* IN, must be managed outside */ 208 void **arc_handle, /* OUT: where to store rar archive handle */ 209 int *found, /* OUT */ 210 struct RARHeaderDataEx *header_data /* OUT, can be null */ 211 ) /* {{{ */ 212{ 213 int result, 214 process_result; 215 struct RARHeaderDataEx *used_header_data; 216 int retval = 0; /* success in rar parlance */ 217 218 assert(open_data != NULL); 219 assert(file_name != NULL); 220 assert(arc_handle != NULL); 221 assert(found != NULL); 222 *found = FALSE; 223 *arc_handle = NULL; 224 used_header_data = header_data != NULL ? 225 header_data : 226 ecalloc(1, sizeof *used_header_data); 227 228 *arc_handle = RAROpenArchiveEx(open_data); 229 if (*arc_handle == NULL) { 230 retval = open_data->OpenResult; 231 goto cleanup; 232 } 233 RARSetCallback(*arc_handle, _rar_unrar_callback, (LPARAM) cb_udata); 234 235 while ((result = RARReadHeaderEx(*arc_handle, used_header_data)) == 0) { 236#if WCHAR_MAX > 0xffff 237 _rar_fix_wide(used_header_data->FileNameW, NM); 238#endif 239 240 if (wcsncmp(used_header_data->FileNameW, file_name, NM) == 0) { 241 *found = TRUE; 242 goto cleanup; 243 } else { 244 process_result = RARProcessFile(*arc_handle, RAR_SKIP, NULL, NULL); 245 } 246 if (process_result != 0) { 247 retval = process_result; 248 goto cleanup; 249 } 250 } 251 252 if (result != 0 && result != 1) { 253 /* 0 indicates success, 1 indicates normal end of file */ 254 retval = result; 255 goto cleanup; 256 } 257 258cleanup: 259 if (header_data == NULL) 260 efree(used_header_data); 261 262 return retval; 263} 264/* }}} */ 265 266int _rar_find_file_p(struct RAROpenArchiveDataEx *open_data, /* IN */ 267 size_t position, /* IN */ 268 rar_cb_user_data *cb_udata, /* IN, must be managed outside */ 269 void **arc_handle, /* OUT: where to store rar archive handle */ 270 int *found, /* OUT */ 271 struct RARHeaderDataEx *header_data /* OUT, can be null */ 272 ) /* {{{ */ 273{ 274 int result, 275 process_result; 276 struct RARHeaderDataEx *used_header_data; 277 int retval = 0; /* success in rar parlance */ 278 size_t curpos = 0; 279 280 assert(open_data != NULL); 281 assert(arc_handle != NULL); 282 assert(found != NULL); 283 *found = FALSE; 284 *arc_handle = NULL; 285 used_header_data = header_data != NULL ? 286 header_data : 287 ecalloc(1, sizeof *used_header_data); 288 289 *arc_handle = RAROpenArchiveEx(open_data); 290 if (*arc_handle == NULL) { 291 retval = open_data->OpenResult; 292 goto cleanup; 293 } 294 RARSetCallback(*arc_handle, _rar_unrar_callback, (LPARAM) cb_udata); 295 296 while ((result = RARReadHeaderEx(*arc_handle, used_header_data)) == 0) { 297 /* skip entries that were split before with incrementing current pos */ 298 if ((used_header_data->Flags & 0x01U) || (curpos++ != position)) { 299 process_result = RARProcessFile(*arc_handle, RAR_SKIP, NULL, NULL); 300 } else { 301 *found = TRUE; 302 goto cleanup; 303 } 304 if (process_result != 0) { 305 retval = process_result; 306 goto cleanup; 307 } 308 } 309 310 if (result != 0 && result != 1) { 311 /* 0 indicates success, 1 indicates normal end of file */ 312 retval = result; 313 goto cleanup; 314 } 315 316cleanup: 317 if (header_data == NULL) 318 efree(used_header_data); 319 320 return retval; 321} 322 323/* An unRAR callback. 324 * Processes requests for passwords and missing volumes 325 * If there is (userland) volume find callback specified, try to use that 326 * callback to retrieve the name of the missing volume. Otherwise, or if 327 * the volume find callback returns null, cancel the operation. */ 328int CALLBACK _rar_unrar_callback(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2) /* {{{ */ 329{ 330 rar_cb_user_data *userdata = (rar_cb_user_data*) UserData; 331 TSRMLS_FETCH(); 332 333 if (msg == UCM_NEEDPASSWORD) { 334 /* user data is the password or null if none */ 335 char *password = userdata->password; 336 337 if (password == NULL || password[0] == '\0') { 338 /*php_error_docref(NULL TSRMLS_CC, E_WARNING, 339 "Password needed, but it has not been specified");*/ 340 return -1; 341 } 342 else { 343 strncpy((char *) P1, password, (size_t) P2); 344 assert((size_t) P2 > 0); 345 ((char *) P1)[(size_t) P2 - 1] = '\0'; 346 } 347 } 348 else if (msg == UCM_CHANGEVOLUME) { 349 if (((int) P2) == RAR_VOL_ASK) { 350 int ret, called_cb = 0; 351 if (userdata->callable == NULL) { 352 /* if there's no callback, abort */ 353 ret = -1; 354 } 355 else { 356 zend_fcall_info fci; 357 zend_fcall_info_cache cache; 358 /* make_userdata_fcall and volume_user_callback are chatty */ 359 if (_rar_make_userdata_fcall(userdata->callable, &fci, &cache 360 TSRMLS_CC) == SUCCESS) { 361 ret = _rar_unrar_volume_user_callback( 362 (char*) P1, &fci, &cache TSRMLS_CC); 363 called_cb = 1; 364 } 365 else { 366 ret = -1; 367 } 368 369 } 370 371 /* always a warning, never an exception here */ 372 if (ret == -1 && !called_cb) 373 php_error_docref(NULL TSRMLS_CC, E_WARNING, 374 "Volume %s was not found", (char*) P1); 375 376 return ret; 377 } 378 } 379 380 return 0; 381} 382/* }}} */ 383 384PHP_FUNCTION(rar_bogus_ctor) /* {{{ */ 385{ 386 /* This exception should not be thrown. The point is to add this as 387 * a class constructor and make it private. This code would be able to 388 * run only if the constructor were made public */ 389 zend_throw_exception(NULL, 390 "An object of this type cannot be created with the new operator.", 391 0 TSRMLS_CC); 392} 393/* }}} */ 394 395PHP_FUNCTION(rar_wrapper_cache_stats) /* {{{ */ 396{ 397 char *result = NULL; 398 int len; 399 400 if (zend_parse_parameters_none() == FAILURE) 401 return; 402 403 len = spprintf(&result, 0, "%u/%u (hits/misses)", 404 RAR_G(contents_cache).hits, RAR_G(contents_cache).misses); 405 406 RETURN_STRINGL(result, len, 0); 407} 408/* }}} */ 409/* }}} */ 410 411/* {{{ Functions with internal linkage */ 412/* 413 * Only relevant when sizeof(wchar_t) > 2 (so not windows). 414 * Removes the characters use value if > 0x10ffff; these are not 415 * valid UTF characters. 416 */ 417 418static void _rar_fix_wide(wchar_t *str, size_t max_size) /* {{{ */ 419{ 420 wchar_t *write, 421 *read, 422 *max_fin; 423 max_fin = str + max_size; 424 for (write = str, read = str; *read != L'\0' && read != max_fin; read++) { 425 if ((unsigned) *read <= 0x10ffff) 426 *(write++) = *read; 427 } 428 *write = L'\0'; 429} 430/* }}} */ 431 432/* called from the RAR callback; calls a user callback in case a volume was 433 * not found 434 * This function sends messages instead of calling _rar_handle_ext_error 435 * because, in case we're using exceptions, we want to let an exception with 436 * error code ERAR_EOPEN to be thrown. 437 */ 438static int _rar_unrar_volume_user_callback(char* dst_buffer, 439 zend_fcall_info *fci, 440 zend_fcall_info_cache *cache 441 TSRMLS_DC) /* {{{ */ 442{ 443 zval *failed_vol, 444 *retval_ptr = NULL, 445 **params; 446 int ret = -1; 447 448 MAKE_STD_ZVAL(failed_vol); 449 ZVAL_STRING(failed_vol, dst_buffer, 1); 450 params = &failed_vol; 451 fci->retval_ptr_ptr = &retval_ptr; 452 fci->params = ¶ms; 453 fci->param_count = 1; 454 455 if (zend_call_function(fci, cache TSRMLS_CC) != SUCCESS || 456 fci->retval_ptr_ptr == NULL || 457 *fci->retval_ptr_ptr == NULL) { 458 php_error_docref(NULL TSRMLS_CC, E_WARNING, 459 "Failure to call volume find callback"); 460 goto cleanup; 461 } 462 463 assert(*fci->retval_ptr_ptr == retval_ptr); 464 if (Z_TYPE_P(retval_ptr) == IS_NULL) { 465 /* let return -1 */ 466 } 467 else if (Z_TYPE_P(retval_ptr) == IS_STRING) { 468 char *filename = Z_STRVAL_P(retval_ptr); 469 char resolved_path[MAXPATHLEN]; 470 size_t resolved_len; 471 472 if (OPENBASEDIR_CHECKPATH(filename)) { 473 goto cleanup; 474 } 475 if (!expand_filepath(filename, resolved_path TSRMLS_CC)) { 476 php_error_docref(NULL TSRMLS_CC, E_WARNING, 477 "Cound not expand filename %s", filename); 478 goto cleanup; 479 } 480 481 resolved_len = _rar_strnlen(resolved_path, MAXPATHLEN); 482 /* dst_buffer size is NM; first condition won't happen short of a bug 483 * in expand_filepath */ 484 if (resolved_len == MAXPATHLEN || resolved_len > NM - 1) { 485 php_error_docref(NULL TSRMLS_CC, E_WARNING, 486 "Resolved path is too big for the unRAR library"); 487 goto cleanup; 488 } 489 490 strncpy(dst_buffer, resolved_path, NM); 491 dst_buffer[NM - 1] = '\0'; 492 ret = 1; /* try this new filename */ 493 } 494 else { 495 php_error_docref(NULL TSRMLS_CC, E_WARNING, 496 "Wrong type returned by volume find callback, " 497 "expected string or NULL"); 498 /* let return -1 */ 499 } 500 501cleanup: 502 zval_ptr_dtor(&failed_vol); 503 if (retval_ptr != NULL) 504 zval_ptr_dtor(&retval_ptr); 505 return ret; 506} 507/* }}} */ 508 509static int _rar_make_userdata_fcall(zval *callable, 510 zend_fcall_info *fci, 511 zend_fcall_info_cache *cache TSRMLS_DC) /* {{{ */ 512{ 513 char *error = NULL; 514 assert(callable != NULL); 515 assert(fci != NULL); 516 assert(cache != NULL); 517 518 *cache = empty_fcall_info_cache; 519 520#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2 521 if (zend_fcall_info_init(callable, fci, cache TSRMLS_CC) != SUCCESS) { 522 php_error_docref(NULL TSRMLS_CC, E_WARNING, 523 "The RAR file was not opened in rar_open/RarArchive::open with a " 524 "valid callback.", error); 525 return FAILURE; 526 } 527 else { 528 return SUCCESS; 529 } 530#else 531 if (zend_fcall_info_init(callable, IS_CALLABLE_STRICT, fci, cache, NULL, 532 &error TSRMLS_CC) == SUCCESS) { 533 if (error) { 534 php_error_docref(NULL TSRMLS_CC, E_STRICT, 535 "The RAR file was not opened with a strictly valid callback (%s)", 536 error); 537 efree(error); 538 } 539 return SUCCESS; 540 } 541 else { 542 if (error) { 543 php_error_docref(NULL TSRMLS_CC, E_STRICT, 544 "The RAR file was not opened with a valid callback (%s)", 545 error); 546 efree(error); 547 } 548 return FAILURE; 549 } 550#endif 551 552} 553/* }}} */ 554 555/* }}} */ 556 557#ifdef COMPILE_DL_RAR 558ZEND_GET_MODULE(rar) 559#endif 560 561/* {{{ arginfo */ 562ZEND_BEGIN_ARG_INFO_EX(arginfo_rar_open, 0, 0, 1) 563 ZEND_ARG_INFO(0, filename) 564 ZEND_ARG_INFO(0, password) 565 ZEND_ARG_INFO(0, volume_callback) 566ZEND_END_ARG_INFO() 567 568ZEND_BEGIN_ARG_INFO_EX(arginfo_rar_void_archmeth, 0, 0, 1) 569#if 0 /* don't turn on type hinting yet */ 570 ZEND_ARG_OBJ_INFO(0, rarfile, RarArchive, 0) 571#else 572 ZEND_ARG_INFO(0, rarfile) 573#endif 574ZEND_END_ARG_INFO() 575 576ZEND_BEGIN_ARG_INFO_EX(arginfo_rar_entry_get, 0, 0, 2) 577#if 0 /* don't turn on type hinting yet */ 578 ZEND_ARG_OBJ_INFO(0, rarfile, RarArchive, 0) 579#else 580 ZEND_ARG_INFO(0, rarfile) 581#endif 582 ZEND_ARG_INFO(0, filename) 583ZEND_END_ARG_INFO() 584 585ZEND_BEGIN_ARG_INFO_EX(arginfo_rar_allow_broken_set, 0, 0, 2) 586#if 0 /* don't turn on type hinting yet */ 587 ZEND_ARG_OBJ_INFO(0, rarfile, RarArchive, 0) 588#else 589 ZEND_ARG_INFO(0, rarfile) 590#endif 591 ZEND_ARG_INFO(0, allow_broken) 592ZEND_END_ARG_INFO() 593 594ZEND_BEGIN_ARG_INFO(arginfo_rar_wrapper_cache_stats, 0) 595ZEND_END_ARG_INFO() 596/* }}} */ 597 598/* {{{ rar_functions[] 599 * 600 */ 601static zend_function_entry rar_functions[] = { 602 PHP_FE(rar_open, arginfo_rar_open) 603 PHP_FE(rar_list, arginfo_rar_void_archmeth) 604 PHP_FE(rar_entry_get, arginfo_rar_entry_get) 605 PHP_FE(rar_solid_is, arginfo_rar_void_archmeth) 606 PHP_FE(rar_comment_get, arginfo_rar_void_archmeth) 607 PHP_FE(rar_broken_is, arginfo_rar_void_archmeth) 608 PHP_FE(rar_allow_broken_set, arginfo_rar_allow_broken_set) 609 PHP_FE(rar_close, arginfo_rar_void_archmeth) 610 PHP_FE(rar_wrapper_cache_stats, arginfo_rar_wrapper_cache_stats) 611 {NULL, NULL, NULL} 612}; 613/* }}} */ 614 615/* {{{ Globals' related activities */ 616ZEND_DECLARE_MODULE_GLOBALS(rar); 617 618static int _rar_array_apply_remove_first(void *pDest TSRMLS_DC) 619{ 620 return (ZEND_HASH_APPLY_STOP | ZEND_HASH_APPLY_REMOVE); 621} 622 623/* caller should increment zval refcount before calling this */ 624static void _rar_contents_cache_put(const char *key, 625 uint key_len, 626 zval *zv TSRMLS_DC) 627{ 628 rar_contents_cache *cc = &RAR_G(contents_cache); 629 int cur_size; 630 631 cur_size = zend_hash_num_elements(cc->data); 632 if (cur_size == cc->max_size) { 633 zend_hash_apply(cc->data, _rar_array_apply_remove_first TSRMLS_CC); 634 assert(zend_hash_num_elements(cc->data) == cur_size - 1); 635 } 636 zval_add_ref(&zv); 637 zend_hash_update(cc->data, key, key_len, &zv, sizeof(zv), NULL); 638} 639 640static zval *_rar_contents_cache_get(const char *key, 641 uint key_len TSRMLS_DC) 642{ 643 rar_contents_cache *cc = &RAR_G(contents_cache); 644 zval **element = NULL; 645 zend_hash_find(cc->data, key, key_len, (void **) &element); 646 647 if (element != NULL) { 648 cc->hits++; 649 zval_add_ref(element); 650 return *element; 651 } 652 else { 653 cc->misses++; 654 return NULL; 655 } 656} 657 658/* ZEND_MODULE_GLOBALS_CTOR_D declares it receiving zend_rar_globals*, 659 * which is incompatible; once cast into ts_allocate_ctor by the macro, 660 * ZEND_INIT_MODULE_GLOBALS, it cannot (per the spec) be used. */ 661static void ZEND_MODULE_GLOBALS_CTOR_N(rar)(void *arg TSRMLS_DC) /* {{{ */ 662{ 663 zend_rar_globals *rar_globals = arg; 664 rar_globals->contents_cache.max_size = 5; /* TODO make configurable */ 665 rar_globals->contents_cache.hits = 0; 666 rar_globals->contents_cache.misses = 0; 667 rar_globals->contents_cache.put = _rar_contents_cache_put; 668 rar_globals->contents_cache.get = _rar_contents_cache_get; 669 rar_globals->contents_cache.data = 670 pemalloc(sizeof *rar_globals->contents_cache.data, 1); 671 zend_hash_init(rar_globals->contents_cache.data, 672 rar_globals->contents_cache.max_size, NULL, 673 ZVAL_PTR_DTOR, 1); 674} 675/* }}} */ 676 677static void ZEND_MODULE_GLOBALS_DTOR_N(rar)(void *arg TSRMLS_DC) /* {{{ */ 678{ 679 zend_rar_globals *rar_globals = arg; 680 zend_hash_destroy(rar_globals->contents_cache.data); 681 pefree(rar_globals->contents_cache.data, 1); 682} 683/* }}} */ 684/* }}} */ 685 686/* {{{ ZEND_MODULE_STARTUP */ 687ZEND_MODULE_STARTUP_D(rar) 688{ 689 minit_rararch(TSRMLS_C); 690 minit_rarentry(TSRMLS_C); 691 minit_rarerror(TSRMLS_C); 692 693 /* This doesn't work, it tries to call the destructor after the 694 * module has been unloaded. This information is in the zend_module_entry 695 * instead; that information is correctly used before the module is 696 * unloaded */ 697 /* ZEND_INIT_MODULE_GLOBALS(rar, ZEND_MODULE_GLOBALS_CTOR_N(rar), 698 ZEND_MODULE_GLOBALS_DTOR_N(rar)); */ 699 700 php_register_url_stream_wrapper("rar", &php_stream_rar_wrapper TSRMLS_CC); 701 702 REGISTER_LONG_CONSTANT("RAR_HOST_MSDOS", HOST_MSDOS, CONST_CS | CONST_PERSISTENT); 703 REGISTER_LONG_CONSTANT("RAR_HOST_OS2", HOST_OS2, CONST_CS | CONST_PERSISTENT); 704 REGISTER_LONG_CONSTANT("RAR_HOST_WIN32", HOST_WIN32, CONST_CS | CONST_PERSISTENT); 705 REGISTER_LONG_CONSTANT("RAR_HOST_UNIX", HOST_UNIX, CONST_CS | CONST_PERSISTENT); 706 REGISTER_LONG_CONSTANT("RAR_HOST_MACOS", HOST_MACOS, CONST_CS | CONST_PERSISTENT); 707 REGISTER_LONG_CONSTANT("RAR_HOST_BEOS", HOST_BEOS, CONST_CS | CONST_PERSISTENT); 708 /* PHP < 5.3 doesn't have the PHP_MAXPATHLEN constant */ 709#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 710 REGISTER_LONG_CONSTANT("RAR_MAXPATHLEN", MAXPATHLEN, CONST_CS | CONST_PERSISTENT); 711#endif 712 return SUCCESS; 713} 714/* }}} */ 715 716/* {{{ ZEND_MODULE_DEACTIVATE */ 717ZEND_MODULE_DEACTIVATE_D(rar) 718{ 719 /* clean cache on request shutdown */ 720 zend_hash_clean(RAR_G(contents_cache).data); 721 722 return SUCCESS; 723} 724/* }}} */ 725 726/* {{{ ZEND_MODULE_INFO */ 727ZEND_MODULE_INFO_D(rar) 728{ 729 char version[256]; 730 char api_version[256]; 731 732 php_info_print_table_start(); 733 php_info_print_table_header(2, "RAR support", "enabled"); 734 php_info_print_table_row(2, "RAR EXT version", PHP_RAR_VERSION); 735 736#if RARVER_BETA != 0 737 sprintf(version,"%d.%02d beta%d patch%d %d-%02d-%02d", RARVER_MAJOR, 738 RARVER_MINOR, RARVER_BETA, RARVER_PATCH, RARVER_YEAR, RARVER_MONTH, 739 RARVER_DAY); 740#else 741 sprintf(version,"%d.%02d patch%d %d-%02d-%02d", RARVER_MAJOR, RARVER_MINOR, 742 RARVER_PATCH, RARVER_YEAR, RARVER_MONTH, RARVER_DAY); 743#endif 744 745 sprintf(api_version,"%d extension %d", RAR_DLL_VERSION, 746 RAR_DLL_EXT_VERSION); 747 748 php_info_print_table_row(2, "UnRAR version", version); 749 php_info_print_table_row(2, "UnRAR API version", api_version); 750 php_info_print_table_end(); 751} 752/* }}} */ 753 754/* {{{ rar_module_entry 755 */ 756zend_module_entry rar_module_entry = { 757 STANDARD_MODULE_HEADER, 758 "rar", 759 rar_functions, 760 ZEND_MODULE_STARTUP_N(rar), 761 /* ZEND_MODULE_SHUTDOWN_N(rar), */ 762 NULL, 763 /* ZEND_MODULE_ACTIVATE_N(rar), */ 764 NULL, 765 ZEND_MODULE_DEACTIVATE_N(rar), 766 ZEND_MODULE_INFO_N(rar), 767 PHP_RAR_VERSION, 768 ZEND_MODULE_GLOBALS(rar), 769 ZEND_MODULE_GLOBALS_CTOR_N(rar), 770 ZEND_MODULE_GLOBALS_DTOR_N(rar), 771 NULL, /* post_deactivate_func */ 772 STANDARD_MODULE_PROPERTIES_EX, 773}; 774/* }}} */ 775 776#endif /* HAVE_RAR */ 777 778#ifdef __cplusplus 779} 780#endif 781 782/* 783 * Local variables: 784 * tab-width: 4 785 * c-basic-offset: 4 786 * End: 787 * vim600: noet sw=4 ts=4 fdm=marker 788 * vim<600: noet sw=4 ts=4 789 */ 790