PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/session - session.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 1177 1384 85.0 %
Date: 2019-07-17 Functions: 89 93 95.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 7                                                        |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1997-2018 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             :    |          Andrei Zmievski <andrei@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             : #ifdef PHP_WIN32
      29             : # include "win32/winutil.h"
      30             : # include "win32/time.h"
      31             : #else
      32             : # include <sys/time.h>
      33             : #endif
      34             : 
      35             : #include <sys/stat.h>
      36             : #include <fcntl.h>
      37             : 
      38             : #include "php_ini.h"
      39             : #include "SAPI.h"
      40             : #include "rfc1867.h"
      41             : #include "php_variables.h"
      42             : #include "php_session.h"
      43             : #include "ext/standard/php_random.h"
      44             : #include "ext/standard/php_var.h"
      45             : #include "ext/date/php_date.h"
      46             : #include "ext/standard/php_lcg.h"
      47             : #include "ext/standard/url_scanner_ex.h"
      48             : #include "ext/standard/info.h"
      49             : #include "zend_smart_str.h"
      50             : #include "ext/standard/url.h"
      51             : #include "ext/standard/basic_functions.h"
      52             : #include "ext/standard/head.h"
      53             : 
      54             : #include "mod_files.h"
      55             : #include "mod_user.h"
      56             : 
      57             : #ifdef HAVE_LIBMM
      58             : #include "mod_mm.h"
      59             : #endif
      60             : 
      61             : PHPAPI ZEND_DECLARE_MODULE_GLOBALS(ps)
      62             : 
      63             : static int php_session_rfc1867_callback(unsigned int event, void *event_data, void **extra);
      64             : static int (*php_session_rfc1867_orig_callback)(unsigned int event, void *event_data, void **extra);
      65             : static void php_session_track_init(void);
      66             : 
      67             : /* SessionHandler class */
      68             : zend_class_entry *php_session_class_entry;
      69             : 
      70             : /* SessionHandlerInterface */
      71             : zend_class_entry *php_session_iface_entry;
      72             : 
      73             : /* SessionIdInterface */
      74             : zend_class_entry *php_session_id_iface_entry;
      75             : 
      76             : /* SessionUpdateTimestampHandler class */
      77             : zend_class_entry *php_session_update_timestamp_class_entry;
      78             : 
      79             : /* SessionUpdateTimestampInterface */
      80             : zend_class_entry *php_session_update_timestamp_iface_entry;
      81             : 
      82             : #define PS_MAX_SID_LENGTH 256
      83             : 
      84             : /* ***********
      85             :    * Helpers *
      86             :    *********** */
      87             : 
      88             : #define IF_SESSION_VARS() \
      89             :         if (Z_ISREF_P(&PS(http_session_vars)) && Z_TYPE_P(Z_REFVAL(PS(http_session_vars))) == IS_ARRAY)
      90             : 
      91             : #define SESSION_CHECK_ACTIVE_STATE      \
      92             :         if (PS(session_status) == php_session_active) { \
      93             :                 php_error_docref(NULL, E_WARNING, "A session is active. You cannot change the session module's ini settings at this time");   \
      94             :                 return FAILURE; \
      95             :         }
      96             : 
      97             : #define SESSION_CHECK_OUTPUT_STATE                                                                              \
      98             :         if (SG(headers_sent) && stage != ZEND_INI_STAGE_DEACTIVATE) {                                                                                           \
      99             :                 php_error_docref(NULL, E_WARNING, "Headers already sent. You cannot change the session module's ini settings at this time");  \
     100             :                 return FAILURE;                                                                                                 \
     101             :         }
     102             : 
     103             : #define APPLY_TRANS_SID (PS(use_trans_sid) && !PS(use_only_cookies))
     104             : 
     105             : static int php_session_send_cookie(void);
     106             : static int php_session_abort(void);
     107             : 
     108             : /* Initialized in MINIT, readonly otherwise. */
     109             : static int my_module_number = 0;
     110             : 
     111             : /* Dispatched by RINIT and by php_session_destroy */
     112       25359 : static inline void php_rinit_session_globals(void) /* {{{ */
     113             : {
     114             :         /* Do NOT init PS(mod_user_names) here! */
     115             :         /* TODO: These could be moved to MINIT and removed. These should be initialized by php_rshutdown_session_globals() always when execution is finished. */
     116       25359 :         PS(id) = NULL;
     117       25359 :         PS(session_status) = php_session_none;
     118       25359 :         PS(in_save_handler) = 0;
     119       25359 :         PS(set_handler) = 0;
     120       25359 :         PS(mod_data) = NULL;
     121       25359 :         PS(mod_user_is_open) = 0;
     122       25359 :         PS(define_sid) = 1;
     123       25359 :         PS(session_vars) = NULL;
     124       25359 :         PS(module_number) = my_module_number;
     125       25359 :         ZVAL_UNDEF(&PS(http_session_vars));
     126       25359 : }
     127             : /* }}} */
     128             : 
     129             : /* Dispatched by RSHUTDOWN and by php_session_destroy */
     130       25411 : static inline void php_rshutdown_session_globals(void) /* {{{ */
     131             : {
     132             :         /* Do NOT destroy PS(mod_user_names) here! */
     133       25411 :         if (!Z_ISUNDEF(PS(http_session_vars))) {
     134         323 :                 zval_ptr_dtor(&PS(http_session_vars));
     135         323 :                 ZVAL_UNDEF(&PS(http_session_vars));
     136             :         }
     137       25411 :         if (PS(mod_data) || PS(mod_user_implemented)) {
     138         217 :                 zend_try {
     139         217 :                         PS(mod)->s_close(&PS(mod_data));
     140         217 :                 } zend_end_try();
     141             :         }
     142       25411 :         if (PS(id)) {
     143         313 :                 zend_string_release(PS(id));
     144         313 :                 PS(id) = NULL;
     145             :         }
     146             : 
     147       25411 :         if (PS(session_vars)) {
     148         299 :                 zend_string_release(PS(session_vars));
     149         299 :                 PS(session_vars) = NULL;
     150             :         }
     151             : 
     152             :         /* User save handlers may end up directly here by misuse, bugs in user script, etc. */
     153             :         /* Set session status to prevent error while restoring save handler INI value. */
     154       25411 :         PS(session_status) = php_session_none;
     155       25411 : }
     156             : /* }}} */
     157             : 
     158         252 : PHPAPI int php_session_destroy(void) /* {{{ */
     159             : {
     160         252 :         int retval = SUCCESS;
     161             : 
     162         252 :         if (PS(session_status) != php_session_active) {
     163          37 :                 php_error_docref(NULL, E_WARNING, "Trying to destroy uninitialized session");
     164          37 :                 return FAILURE;
     165             :         }
     166             : 
     167         215 :         if (PS(id) && PS(mod)->s_destroy(&PS(mod_data), PS(id)) == FAILURE) {
     168           1 :                 retval = FAILURE;
     169           1 :                 php_error_docref(NULL, E_WARNING, "Session object destruction failed");
     170             :         }
     171             : 
     172         215 :         php_rshutdown_session_globals();
     173         215 :         php_rinit_session_globals();
     174             : 
     175         215 :         return retval;
     176             : }
     177             : /* }}} */
     178             : 
     179           2 : PHPAPI void php_add_session_var(zend_string *name) /* {{{ */
     180             : {
     181           4 :         IF_SESSION_VARS() {
     182           2 :                 zval *sess_var = Z_REFVAL(PS(http_session_vars));
     183           2 :                 SEPARATE_ARRAY(sess_var);
     184           2 :                 if (!zend_hash_exists(Z_ARRVAL_P(sess_var), name)) {
     185             :                         zval empty_var;
     186           0 :                         ZVAL_NULL(&empty_var);
     187           0 :                         zend_hash_update(Z_ARRVAL_P(sess_var), name, &empty_var);
     188             :                 }
     189             :         }
     190           2 : }
     191             : /* }}} */
     192             : 
     193         216 : PHPAPI zval* php_set_session_var(zend_string *name, zval *state_val, php_unserialize_data_t *var_hash) /* {{{ */
     194             : {
     195         432 :         IF_SESSION_VARS() {
     196         216 :                 zval *sess_var = Z_REFVAL(PS(http_session_vars));
     197         216 :                 SEPARATE_ARRAY(sess_var);
     198         216 :                 return zend_hash_update(Z_ARRVAL_P(sess_var), name, state_val);
     199             :         }
     200           0 :         return NULL;
     201             : }
     202             : /* }}} */
     203             : 
     204         472 : PHPAPI zval* php_get_session_var(zend_string *name) /* {{{ */
     205             : {
     206         944 :         IF_SESSION_VARS() {
     207         472 :                 return zend_hash_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), name);
     208             :         }
     209           0 :         return NULL;
     210             : }
     211             : /* }}} */
     212             : 
     213         466 : static void php_session_track_init(void) /* {{{ */
     214             : {
     215             :         zval session_vars;
     216         466 :         zend_string *var_name = zend_string_init("_SESSION", sizeof("_SESSION") - 1, 0);
     217             :         /* Unconditionally destroy existing array -- possible dirty data */
     218         466 :         zend_delete_global_variable(var_name);
     219             : 
     220         466 :         if (!Z_ISUNDEF(PS(http_session_vars))) {
     221         143 :                 zval_ptr_dtor(&PS(http_session_vars));
     222             :         }
     223             : 
     224         466 :         array_init(&session_vars);
     225         466 :         ZVAL_NEW_REF(&PS(http_session_vars), &session_vars);
     226             :         Z_ADDREF_P(&PS(http_session_vars));
     227         466 :         zend_hash_update_ind(&EG(symbol_table), var_name, &PS(http_session_vars));
     228             :         zend_string_release(var_name);
     229         466 : }
     230             : /* }}} */
     231             : 
     232         326 : static zend_string *php_session_encode(void) /* {{{ */
     233             : {
     234         646 :         IF_SESSION_VARS() {
     235         320 :                 if (!PS(serializer)) {
     236           0 :                         php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to encode session object");
     237           0 :                         return NULL;
     238             :                 }
     239         320 :                 return PS(serializer)->encode();
     240             :         } else {
     241           6 :                 php_error_docref(NULL, E_WARNING, "Cannot encode non-existent session");
     242             :         }
     243           6 :         return NULL;
     244             : }
     245             : /* }}} */
     246             : 
     247         559 : static int php_session_decode(zend_string *data) /* {{{ */
     248             : {
     249         559 :         if (!PS(serializer)) {
     250           0 :                 php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
     251           0 :                 return FAILURE;
     252             :         }
     253         559 :         if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) {
     254          41 :                 php_session_destroy();
     255          41 :                 php_session_track_init();
     256          41 :                 php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
     257          41 :                 return FAILURE;
     258             :         }
     259         518 :         return SUCCESS;
     260             : }
     261             : /* }}} */
     262             : 
     263             : /*
     264             :  * Note that we cannot use the BASE64 alphabet here, because
     265             :  * it contains "/" and "+": both are unacceptable for simple inclusion
     266             :  * into URLs.
     267             :  */
     268             : 
     269             : static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-";
     270             : 
     271             : /* returns a pointer to the byte after the last valid character in out */
     272         269 : static size_t bin_to_readable(unsigned char *in, size_t inlen, char *out, char nbits) /* {{{ */
     273             : {
     274             :         unsigned char *p, *q;
     275             :         unsigned short w;
     276         269 :         size_t len = inlen;
     277             :         int mask;
     278             :         int have;
     279             : 
     280         269 :         p = (unsigned char *)in;
     281         269 :         q = (unsigned char *)in + inlen;
     282             : 
     283         269 :         w = 0;
     284         269 :         have = 0;
     285         269 :         mask = (1 << nbits) - 1;
     286             : 
     287        9232 :         while (inlen--) {
     288        8694 :                 if (have < nbits) {
     289        4347 :                         if (p < q) {
     290        4347 :                                 w |= *p++ << have;
     291        4347 :                                 have += 8;
     292             :                         } else {
     293             :                                 /* consumed everything? */
     294           0 :                                 if (have == 0) break;
     295             :                                 /* No? We need a final round */
     296           0 :                                 have = nbits;
     297             :                         }
     298             :                 }
     299             : 
     300             :                 /* consume nbits */
     301        8694 :                 *out++ = hexconvtab[w & mask];
     302        8694 :                 w >>= nbits;
     303        8694 :                 have -= nbits;
     304             :         }
     305             : 
     306         269 :         *out = '\0';
     307         269 :         return len;
     308             : }
     309             : /* }}} */
     310             : 
     311             : #define PS_EXTRA_RAND_BYTES 60
     312             : 
     313         269 : PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
     314             : {
     315             :         unsigned char rbuf[PS_MAX_SID_LENGTH + PS_EXTRA_RAND_BYTES];
     316             :         zend_string *outid;
     317             : 
     318             :         /* Read additional PS_EXTRA_RAND_BYTES just in case CSPRNG is not safe enough */
     319         269 :         if (php_random_bytes_throw(rbuf, PS(sid_length) + PS_EXTRA_RAND_BYTES) == FAILURE) {
     320           0 :                 return NULL;
     321             :         }
     322             : 
     323         538 :         outid = zend_string_alloc(PS(sid_length), 0);
     324         269 :         ZSTR_LEN(outid) = bin_to_readable(rbuf, PS(sid_length), ZSTR_VAL(outid), (char)PS(sid_bits_per_character));
     325             : 
     326         269 :         return outid;
     327             : }
     328             : /* }}} */
     329             : 
     330             : /* Default session id char validation function allowed by ps_modules.
     331             :  * If you change the logic here, please also update the error message in
     332             :  * ps_modules appropriately */
     333         383 : PHPAPI int php_session_valid_key(const char *key) /* {{{ */
     334             : {
     335             :         size_t len;
     336             :         const char *p;
     337             :         char c;
     338         383 :         int ret = SUCCESS;
     339             : 
     340       10574 :         for (p = key; (c = *p); p++) {
     341             :                 /* valid characters are a..z,A..Z,0..9 */
     342       10226 :                 if (!((c >= 'a' && c <= 'z')
     343        6122 :                                 || (c >= 'A' && c <= 'Z')
     344        6106 :                                 || (c >= '0' && c <= '9')
     345          33 :                                 || c == ','
     346             :                                 || c == '-')) {
     347           2 :                         ret = FAILURE;
     348           2 :                         break;
     349             :                 }
     350             :         }
     351             : 
     352         383 :         len = p - key;
     353             : 
     354             :         /* Somewhat arbitrary length limit here, but should be way more than
     355             :            anyone needs and avoids file-level warnings later on if we exceed MAX_PATH */
     356         383 :         if (len == 0 || len > PS_MAX_SID_LENGTH) {
     357           2 :                 ret = FAILURE;
     358             :         }
     359             : 
     360         383 :         return ret;
     361             : }
     362             : /* }}} */
     363             : 
     364             : 
     365         417 : static zend_long php_session_gc(zend_bool immediate) /* {{{ */
     366             : {
     367             :         int nrand;
     368         417 :         zend_long num = -1;
     369             : 
     370             :         /* GC must be done before reading session data. */
     371         417 :         if ((PS(mod_data) || PS(mod_user_implemented))) {
     372         417 :                 if (immediate) {
     373           2 :                         PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num);
     374           2 :                         return num;
     375             :                 }
     376         415 :                 nrand = (zend_long) ((float) PS(gc_divisor) * php_combined_lcg());
     377         415 :                 if (PS(gc_probability) > 0 && nrand < PS(gc_probability)) {
     378          15 :                         PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num);
     379             :                 }
     380             :         }
     381         415 :         return num;
     382             : } /* }}} */
     383             : 
     384         429 : static int php_session_initialize(void) /* {{{ */
     385             : {
     386         429 :         zend_string *val = NULL;
     387             : 
     388         429 :         PS(session_status) = php_session_active;
     389             : 
     390         429 :         if (!PS(mod)) {
     391           0 :                 PS(session_status) = php_session_disabled;
     392           0 :                 php_error_docref(NULL, E_WARNING, "No storage module chosen - failed to initialize session");
     393           0 :                 return FAILURE;
     394             :         }
     395             : 
     396             :         /* Open session handler first */
     397         429 :         if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE
     398             :                 /* || PS(mod_data) == NULL */ /* FIXME: open must set valid PS(mod_data) with success */
     399             :         ) {
     400           3 :                 php_session_abort();
     401           3 :                 php_error_docref(NULL, E_WARNING, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
     402           3 :                 return FAILURE;
     403             :         }
     404             : 
     405             :         /* If there is no ID, use session module to create one */
     406         426 :         if (!PS(id) || !ZSTR_VAL(PS(id))[0]) {
     407         253 :                 if (PS(id)) {
     408           1 :                         zend_string_release(PS(id));
     409             :                 }
     410         253 :                 PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
     411         253 :                 if (!PS(id)) {
     412           1 :                         php_session_abort();
     413           1 :                         zend_throw_error(NULL, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
     414           1 :                         return FAILURE;
     415             :                 }
     416         504 :                 if (PS(use_cookies)) {
     417         242 :                         PS(send_cookie) = 1;
     418             :                 }
     419         179 :         } else if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
     420           6 :                 PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == FAILURE) {
     421           3 :                 if (PS(id)) {
     422           3 :                         zend_string_release(PS(id));
     423             :                 }
     424           3 :                 PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
     425           3 :                 if (!PS(id)) {
     426           0 :                         PS(id) = php_session_create_id(NULL);
     427             :                 }
     428           3 :                 if (PS(use_cookies)) {
     429           3 :                         PS(send_cookie) = 1;
     430             :                 }
     431             :         }
     432             : 
     433         425 :         if (php_session_reset_id() == FAILURE) {
     434           0 :                 php_session_abort();
     435           0 :                 return FAILURE;
     436             :         }
     437             : 
     438             :         /* Read data */
     439         425 :         php_session_track_init();
     440         425 :         if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, PS(gc_maxlifetime)) == FAILURE) {
     441          10 :                 php_session_abort();
     442             :                 /* FYI: Some broken save handlers return FAILURE for non-existent session ID, this is incorrect */
     443          10 :                 php_error_docref(NULL, E_WARNING, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path));
     444          10 :                 return FAILURE;
     445             :         }
     446             : 
     447             :         /* GC must be done after read */
     448         415 :         php_session_gc(0);
     449             : 
     450         415 :         if (PS(session_vars)) {
     451          98 :                 zend_string_release(PS(session_vars));
     452          98 :                 PS(session_vars) = NULL;
     453             :         }
     454         415 :         if (val) {
     455         415 :                 if (PS(lazy_write)) {
     456         822 :                         PS(session_vars) = zend_string_copy(val);
     457             :                 }
     458         415 :                 php_session_decode(val);
     459         415 :                 zend_string_release(val);
     460             :         }
     461         415 :         return SUCCESS;
     462             : }
     463             : /* }}} */
     464             : 
     465         201 : static void php_session_save_current_state(int write) /* {{{ */
     466             : {
     467         201 :         int ret = FAILURE;
     468             : 
     469         201 :         if (write) {
     470         402 :                 IF_SESSION_VARS() {
     471         199 :                         if (PS(mod_data) || PS(mod_user_implemented)) {
     472             :                                 zend_string *val;
     473             : 
     474         197 :                                 val = php_session_encode();
     475         197 :                                 if (val) {
     476          98 :                                         if (PS(lazy_write) && PS(session_vars)
     477          94 :                                                 && PS(mod)->s_update_timestamp
     478          94 :                                                 && PS(mod)->s_update_timestamp != php_session_update_timestamp
     479          94 :                                                 && ZSTR_LEN(val) == ZSTR_LEN(PS(session_vars))
     480          32 :                                                 && !memcmp(ZSTR_VAL(val), ZSTR_VAL(PS(session_vars)), ZSTR_LEN(val))
     481             :                                         ) {
     482          27 :                                                 ret = PS(mod)->s_update_timestamp(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
     483             :                                         } else {
     484          71 :                                                 ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
     485             :                                         }
     486             :                                         zend_string_release(val);
     487             :                                 } else {
     488          99 :                                         ret = PS(mod)->s_write(&PS(mod_data), PS(id), ZSTR_EMPTY_ALLOC(), PS(gc_maxlifetime));
     489             :                                 }
     490             :                         }
     491             : 
     492         196 :                         if ((ret == FAILURE) && !EG(exception)) {
     493           3 :                                 if (!PS(mod_user_implemented)) {
     494           4 :                                         php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please "
     495             :                                                                          "verify that the current setting of session.save_path "
     496             :                                                                          "is correct (%s)",
     497           2 :                                                                          PS(mod)->s_name,
     498             :                                                                          PS(save_path));
     499             :                                 } else {
     500           1 :                                         php_error_docref(NULL, E_WARNING, "Failed to write session data using user "
     501             :                                                                          "defined save handler. (session.save_path: %s)", PS(save_path));
     502             :                                 }
     503             :                         }
     504             :                 }
     505             :         }
     506             : 
     507         198 :         if (PS(mod_data) || PS(mod_user_implemented)) {
     508         196 :                 PS(mod)->s_close(&PS(mod_data));
     509             :         }
     510         197 : }
     511             : /* }}} */
     512             : 
     513         518 : static void php_session_normalize_vars() /* {{{ */
     514             : {
     515             :         PS_ENCODE_VARS;
     516             : 
     517        1036 :         IF_SESSION_VARS() {
     518         952 :                 PS_ENCODE_LOOP(
     519             :                         if (Z_TYPE_P(struc) == IS_PTR) {
     520             :                                 zval *zv = (zval *)Z_PTR_P(struc);
     521             :                                 ZVAL_COPY_VALUE(struc, zv);
     522             :                                 ZVAL_UNDEF(zv);
     523             :                         }
     524             :                 );
     525             :         }
     526         518 : }
     527             : /* }}} */
     528             : 
     529             : /* *************************
     530             :    * INI Settings/Handlers *
     531             :    ************************* */
     532             : 
     533       25307 : static PHP_INI_MH(OnUpdateSaveHandler) /* {{{ */
     534             : {
     535             :         ps_module *tmp;
     536             : 
     537       25307 :         SESSION_CHECK_ACTIVE_STATE;
     538       25306 :         SESSION_CHECK_OUTPUT_STATE;
     539             : 
     540       25306 :         tmp = _php_find_ps_module(ZSTR_VAL(new_value));
     541             : 
     542       25306 :         if (PG(modules_activated) && !tmp) {
     543             :                 int err_type;
     544             : 
     545           1 :                 if (stage == ZEND_INI_STAGE_RUNTIME) {
     546           0 :                         err_type = E_WARNING;
     547             :                 } else {
     548           1 :                         err_type = E_ERROR;
     549             :                 }
     550             : 
     551             :                 /* Do not output error when restoring ini options. */
     552           1 :                 if (stage != ZEND_INI_STAGE_DEACTIVATE) {
     553           0 :                         php_error_docref(NULL, err_type, "Cannot find save handler '%s'", ZSTR_VAL(new_value));
     554             :                 }
     555             : 
     556           1 :                 return FAILURE;
     557             :         }
     558             : 
     559             :         /* "user" save handler should not be set by user */
     560       25305 :         if (!PS(set_handler) &&  tmp == ps_user_ptr) {
     561           5 :                 php_error_docref(NULL, E_RECOVERABLE_ERROR, "Cannot set 'user' save handler by ini_set() or session_module_name()");
     562           4 :                 return FAILURE;
     563             :         }
     564             : 
     565       25300 :         PS(default_mod) = PS(mod);
     566       25300 :         PS(mod) = tmp;
     567             : 
     568       25300 :         return SUCCESS;
     569             : }
     570             : /* }}} */
     571             : 
     572       25204 : static PHP_INI_MH(OnUpdateSerializer) /* {{{ */
     573             : {
     574             :         const ps_serializer *tmp;
     575             : 
     576       25204 :         SESSION_CHECK_ACTIVE_STATE;
     577       25203 :         SESSION_CHECK_OUTPUT_STATE;
     578             : 
     579       25203 :         tmp = _php_find_ps_serializer(ZSTR_VAL(new_value));
     580             : 
     581       25203 :         if (PG(modules_activated) && !tmp) {
     582             :                 int err_type;
     583             : 
     584           0 :                 if (stage == ZEND_INI_STAGE_RUNTIME) {
     585           0 :                         err_type = E_WARNING;
     586             :                 } else {
     587           0 :                         err_type = E_ERROR;
     588             :                 }
     589             : 
     590             :                 /* Do not output error when restoring ini options. */
     591           0 :                 if (stage != ZEND_INI_STAGE_DEACTIVATE) {
     592           0 :                         php_error_docref(NULL, err_type, "Cannot find serialization handler '%s'", ZSTR_VAL(new_value));
     593             :                 }
     594           0 :                 return FAILURE;
     595             :         }
     596       25203 :         PS(serializer) = tmp;
     597             : 
     598       25203 :         return SUCCESS;
     599             : }
     600             : /* }}} */
     601             : 
     602       25206 : static PHP_INI_MH(OnUpdateTransSid) /* {{{ */
     603             : {
     604       25206 :         SESSION_CHECK_ACTIVE_STATE;
     605       25203 :         SESSION_CHECK_OUTPUT_STATE;
     606             : 
     607       25203 :         if (!strncasecmp(ZSTR_VAL(new_value), "on", sizeof("on"))) {
     608           0 :                 PS(use_trans_sid) = (zend_bool) 1;
     609             :         } else {
     610       25203 :                 PS(use_trans_sid) = (zend_bool) atoi(ZSTR_VAL(new_value));
     611             :         }
     612             : 
     613       25203 :         return SUCCESS;
     614             : }
     615             : /* }}} */
     616             : 
     617             : 
     618       25247 : static PHP_INI_MH(OnUpdateSaveDir) /* {{{ */
     619             : {
     620       25247 :         SESSION_CHECK_ACTIVE_STATE;
     621       25246 :         SESSION_CHECK_OUTPUT_STATE;
     622             : 
     623             :         /* Only do the safemode/open_basedir check at runtime */
     624       25246 :         if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) {
     625             :                 char *p;
     626             : 
     627          42 :                 if (memchr(ZSTR_VAL(new_value), '\0', ZSTR_LEN(new_value)) != NULL) {
     628           0 :                         return FAILURE;
     629             :                 }
     630             : 
     631             :                 /* we do not use zend_memrchr() since path can contain ; itself */
     632          42 :                 if ((p = strchr(ZSTR_VAL(new_value), ';'))) {
     633             :                         char *p2;
     634           1 :                         p++;
     635           1 :                         if ((p2 = strchr(p, ';'))) {
     636           1 :                                 p = p2 + 1;
     637             :                         }
     638             :                 } else {
     639          41 :                         p = ZSTR_VAL(new_value);
     640             :                 }
     641             : 
     642          42 :                 if (PG(open_basedir) && *p && php_check_open_basedir(p)) {
     643           2 :                         return FAILURE;
     644             :                 }
     645             :         }
     646             : 
     647       25244 :         return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     648             : }
     649             : /* }}} */
     650             : 
     651             : 
     652       25226 : static PHP_INI_MH(OnUpdateName) /* {{{ */
     653             : {
     654       25226 :         SESSION_CHECK_ACTIVE_STATE;
     655       25225 :         SESSION_CHECK_OUTPUT_STATE;
     656             : 
     657             :         /* Numeric session.name won't work at all */
     658       50439 :         if ((!ZSTR_LEN(new_value) || is_numeric_string(ZSTR_VAL(new_value), ZSTR_LEN(new_value), NULL, NULL, 0))) {
     659             :                 int err_type;
     660             : 
     661          22 :                 if (stage == ZEND_INI_STAGE_RUNTIME || stage == ZEND_INI_STAGE_ACTIVATE || stage == ZEND_INI_STAGE_STARTUP) {
     662          22 :                         err_type = E_WARNING;
     663             :                 } else {
     664           0 :                         err_type = E_ERROR;
     665             :                 }
     666             : 
     667             :                 /* Do not output error when restoring ini options. */
     668          22 :                 if (stage != ZEND_INI_STAGE_DEACTIVATE) {
     669          22 :                         php_error_docref(NULL, err_type, "session.name cannot be a numeric or empty '%s'", ZSTR_VAL(new_value));
     670             :                 }
     671          22 :                 return FAILURE;
     672             :         }
     673             : 
     674       25203 :         return OnUpdateStringUnempty(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     675             : }
     676             : /* }}} */
     677             : 
     678             : 
     679       25346 : static PHP_INI_MH(OnUpdateCookieLifetime) /* {{{ */
     680             : {
     681       25346 :         SESSION_CHECK_ACTIVE_STATE;
     682       25346 :         SESSION_CHECK_OUTPUT_STATE;
     683       25346 :         if (atol(ZSTR_VAL(new_value)) < 0) {
     684           2 :                 php_error_docref(NULL, E_WARNING, "CookieLifetime cannot be negative");
     685           2 :                 return FAILURE;
     686             :         }
     687       25344 :         return OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     688             : }
     689             : /* }}} */
     690             : 
     691             : 
     692      100785 : static PHP_INI_MH(OnUpdateSessionLong) /* {{{ */
     693             : {
     694      100785 :         SESSION_CHECK_ACTIVE_STATE;
     695      100781 :         SESSION_CHECK_OUTPUT_STATE;
     696      100781 :         return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     697             : }
     698             : /* }}} */
     699             : 
     700             : 
     701      101019 : static PHP_INI_MH(OnUpdateSessionString) /* {{{ */
     702             : {
     703      101019 :         SESSION_CHECK_ACTIVE_STATE;
     704      101015 :         SESSION_CHECK_OUTPUT_STATE;
     705      101015 :         return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     706             : }
     707             : /* }}} */
     708             : 
     709             : 
     710      126118 : static PHP_INI_MH(OnUpdateSessionBool) /* {{{ */
     711             : {
     712      126118 :         SESSION_CHECK_OUTPUT_STATE;
     713      126118 :         SESSION_CHECK_ACTIVE_STATE;
     714      126113 :         return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     715             : }
     716             : /* }}} */
     717             : 
     718             : 
     719       25190 : static PHP_INI_MH(OnUpdateSidLength) /* {{{ */
     720             : {
     721             :         zend_long val;
     722       25190 :         char *endptr = NULL;
     723             : 
     724       25190 :         SESSION_CHECK_OUTPUT_STATE;
     725       25190 :         SESSION_CHECK_ACTIVE_STATE;
     726       25189 :         val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
     727       25189 :         if (endptr && (*endptr == '\0')
     728       25189 :                 && val >= 22 && val <= PS_MAX_SID_LENGTH) {
     729             :                 /* Numeric value */
     730       25189 :                 PS(sid_length) = val;
     731       25189 :                 return SUCCESS;
     732             :         }
     733             : 
     734           0 :         php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_length' must be between 22 and 256.");
     735           0 :         return FAILURE;
     736             : }
     737             : /* }}} */
     738             : 
     739       25187 : static PHP_INI_MH(OnUpdateSidBits) /* {{{ */
     740             : {
     741             :         zend_long val;
     742       25187 :         char *endptr = NULL;
     743             : 
     744       25187 :         SESSION_CHECK_OUTPUT_STATE;
     745       25187 :         SESSION_CHECK_ACTIVE_STATE;
     746       25186 :         val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
     747       25186 :         if (endptr && (*endptr == '\0')
     748       25186 :                 && val >= 4 && val <=6) {
     749             :                 /* Numeric value */
     750       25186 :                 PS(sid_bits_per_character) = val;
     751       25186 :                 return SUCCESS;
     752             :         }
     753             : 
     754           0 :         php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_bits_per_character' must be between 4 and 6.");
     755           0 :         return FAILURE;
     756             : }
     757             : /* }}} */
     758             : 
     759             : 
     760       25200 : static PHP_INI_MH(OnUpdateLazyWrite) /* {{{ */
     761             : {
     762       25200 :         SESSION_CHECK_ACTIVE_STATE;
     763       25199 :         SESSION_CHECK_OUTPUT_STATE;
     764       25199 :         return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
     765             : }
     766             : /* }}} */
     767             : 
     768             : 
     769             : 
     770       25188 : static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */
     771             : {
     772             :         int tmp;
     773       25188 :         tmp = zend_atoi(ZSTR_VAL(new_value), (int)ZSTR_LEN(new_value));
     774       25188 :         if(tmp < 0) {
     775           2 :                 php_error_docref(NULL, E_WARNING, "session.upload_progress.freq must be greater than or equal to zero");
     776           2 :                 return FAILURE;
     777             :         }
     778       25186 :         if(ZSTR_LEN(new_value) > 0 && ZSTR_VAL(new_value)[ZSTR_LEN(new_value)-1] == '%') {
     779       25172 :                 if(tmp > 100) {
     780           2 :                         php_error_docref(NULL, E_WARNING, "session.upload_progress.freq cannot be over 100%%");
     781           2 :                         return FAILURE;
     782             :                 }
     783       25170 :                 PS(rfc1867_freq) = -tmp;
     784             :         } else {
     785          14 :                 PS(rfc1867_freq) = tmp;
     786             :         }
     787       25184 :         return SUCCESS;
     788             : } /* }}} */
     789             : 
     790             : /* {{{ PHP_INI
     791             :  */
     792             : PHP_INI_BEGIN()
     793             :         STD_PHP_INI_ENTRY("session.save_path",          "",          PHP_INI_ALL, OnUpdateSaveDir,       save_path,          php_ps_globals,    ps_globals)
     794             :         STD_PHP_INI_ENTRY("session.name",               "PHPSESSID", PHP_INI_ALL, OnUpdateName,          session_name,       php_ps_globals,    ps_globals)
     795             :         PHP_INI_ENTRY("session.save_handler",           "files",     PHP_INI_ALL, OnUpdateSaveHandler)
     796             :         STD_PHP_INI_BOOLEAN("session.auto_start",       "0",         PHP_INI_PERDIR, OnUpdateBool,       auto_start,         php_ps_globals,    ps_globals)
     797             :         STD_PHP_INI_ENTRY("session.gc_probability",     "1",         PHP_INI_ALL, OnUpdateSessionLong,          gc_probability,     php_ps_globals,    ps_globals)
     798             :         STD_PHP_INI_ENTRY("session.gc_divisor",         "100",       PHP_INI_ALL, OnUpdateSessionLong,          gc_divisor,         php_ps_globals,    ps_globals)
     799             :         STD_PHP_INI_ENTRY("session.gc_maxlifetime",     "1440",      PHP_INI_ALL, OnUpdateSessionLong,          gc_maxlifetime,     php_ps_globals,    ps_globals)
     800             :         PHP_INI_ENTRY("session.serialize_handler",      "php",       PHP_INI_ALL, OnUpdateSerializer)
     801             :         STD_PHP_INI_ENTRY("session.cookie_lifetime",    "0",         PHP_INI_ALL, OnUpdateCookieLifetime,cookie_lifetime,    php_ps_globals,    ps_globals)
     802             :         STD_PHP_INI_ENTRY("session.cookie_path",        "/",         PHP_INI_ALL, OnUpdateSessionString, cookie_path,        php_ps_globals,    ps_globals)
     803             :         STD_PHP_INI_ENTRY("session.cookie_domain",      "",          PHP_INI_ALL, OnUpdateSessionString, cookie_domain,      php_ps_globals,    ps_globals)
     804             :         STD_PHP_INI_ENTRY("session.cookie_secure",      "0",         PHP_INI_ALL, OnUpdateSessionBool,   cookie_secure,      php_ps_globals,    ps_globals)
     805             :         STD_PHP_INI_ENTRY("session.cookie_httponly",    "0",         PHP_INI_ALL, OnUpdateSessionBool,   cookie_httponly,    php_ps_globals,    ps_globals)
     806             :         STD_PHP_INI_ENTRY("session.use_cookies",        "1",         PHP_INI_ALL, OnUpdateSessionBool,   use_cookies,        php_ps_globals,    ps_globals)
     807             :         STD_PHP_INI_ENTRY("session.use_only_cookies",   "1",         PHP_INI_ALL, OnUpdateSessionBool,   use_only_cookies,   php_ps_globals,    ps_globals)
     808             :         STD_PHP_INI_ENTRY("session.use_strict_mode",    "0",         PHP_INI_ALL, OnUpdateSessionBool,   use_strict_mode,    php_ps_globals,    ps_globals)
     809             :         STD_PHP_INI_ENTRY("session.referer_check",      "",          PHP_INI_ALL, OnUpdateSessionString, extern_referer_chk, php_ps_globals,    ps_globals)
     810             :         STD_PHP_INI_ENTRY("session.cache_limiter",      "nocache",   PHP_INI_ALL, OnUpdateSessionString, cache_limiter,      php_ps_globals,    ps_globals)
     811             :         STD_PHP_INI_ENTRY("session.cache_expire",       "180",       PHP_INI_ALL, OnUpdateSessionLong,   cache_expire,       php_ps_globals,    ps_globals)
     812             :         PHP_INI_ENTRY("session.use_trans_sid",          "0",         PHP_INI_ALL, OnUpdateTransSid)
     813             :         PHP_INI_ENTRY("session.sid_length",             "32",        PHP_INI_ALL, OnUpdateSidLength)
     814             :         PHP_INI_ENTRY("session.sid_bits_per_character", "4",         PHP_INI_ALL, OnUpdateSidBits)
     815             :         STD_PHP_INI_BOOLEAN("session.lazy_write",       "1",         PHP_INI_ALL, OnUpdateLazyWrite,     lazy_write,         php_ps_globals,    ps_globals)
     816             : 
     817             :         /* Upload progress */
     818             :         STD_PHP_INI_BOOLEAN("session.upload_progress.enabled",
     819             :                                                         "1",     ZEND_INI_PERDIR, OnUpdateBool,        rfc1867_enabled, php_ps_globals, ps_globals)
     820             :         STD_PHP_INI_BOOLEAN("session.upload_progress.cleanup",
     821             :                                                         "1",     ZEND_INI_PERDIR, OnUpdateBool,        rfc1867_cleanup, php_ps_globals, ps_globals)
     822             :         STD_PHP_INI_ENTRY("session.upload_progress.prefix",
     823             :                                              "upload_progress_", ZEND_INI_PERDIR, OnUpdateString,      rfc1867_prefix,  php_ps_globals, ps_globals)
     824             :         STD_PHP_INI_ENTRY("session.upload_progress.name",
     825             :                                   "PHP_SESSION_UPLOAD_PROGRESS", ZEND_INI_PERDIR, OnUpdateString,      rfc1867_name,    php_ps_globals, ps_globals)
     826             :         STD_PHP_INI_ENTRY("session.upload_progress.freq",  "1%", ZEND_INI_PERDIR, OnUpdateRfc1867Freq, rfc1867_freq,    php_ps_globals, ps_globals)
     827             :         STD_PHP_INI_ENTRY("session.upload_progress.min_freq",
     828             :                                                            "1",  ZEND_INI_PERDIR, OnUpdateReal,        rfc1867_min_freq,php_ps_globals, ps_globals)
     829             : 
     830             :         /* Commented out until future discussion */
     831             :         /* PHP_INI_ENTRY("session.encode_sources", "globals,track", PHP_INI_ALL, NULL) */
     832             : PHP_INI_END()
     833             : /* }}} */
     834             : 
     835             : /* ***************
     836             :    * Serializers *
     837             :    *************** */
     838          28 : PS_SERIALIZER_ENCODE_FUNC(php_serialize) /* {{{ */
     839             : {
     840          28 :         smart_str buf = {0};
     841             :         php_serialize_data_t var_hash;
     842             : 
     843          56 :         IF_SESSION_VARS() {
     844          28 :                 PHP_VAR_SERIALIZE_INIT(var_hash);
     845          28 :                 php_var_serialize(&buf, Z_REFVAL(PS(http_session_vars)), &var_hash);
     846          28 :                 PHP_VAR_SERIALIZE_DESTROY(var_hash);
     847             :         }
     848          28 :         return buf.s;
     849             : }
     850             : /* }}} */
     851             : 
     852          35 : PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
     853             : {
     854          35 :         const char *endptr = val + vallen;
     855             :         zval session_vars;
     856             :         php_unserialize_data_t var_hash;
     857             :         int result;
     858          35 :         zend_string *var_name = zend_string_init("_SESSION", sizeof("_SESSION") - 1, 0);
     859             : 
     860          35 :         ZVAL_NULL(&session_vars);
     861          35 :         PHP_VAR_UNSERIALIZE_INIT(var_hash);
     862          35 :         result = php_var_unserialize(
     863             :                 &session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash);
     864          35 :         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
     865          35 :         if (!result) {
     866           8 :                 zval_ptr_dtor(&session_vars);
     867           8 :                 ZVAL_NULL(&session_vars);
     868             :         }
     869             : 
     870          35 :         if (!Z_ISUNDEF(PS(http_session_vars))) {
     871          35 :                 zval_ptr_dtor(&PS(http_session_vars));
     872             :         }
     873          35 :         if (Z_TYPE(session_vars) == IS_NULL) {
     874           8 :                 array_init(&session_vars);
     875             :         }
     876          35 :         ZVAL_NEW_REF(&PS(http_session_vars), &session_vars);
     877             :         Z_ADDREF_P(&PS(http_session_vars));
     878          35 :         zend_hash_update_ind(&EG(symbol_table), var_name, &PS(http_session_vars));
     879             :         zend_string_release(var_name);
     880          35 :         return SUCCESS;
     881             : }
     882             : /* }}} */
     883             : 
     884             : #define PS_BIN_NR_OF_BITS 8
     885             : #define PS_BIN_UNDEF (1<<(PS_BIN_NR_OF_BITS-1))
     886             : #define PS_BIN_MAX (PS_BIN_UNDEF-1)
     887             : 
     888           1 : PS_SERIALIZER_ENCODE_FUNC(php_binary) /* {{{ */
     889             : {
     890           1 :         smart_str buf = {0};
     891             :         php_serialize_data_t var_hash;
     892             :         PS_ENCODE_VARS;
     893             : 
     894           1 :         PHP_VAR_SERIALIZE_INIT(var_hash);
     895             : 
     896           4 :         PS_ENCODE_LOOP(
     897             :                         if (ZSTR_LEN(key) > PS_BIN_MAX) continue;
     898             :                         smart_str_appendc(&buf, (unsigned char)ZSTR_LEN(key));
     899             :                         smart_str_appendl(&buf, ZSTR_VAL(key), ZSTR_LEN(key));
     900             :                         php_var_serialize(&buf, struc, &var_hash);
     901             :         );
     902             : 
     903             :         smart_str_0(&buf);
     904           1 :         PHP_VAR_SERIALIZE_DESTROY(var_hash);
     905             : 
     906           1 :         return buf.s;
     907             : }
     908             : /* }}} */
     909             : 
     910           3 : PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
     911             : {
     912             :         const char *p;
     913           3 :         const char *endptr = val + vallen;
     914             :         int namelen;
     915             :         zend_string *name;
     916             :         php_unserialize_data_t var_hash;
     917             :         zval *current, rv;
     918             : 
     919           3 :         PHP_VAR_UNSERIALIZE_INIT(var_hash);
     920             : 
     921           7 :         for (p = val; p < endptr; ) {
     922           2 :                 namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);
     923             : 
     924           2 :                 if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
     925           1 :                         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
     926           1 :                         return FAILURE;
     927             :                 }
     928             : 
     929           2 :                 name = zend_string_init(p + 1, namelen, 0);
     930           1 :                 p += namelen + 1;
     931           1 :                 current = var_tmp_var(&var_hash);
     932             : 
     933           1 :                 if (php_var_unserialize(current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash)) {
     934           1 :                         ZVAL_PTR(&rv, current);
     935           1 :                         php_set_session_var(name, &rv, &var_hash);
     936             :                 } else {
     937             :                         zend_string_release(name);
     938           0 :                         php_session_normalize_vars();
     939           0 :                         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
     940           0 :                         return FAILURE;
     941             :                 }
     942             :                 zend_string_release(name);
     943             :         }
     944             : 
     945           2 :         php_session_normalize_vars();
     946           2 :         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
     947             : 
     948           2 :         return SUCCESS;
     949             : }
     950             : /* }}} */
     951             : 
     952             : #define PS_DELIMITER '|'
     953             : 
     954         290 : PS_SERIALIZER_ENCODE_FUNC(php) /* {{{ */
     955             : {
     956         290 :         smart_str buf = {0};
     957             :         php_serialize_data_t var_hash;
     958             :         PS_ENCODE_VARS;
     959             : 
     960         290 :         PHP_VAR_SERIALIZE_INIT(var_hash);
     961             : 
     962        1071 :         PS_ENCODE_LOOP(
     963             :                 smart_str_appendl(&buf, ZSTR_VAL(key), ZSTR_LEN(key));
     964             :                 if (memchr(ZSTR_VAL(key), PS_DELIMITER, ZSTR_LEN(key))) {
     965             :                         PHP_VAR_SERIALIZE_DESTROY(var_hash);
     966             :                         smart_str_free(&buf);
     967             :                         return NULL;
     968             :                 }
     969             :                 smart_str_appendc(&buf, PS_DELIMITER);
     970             :                 php_var_serialize(&buf, struc, &var_hash);
     971             :         );
     972             : 
     973             :         smart_str_0(&buf);
     974             : 
     975         290 :         PHP_VAR_SERIALIZE_DESTROY(var_hash);
     976         290 :         return buf.s;
     977             : }
     978             : /* }}} */
     979             : 
     980         516 : PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
     981             : {
     982             :         const char *p, *q;
     983         516 :         const char *endptr = val + vallen;
     984             :         ptrdiff_t namelen;
     985             :         zend_string *name;
     986         516 :         int retval = SUCCESS;
     987             :         php_unserialize_data_t var_hash;
     988             :         zval *current, rv;
     989             : 
     990         516 :         PHP_VAR_UNSERIALIZE_INIT(var_hash);
     991             : 
     992         516 :         p = val;
     993             : 
     994        1245 :         while (p < endptr) {
     995         280 :                 q = p;
     996        2133 :                 while (*q != PS_DELIMITER) {
     997        1601 :                         if (++q >= endptr) goto break_outer_loop;
     998             :                 }
     999             : 
    1000         252 :                 namelen = q - p;
    1001         504 :                 name = zend_string_init(p, namelen, 0);
    1002         252 :                 q++;
    1003             : 
    1004         252 :                 current = var_tmp_var(&var_hash);
    1005         252 :                 if (php_var_unserialize(current, (const unsigned char **)&q, (const unsigned char *)endptr, &var_hash)) {
    1006         213 :                         ZVAL_PTR(&rv, current);
    1007         213 :                         php_set_session_var(name, &rv, &var_hash);
    1008             :                 } else {
    1009             :                         zend_string_release(name);
    1010          39 :                         retval = FAILURE;
    1011          39 :                         goto break_outer_loop;
    1012             :                 }
    1013             :                 zend_string_release(name);
    1014         213 :                 p = q;
    1015             :         }
    1016             : 
    1017         449 : break_outer_loop:
    1018         516 :         php_session_normalize_vars();
    1019             : 
    1020         516 :         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
    1021             : 
    1022         516 :         return retval;
    1023             : }
    1024             : /* }}} */
    1025             : 
    1026             : #define MAX_SERIALIZERS 32
    1027             : #define PREDEFINED_SERIALIZERS 3
    1028             : 
    1029             : static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = {
    1030             :         PS_SERIALIZER_ENTRY(php_serialize),
    1031             :         PS_SERIALIZER_ENTRY(php),
    1032             :         PS_SERIALIZER_ENTRY(php_binary)
    1033             : };
    1034             : 
    1035       25184 : PHPAPI int php_session_register_serializer(const char *name, zend_string *(*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */
    1036             : {
    1037       25184 :         int ret = FAILURE;
    1038             :         int i;
    1039             : 
    1040      100736 :         for (i = 0; i < MAX_SERIALIZERS; i++) {
    1041      100736 :                 if (ps_serializers[i].name == NULL) {
    1042       25184 :                         ps_serializers[i].name = name;
    1043       25184 :                         ps_serializers[i].encode = encode;
    1044       25184 :                         ps_serializers[i].decode = decode;
    1045       25184 :                         ps_serializers[i + 1].name = NULL;
    1046       25184 :                         ret = SUCCESS;
    1047       25184 :                         break;
    1048             :                 }
    1049             :         }
    1050       25184 :         return ret;
    1051             : }
    1052             : /* }}} */
    1053             : 
    1054             : /* *******************
    1055             :    * Storage Modules *
    1056             :    ******************* */
    1057             : 
    1058             : #define MAX_MODULES 32
    1059             : #define PREDEFINED_MODULES 2
    1060             : 
    1061             : static ps_module *ps_modules[MAX_MODULES + 1] = {
    1062             :         ps_files_ptr,
    1063             :         ps_user_ptr
    1064             : };
    1065             : 
    1066           0 : PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
    1067             : {
    1068           0 :         int ret = FAILURE;
    1069             :         int i;
    1070             : 
    1071           0 :         for (i = 0; i < MAX_MODULES; i++) {
    1072           0 :                 if (!ps_modules[i]) {
    1073           0 :                         ps_modules[i] = ptr;
    1074           0 :                         ret = SUCCESS;
    1075           0 :                         break;
    1076             :                 }
    1077             :         }
    1078           0 :         return ret;
    1079             : }
    1080             : /* }}} */
    1081             : 
    1082             : /* Dummy PS module function */
    1083           0 : PHPAPI int php_session_validate_sid(PS_VALIDATE_SID_ARGS) {
    1084           0 :         return SUCCESS;
    1085             : }
    1086             : 
    1087             : /* Dummy PS module function */
    1088           0 : PHPAPI int php_session_update_timestamp(PS_UPDATE_TIMESTAMP_ARGS) {
    1089           0 :         return SUCCESS;
    1090             : }
    1091             : 
    1092             : 
    1093             : /* ******************
    1094             :    * Cache Limiters *
    1095             :    ****************** */
    1096             : 
    1097             : typedef struct {
    1098             :         char *name;
    1099             :         void (*func)(void);
    1100             : } php_session_cache_limiter_t;
    1101             : 
    1102             : #define CACHE_LIMITER(name) _php_cache_limiter_##name
    1103             : #define CACHE_LIMITER_FUNC(name) static void CACHE_LIMITER(name)(void)
    1104             : #define CACHE_LIMITER_ENTRY(name) { #name, CACHE_LIMITER(name) },
    1105             : #define ADD_HEADER(a) sapi_add_header(a, strlen(a), 1);
    1106             : #define MAX_STR 512
    1107             : 
    1108             : static char *month_names[] = {
    1109             :         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    1110             :         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    1111             : };
    1112             : 
    1113             : static char *week_days[] = {
    1114             :         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
    1115             : };
    1116             : 
    1117           4 : static inline void strcpy_gmt(char *ubuf, time_t *when) /* {{{ */
    1118             : {
    1119             :         char buf[MAX_STR];
    1120             :         struct tm tm, *res;
    1121             :         int n;
    1122             : 
    1123           4 :         res = php_gmtime_r(when, &tm);
    1124             : 
    1125           4 :         if (!res) {
    1126           0 :                 ubuf[0] = '\0';
    1127           0 :                 return;
    1128             :         }
    1129             : 
    1130          16 :         n = slprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT", /* SAFE */
    1131           4 :                                 week_days[tm.tm_wday], tm.tm_mday,
    1132           8 :                                 month_names[tm.tm_mon], tm.tm_year + 1900,
    1133             :                                 tm.tm_hour, tm.tm_min,
    1134             :                                 tm.tm_sec);
    1135           4 :         memcpy(ubuf, buf, n);
    1136           4 :         ubuf[n] = '\0';
    1137             : }
    1138             : /* }}} */
    1139             : 
    1140           3 : static inline void last_modified(void) /* {{{ */
    1141             : {
    1142             :         const char *path;
    1143             :         zend_stat_t sb;
    1144             :         char buf[MAX_STR + 1];
    1145             : 
    1146           3 :         path = SG(request_info).path_translated;
    1147           3 :         if (path) {
    1148           3 :                 if (VCWD_STAT(path, &sb) == -1) {
    1149           0 :                         return;
    1150             :                 }
    1151             : 
    1152             : #define LAST_MODIFIED "Last-Modified: "
    1153           3 :                 memcpy(buf, LAST_MODIFIED, sizeof(LAST_MODIFIED) - 1);
    1154           3 :                 strcpy_gmt(buf + sizeof(LAST_MODIFIED) - 1, &sb.st_mtime);
    1155           3 :                 ADD_HEADER(buf);
    1156             :         }
    1157             : }
    1158             : /* }}} */
    1159             : 
    1160             : #define EXPIRES "Expires: "
    1161           1 : CACHE_LIMITER_FUNC(public) /* {{{ */
    1162             : {
    1163             :         char buf[MAX_STR + 1];
    1164             :         struct timeval tv;
    1165             :         time_t now;
    1166             : 
    1167           1 :         gettimeofday(&tv, NULL);
    1168           1 :         now = tv.tv_sec + PS(cache_expire) * 60;
    1169           1 :         memcpy(buf, EXPIRES, sizeof(EXPIRES) - 1);
    1170           1 :         strcpy_gmt(buf + sizeof(EXPIRES) - 1, &now);
    1171           1 :         ADD_HEADER(buf);
    1172             : 
    1173           1 :         snprintf(buf, sizeof(buf) , "Cache-Control: public, max-age=" ZEND_LONG_FMT, PS(cache_expire) * 60); /* SAFE */
    1174           1 :         ADD_HEADER(buf);
    1175             : 
    1176           1 :         last_modified();
    1177           1 : }
    1178             : /* }}} */
    1179             : 
    1180           2 : CACHE_LIMITER_FUNC(private_no_expire) /* {{{ */
    1181             : {
    1182             :         char buf[MAX_STR + 1];
    1183             : 
    1184           2 :         snprintf(buf, sizeof(buf), "Cache-Control: private, max-age=" ZEND_LONG_FMT, PS(cache_expire) * 60); /* SAFE */
    1185           2 :         ADD_HEADER(buf);
    1186             : 
    1187           2 :         last_modified();
    1188           2 : }
    1189             : /* }}} */
    1190             : 
    1191           1 : CACHE_LIMITER_FUNC(private) /* {{{ */
    1192             : {
    1193           1 :         ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
    1194           1 :         CACHE_LIMITER(private_no_expire)();
    1195           1 : }
    1196             : /* }}} */
    1197             : 
    1198         344 : CACHE_LIMITER_FUNC(nocache) /* {{{ */
    1199             : {
    1200         344 :         ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
    1201             : 
    1202             :         /* For HTTP/1.1 conforming clients */
    1203         344 :         ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate");
    1204             : 
    1205             :         /* For HTTP/1.0 conforming clients */
    1206         344 :         ADD_HEADER("Pragma: no-cache");
    1207         344 : }
    1208             : /* }}} */
    1209             : 
    1210             : static php_session_cache_limiter_t php_session_cache_limiters[] = {
    1211             :         CACHE_LIMITER_ENTRY(public)
    1212             :         CACHE_LIMITER_ENTRY(private)
    1213             :         CACHE_LIMITER_ENTRY(private_no_expire)
    1214             :         CACHE_LIMITER_ENTRY(nocache)
    1215             :         {0}
    1216             : };
    1217             : 
    1218         396 : static int php_session_cache_limiter(void) /* {{{ */
    1219             : {
    1220             :         php_session_cache_limiter_t *lim;
    1221             : 
    1222         396 :         if (PS(cache_limiter)[0] == '\0') return 0;
    1223         347 :         if (PS(session_status) != php_session_active) return -1;
    1224             : 
    1225         347 :         if (SG(headers_sent)) {
    1226           0 :                 const char *output_start_filename = php_output_get_start_filename();
    1227           0 :                 int output_start_lineno = php_output_get_start_lineno();
    1228             : 
    1229           0 :                 php_session_abort();
    1230           0 :                 if (output_start_filename) {
    1231           0 :                         php_error_docref(NULL, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
    1232             :                 } else {
    1233           0 :                         php_error_docref(NULL, E_WARNING, "Cannot send session cache limiter - headers already sent");
    1234             :                 }
    1235           0 :                 return -2;
    1236             :         }
    1237             : 
    1238        1382 :         for (lim = php_session_cache_limiters; lim->name; lim++) {
    1239        1382 :                 if (!strcasecmp(lim->name, PS(cache_limiter))) {
    1240         347 :                         lim->func();
    1241         347 :                         return 0;
    1242             :                 }
    1243             :         }
    1244             : 
    1245           0 :         return -1;
    1246             : }
    1247             : /* }}} */
    1248             : 
    1249             : /* *********************
    1250             :    * Cookie Management *
    1251             :    ********************* */
    1252             : 
    1253             : /*
    1254             :  * Remove already sent session ID cookie.
    1255             :  * It must be directly removed from SG(sapi_header) because sapi_add_header_ex()
    1256             :  * removes all of matching cookie. i.e. It deletes all of Set-Cookie headers.
    1257             :  */
    1258         347 : static void php_session_remove_cookie(void) {
    1259             :         sapi_header_struct *header;
    1260         347 :         zend_llist *l = &SG(sapi_headers).headers;
    1261             :         zend_llist_element *next;
    1262             :         zend_llist_element *current;
    1263             :         char *session_cookie;
    1264             :         zend_string *e_session_name;
    1265             :         size_t session_cookie_len;
    1266         347 :         size_t len = sizeof("Set-Cookie")-1;
    1267             : 
    1268         347 :         e_session_name = php_url_encode(PS(session_name), strlen(PS(session_name)));
    1269         347 :         spprintf(&session_cookie, 0, "Set-Cookie: %s=", ZSTR_VAL(e_session_name));
    1270             :         zend_string_free(e_session_name);
    1271             : 
    1272         347 :         session_cookie_len = strlen(session_cookie);
    1273         347 :         current = l->head;
    1274         701 :         while (current) {
    1275           7 :                 header = (sapi_header_struct *)(current->data);
    1276           7 :                 next = current->next;
    1277           7 :                 if (header->header_len > len && header->header[len] == ':'
    1278           1 :                         && !strncmp(header->header, session_cookie, session_cookie_len)) {
    1279           1 :                         if (current->prev) {
    1280           1 :                                 current->prev->next = next;
    1281             :                         } else {
    1282           0 :                                 l->head = next;
    1283             :                         }
    1284           1 :                         if (next) {
    1285           1 :                                 next->prev = current->prev;
    1286             :                         } else {
    1287           0 :                                 l->tail = current->prev;
    1288             :                         }
    1289           1 :                         sapi_free_header(header);
    1290           1 :                         efree(current);
    1291           1 :                         --l->count;
    1292             :                 }
    1293           7 :                 current = next;
    1294             :         }
    1295         347 :         efree(session_cookie);
    1296         347 : }
    1297             : 
    1298         347 : static int php_session_send_cookie(void) /* {{{ */
    1299             : {
    1300         347 :         smart_str ncookie = {0};
    1301         347 :         zend_string *date_fmt = NULL;
    1302             :         zend_string *e_session_name, *e_id;
    1303             : 
    1304         347 :         if (SG(headers_sent)) {
    1305           0 :                 const char *output_start_filename = php_output_get_start_filename();
    1306           0 :                 int output_start_lineno = php_output_get_start_lineno();
    1307             : 
    1308           0 :                 if (output_start_filename) {
    1309           0 :                         php_error_docref(NULL, E_WARNING, "Cannot send session cookie - headers already sent by (output started at %s:%d)", output_start_filename, output_start_lineno);
    1310             :                 } else {
    1311           0 :                         php_error_docref(NULL, E_WARNING, "Cannot send session cookie - headers already sent");
    1312             :                 }
    1313           0 :                 return FAILURE;
    1314             :         }
    1315             : 
    1316             :         /* URL encode session_name and id because they might be user supplied */
    1317         347 :         e_session_name = php_url_encode(PS(session_name), strlen(PS(session_name)));
    1318         347 :         e_id = php_url_encode(ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)));
    1319             : 
    1320             :         smart_str_appendl(&ncookie, "Set-Cookie: ", sizeof("Set-Cookie: ")-1);
    1321         347 :         smart_str_appendl(&ncookie, ZSTR_VAL(e_session_name), ZSTR_LEN(e_session_name));
    1322             :         smart_str_appendc(&ncookie, '=');
    1323         347 :         smart_str_appendl(&ncookie, ZSTR_VAL(e_id), ZSTR_LEN(e_id));
    1324             : 
    1325             :         zend_string_release(e_session_name);
    1326             :         zend_string_release(e_id);
    1327             : 
    1328         347 :         if (PS(cookie_lifetime) > 0) {
    1329             :                 struct timeval tv;
    1330             :                 time_t t;
    1331             : 
    1332           6 :                 gettimeofday(&tv, NULL);
    1333           6 :                 t = tv.tv_sec + PS(cookie_lifetime);
    1334             : 
    1335           6 :                 if (t > 0) {
    1336           6 :                         date_fmt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, t, 0);
    1337             :                         smart_str_appends(&ncookie, COOKIE_EXPIRES);
    1338           6 :                         smart_str_appendl(&ncookie, ZSTR_VAL(date_fmt), ZSTR_LEN(date_fmt));
    1339             :                         zend_string_release(date_fmt);
    1340             : 
    1341             :                         smart_str_appends(&ncookie, COOKIE_MAX_AGE);
    1342           6 :                         smart_str_append_long(&ncookie, PS(cookie_lifetime));
    1343             :                 }
    1344             :         }
    1345             : 
    1346         347 :         if (PS(cookie_path)[0]) {
    1347             :                 smart_str_appends(&ncookie, COOKIE_PATH);
    1348         347 :                 smart_str_appends(&ncookie, PS(cookie_path));
    1349             :         }
    1350             : 
    1351         347 :         if (PS(cookie_domain)[0]) {
    1352             :                 smart_str_appends(&ncookie, COOKIE_DOMAIN);
    1353           3 :                 smart_str_appends(&ncookie, PS(cookie_domain));
    1354             :         }
    1355             : 
    1356         347 :         if (PS(cookie_secure)) {
    1357             :                 smart_str_appends(&ncookie, COOKIE_SECURE);
    1358             :         }
    1359             : 
    1360         347 :         if (PS(cookie_httponly)) {
    1361             :                 smart_str_appends(&ncookie, COOKIE_HTTPONLY);
    1362             :         }
    1363             : 
    1364             :         smart_str_0(&ncookie);
    1365             : 
    1366         347 :         php_session_remove_cookie(); /* remove already sent session ID cookie */
    1367             :         /*      'replace' must be 0 here, else a previous Set-Cookie
    1368             :                 header, probably sent with setcookie() will be replaced! */
    1369         347 :         sapi_add_header_ex(estrndup(ZSTR_VAL(ncookie.s), ZSTR_LEN(ncookie.s)), ZSTR_LEN(ncookie.s), 0, 0);
    1370             :         smart_str_free(&ncookie);
    1371             : 
    1372         347 :         return SUCCESS;
    1373             : }
    1374             : /* }}} */
    1375             : 
    1376       25348 : PHPAPI ps_module *_php_find_ps_module(char *name) /* {{{ */
    1377             : {
    1378       25348 :         ps_module *ret = NULL;
    1379             :         ps_module **mod;
    1380             :         int i;
    1381             : 
    1382       26716 :         for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
    1383       26675 :                 if (*mod && !strcasecmp(name, (*mod)->s_name)) {
    1384       25307 :                         ret = *mod;
    1385       25307 :                         break;
    1386             :                 }
    1387             :         }
    1388       25348 :         return ret;
    1389             : }
    1390             : /* }}} */
    1391             : 
    1392       25213 : PHPAPI const ps_serializer *_php_find_ps_serializer(char *name) /* {{{ */
    1393             : {
    1394       25213 :         const ps_serializer *ret = NULL;
    1395             :         const ps_serializer *mod;
    1396             : 
    1397       50466 :         for (mod = ps_serializers; mod->name; mod++) {
    1398       50452 :                 if (!strcasecmp(name, mod->name)) {
    1399       25199 :                         ret = mod;
    1400       25199 :                         break;
    1401             :                 }
    1402             :         }
    1403       25213 :         return ret;
    1404             : }
    1405             : /* }}} */
    1406             : 
    1407          15 : static void ppid2sid(zval *ppid) {
    1408          15 :         ZVAL_DEREF(ppid);
    1409          15 :         if (Z_TYPE_P(ppid) == IS_STRING) {
    1410          28 :                 PS(id) = zend_string_init(Z_STRVAL_P(ppid), Z_STRLEN_P(ppid), 0);
    1411          14 :                 PS(send_cookie) = 0;
    1412             :         } else {
    1413           1 :                 PS(id) = NULL;
    1414           1 :                 PS(send_cookie) = 1;
    1415             :         }
    1416          15 : }
    1417             : 
    1418             : 
    1419         440 : PHPAPI int php_session_reset_id(void) /* {{{ */
    1420             : {
    1421         440 :         int module_number = PS(module_number);
    1422             :         zval *sid, *data, *ppid;
    1423             :         zend_bool apply_trans_sid;
    1424             : 
    1425         440 :         if (!PS(id)) {
    1426           0 :                 php_error_docref(NULL, E_WARNING, "Cannot set session ID - session ID is not initialized");
    1427           0 :                 return FAILURE;
    1428             :         }
    1429             : 
    1430         440 :         if (PS(use_cookies) && PS(send_cookie)) {
    1431         347 :                 php_session_send_cookie();
    1432         347 :                 PS(send_cookie) = 0;
    1433             :         }
    1434             : 
    1435             :         /* If the SID constant exists, destroy it. */
    1436             :         /* We must not delete any items in EG(zend_contants) */
    1437             :         /* zend_hash_str_del(EG(zend_constants), "sid", sizeof("sid") - 1); */
    1438         440 :         sid = zend_get_constant_str("SID", sizeof("SID") - 1);
    1439             : 
    1440         440 :         if (PS(define_sid)) {
    1441          45 :                 smart_str var = {0};
    1442             : 
    1443          45 :                 smart_str_appends(&var, PS(session_name));
    1444             :                 smart_str_appendc(&var, '=');
    1445          45 :                 smart_str_appends(&var, ZSTR_VAL(PS(id)));
    1446             :                 smart_str_0(&var);
    1447          45 :                 if (sid) {
    1448          19 :                         zend_string_release(Z_STR_P(sid));
    1449          19 :                         ZVAL_NEW_STR(sid, var.s);
    1450             :                 } else {
    1451          26 :                         REGISTER_STRINGL_CONSTANT("SID", ZSTR_VAL(var.s), ZSTR_LEN(var.s), 0);
    1452             :                         smart_str_free(&var);
    1453             :                 }
    1454             :         } else {
    1455         395 :                 if (sid) {
    1456         203 :                         zend_string_release(Z_STR_P(sid));
    1457         203 :                         ZVAL_EMPTY_STRING(sid);
    1458             :                 } else {
    1459         192 :                         REGISTER_STRINGL_CONSTANT("SID", "", 0, 0);
    1460             :                 }
    1461             :         }
    1462             : 
    1463             :         /* Apply trans sid if sid cookie is not set */
    1464         440 :         apply_trans_sid = 0;
    1465         440 :         if (APPLY_TRANS_SID) {
    1466          18 :                 apply_trans_sid = 1;
    1467          18 :                 if (PS(use_cookies) &&
    1468             :                         (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) {
    1469           9 :                         ZVAL_DEREF(data);
    1470          18 :                         if (Z_TYPE_P(data) == IS_ARRAY &&
    1471           9 :                                 (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) {
    1472           1 :                                 ZVAL_DEREF(ppid);
    1473           1 :                                 apply_trans_sid = 0;
    1474             :                         }
    1475             :                 }
    1476             :         }
    1477         440 :         if (apply_trans_sid) {
    1478             :                 zend_string *sname;
    1479          34 :                 sname = zend_string_init(PS(session_name), strlen(PS(session_name)), 0);
    1480          17 :                 php_url_scanner_reset_session_var(sname, 1); /* This may fail when session name has changed */
    1481             :                 zend_string_release(sname);
    1482          17 :                 php_url_scanner_add_session_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1);
    1483             :         }
    1484         440 :         return SUCCESS;
    1485             : }
    1486             : /* }}} */
    1487             : 
    1488             : 
    1489         410 : PHPAPI int php_session_start(void) /* {{{ */
    1490             : {
    1491             :         zval *ppid;
    1492             :         zval *data;
    1493             :         char *p, *value;
    1494             :         size_t lensess;
    1495             : 
    1496         410 :         switch (PS(session_status)) {
    1497           0 :                 case php_session_active:
    1498           0 :                         php_error(E_NOTICE, "A session had already been started - ignoring session_start()");
    1499           0 :                         return FAILURE;
    1500             :                         break;
    1501             : 
    1502           2 :                 case php_session_disabled:
    1503           2 :                         value = zend_ini_string("session.save_handler", sizeof("session.save_handler") - 1, 0);
    1504           2 :                         if (!PS(mod) && value) {
    1505           0 :                                 PS(mod) = _php_find_ps_module(value);
    1506           0 :                                 if (!PS(mod)) {
    1507           0 :                                         php_error_docref(NULL, E_WARNING, "Cannot find save handler '%s' - session startup failed", value);
    1508           0 :                                         return FAILURE;
    1509             :                                 }
    1510             :                         }
    1511           2 :                         value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler") - 1, 0);
    1512           2 :                         if (!PS(serializer) && value) {
    1513           2 :                                 PS(serializer) = _php_find_ps_serializer(value);
    1514           2 :                                 if (!PS(serializer)) {
    1515           2 :                                         php_error_docref(NULL, E_WARNING, "Cannot find serialization handler '%s' - session startup failed", value);
    1516           2 :                                         return FAILURE;
    1517             :                                 }
    1518             :                         }
    1519           0 :                         PS(session_status) = php_session_none;
    1520             :                         /* Fall through */
    1521             : 
    1522         408 :                 case php_session_none:
    1523             :                 default:
    1524             :                         /* Setup internal flags */
    1525         408 :                         PS(define_sid) = !PS(use_only_cookies); /* SID constant is defined when non-cookie ID is used */
    1526         408 :                         PS(send_cookie) = PS(use_cookies) || PS(use_only_cookies);
    1527             :         }
    1528             : 
    1529         408 :         lensess = strlen(PS(session_name));
    1530             : 
    1531             :         /*
    1532             :          * Cookies are preferred, because initially cookie and get
    1533             :          * variables will be available.
    1534             :          * URL/POST session ID may be used when use_only_cookies=Off.
    1535             :          * session.use_strice_mode=On prevents session adoption.
    1536             :          * Session based file upload progress uses non-cookie ID.
    1537             :          */
    1538             : 
    1539         408 :         if (!PS(id)) {
    1540         269 :                 if (PS(use_cookies) && (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) {
    1541         257 :                         ZVAL_DEREF(data);
    1542         257 :                         if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), lensess))) {
    1543          11 :                                 ppid2sid(ppid);
    1544          11 :                                 PS(send_cookie) = 0;
    1545          11 :                                 PS(define_sid) = 0;
    1546             :                         }
    1547             :                 }
    1548             :                 /* Initilize session ID from non cookie values */
    1549         269 :                 if (!PS(use_only_cookies)) {
    1550          18 :                         if (!PS(id) && (data = zend_hash_str_find(&EG(symbol_table), "_GET", sizeof("_GET") - 1))) {
    1551           9 :                                 ZVAL_DEREF(data);
    1552           9 :                                 if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), lensess))) {
    1553           3 :                                         ppid2sid(ppid);
    1554             :                                 }
    1555             :                         }
    1556          18 :                         if (!PS(id) && (data = zend_hash_str_find(&EG(symbol_table), "_POST", sizeof("_POST") - 1))) {
    1557           6 :                                 ZVAL_DEREF(data);
    1558           6 :                                 if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), lensess))) {
    1559           1 :                                         ppid2sid(ppid);
    1560             :                                 }
    1561             :                         }
    1562             :                         /* Check the REQUEST_URI symbol for a string of the form
    1563             :                          * '<session-name>=<session-id>' to allow URLs of the form
    1564             :                          * http://yoursite/<session-name>=<session-id>/script.php */
    1565          18 :                         if (!PS(id) && zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1) == SUCCESS &&
    1566           0 :                                 (data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI") - 1)) &&
    1567           0 :                                 Z_TYPE_P(data) == IS_STRING &&
    1568           0 :                                 (p = strstr(Z_STRVAL_P(data), PS(session_name))) &&
    1569           0 :                                 p[lensess] == '='
    1570             :                                 ) {
    1571             :                                 char *q;
    1572           0 :                                 p += lensess + 1;
    1573           0 :                                 if ((q = strpbrk(p, "/?\\"))) {
    1574           0 :                                         PS(id) = zend_string_init(p, q - p, 0);
    1575             :                                 }
    1576             :                         }
    1577             :                         /* Check whether the current request was referred to by
    1578             :                          * an external site which invalidates the previously found id. */
    1579          18 :                         if (PS(id) && PS(extern_referer_chk)[0] != '\0' &&
    1580           0 :                                 !Z_ISUNDEF(PG(http_globals)[TRACK_VARS_SERVER]) &&
    1581           0 :                                 (data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER") - 1)) &&
    1582           0 :                                 Z_TYPE_P(data) == IS_STRING &&
    1583           0 :                                 Z_STRLEN_P(data) != 0 &&
    1584           0 :                                 strstr(Z_STRVAL_P(data), PS(extern_referer_chk)) == NULL
    1585             :                         ) {
    1586           0 :                                 zend_string_release(PS(id));
    1587           0 :                                 PS(id) = NULL;
    1588             :                         }
    1589             :                 }
    1590             :         }
    1591             : 
    1592             :         /* Finally check session id for dangerous characters
    1593             :          * Security note: session id may be embedded in HTML pages.*/
    1594         408 :         if (PS(id) && strpbrk(ZSTR_VAL(PS(id)), "\r\n\t <>'\"\\")) {
    1595           0 :                 zend_string_release(PS(id));
    1596           0 :                 PS(id) = NULL;
    1597             :         }
    1598             : 
    1599         408 :         if (php_session_initialize() == FAILURE
    1600         396 :                 || php_session_cache_limiter() == -2) {
    1601          12 :                 PS(session_status) = php_session_none;
    1602          12 :                 if (PS(id)) {
    1603           8 :                         zend_string_release(PS(id));
    1604           8 :                         PS(id) = NULL;
    1605             :                 }
    1606          12 :                 return FAILURE;
    1607             :         }
    1608         396 :         return SUCCESS;
    1609             : }
    1610             : /* }}} */
    1611             : 
    1612         201 : PHPAPI int php_session_flush(int write) /* {{{ */
    1613             : {
    1614         201 :         if (PS(session_status) == php_session_active) {
    1615         201 :                 php_session_save_current_state(write);
    1616         197 :                 PS(session_status) = php_session_none;
    1617         197 :                 return SUCCESS;
    1618             :         }
    1619           0 :         return FAILURE;
    1620             : }
    1621             : /* }}} */
    1622             : 
    1623          15 : static int php_session_abort(void) /* {{{ */
    1624             : {
    1625          15 :         if (PS(session_status) == php_session_active) {
    1626          15 :                 if (PS(mod_data) || PS(mod_user_implemented)) {
    1627          14 :                         PS(mod)->s_close(&PS(mod_data));
    1628             :                 }
    1629          15 :                 PS(session_status) = php_session_none;
    1630          15 :                 return SUCCESS;
    1631             :         }
    1632           0 :         return FAILURE;
    1633             : }
    1634             : /* }}} */
    1635             : 
    1636           1 : static int php_session_reset(void) /* {{{ */
    1637             : {
    1638           1 :         if (PS(session_status) == php_session_active
    1639           1 :                 && php_session_initialize() == SUCCESS) {
    1640           1 :                 return SUCCESS;
    1641             :         }
    1642           0 :         return FAILURE;
    1643             : }
    1644             : /* }}} */
    1645             : 
    1646             : 
    1647             : /* This API is not used by any PHP modules including session currently.
    1648             :    session_adapt_url() may be used to set Session ID to target url without
    1649             :    starting "URL-Rewriter" output handler. */
    1650           0 : PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t *newlen) /* {{{ */
    1651             : {
    1652           0 :         if (APPLY_TRANS_SID && (PS(session_status) == php_session_active)) {
    1653           0 :                 *new = php_url_scanner_adapt_single_url(url, urllen, PS(session_name), ZSTR_VAL(PS(id)), newlen, 1);
    1654             :         }
    1655           0 : }
    1656             : /* }}} */
    1657             : 
    1658             : /* ********************************
    1659             :    * Userspace exported functions *
    1660             :    ******************************** */
    1661             : 
    1662             : /* {{{ proto bool session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])
    1663             :    Set session cookie parameters */
    1664         164 : static PHP_FUNCTION(session_set_cookie_params)
    1665             : {
    1666             :         zval *lifetime;
    1667         164 :         zend_string *path = NULL, *domain = NULL;
    1668         164 :         int argc = ZEND_NUM_ARGS();
    1669         164 :         zend_bool secure = 0, httponly = 0;
    1670             :         zend_string *ini_name;
    1671             : 
    1672         328 :         if (!PS(use_cookies) ||
    1673         164 :                 zend_parse_parameters(argc, "z|SSbb", &lifetime, &path, &domain, &secure, &httponly) == FAILURE) {
    1674           6 :                 return;
    1675             :         }
    1676             : 
    1677             : 
    1678         158 :         if (PS(session_status) == php_session_active) {
    1679           6 :                 php_error_docref(NULL, E_WARNING, "Cannot change session cookie parameters when session is active");
    1680           6 :                 RETURN_FALSE;
    1681             :         }
    1682             : 
    1683         152 :         if (SG(headers_sent)) {
    1684           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change session cookie parameters when headers already sent");
    1685           0 :                 RETURN_FALSE;
    1686             :         }
    1687             : 
    1688         451 :         convert_to_string_ex(lifetime);
    1689             : 
    1690         152 :         ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0);
    1691         152 :         if (zend_alter_ini_entry(ini_name,  Z_STR_P(lifetime), PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
    1692             :                 zend_string_release(ini_name);
    1693           2 :                 RETURN_FALSE;
    1694             :         }
    1695             :         zend_string_release(ini_name);
    1696             : 
    1697         150 :         if (path) {
    1698         124 :                 ini_name = zend_string_init("session.cookie_path", sizeof("session.cookie_path") - 1, 0);
    1699         124 :                 if (zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
    1700             :                         zend_string_release(ini_name);
    1701           0 :                         RETURN_FALSE;
    1702             :                 }
    1703             :                 zend_string_release(ini_name);
    1704             :         }
    1705         150 :         if (domain) {
    1706          99 :                 ini_name = zend_string_init("session.cookie_domain", sizeof("session.cookie_domain") - 1, 0);
    1707          99 :                 if (zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
    1708             :                         zend_string_release(ini_name);
    1709           0 :                         RETURN_FALSE;
    1710             :                 }
    1711             :                 zend_string_release(ini_name);
    1712             :         }
    1713             : 
    1714         150 :         if (argc > 3) {
    1715          74 :                 ini_name = zend_string_init("session.cookie_secure", sizeof("session.cookie_secure") - 1, 0);
    1716          74 :                 if (zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
    1717             :                         zend_string_release(ini_name);
    1718           0 :                         RETURN_FALSE;
    1719             :                 }
    1720             :                 zend_string_release(ini_name);
    1721             :         }
    1722         150 :         if (argc > 4) {
    1723          50 :                 ini_name = zend_string_init("session.cookie_httponly", sizeof("session.cookie_httponly") - 1, 0);
    1724          50 :                 if (zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
    1725             :                         zend_string_release(ini_name);
    1726           0 :                         RETURN_FALSE;
    1727             :                 }
    1728             :                 zend_string_release(ini_name);
    1729             :         }
    1730             : 
    1731         150 :         RETURN_TRUE;
    1732             : }
    1733             : /* }}} */
    1734             : 
    1735             : /* {{{ proto array session_get_cookie_params(void)
    1736             :    Return the session cookie parameters */
    1737          33 : static PHP_FUNCTION(session_get_cookie_params)
    1738             : {
    1739          33 :         if (zend_parse_parameters_none() == FAILURE) {
    1740          24 :                 return;
    1741             :         }
    1742             : 
    1743           9 :         array_init(return_value);
    1744             : 
    1745           9 :         add_assoc_long(return_value, "lifetime", PS(cookie_lifetime));
    1746           9 :         add_assoc_string(return_value, "path", PS(cookie_path));
    1747           9 :         add_assoc_string(return_value, "domain", PS(cookie_domain));
    1748           9 :         add_assoc_bool(return_value, "secure", PS(cookie_secure));
    1749           9 :         add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
    1750             : }
    1751             : /* }}} */
    1752             : 
    1753             : /* {{{ proto string session_name([string newname])
    1754             :    Return the current session name. If newname is given, the session name is replaced with newname */
    1755          46 : static PHP_FUNCTION(session_name)
    1756             : {
    1757          46 :         zend_string *name = NULL;
    1758             :         zend_string *ini_name;
    1759             : 
    1760          46 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
    1761           2 :                 return;
    1762             :         }
    1763             : 
    1764          45 :         if (name && PS(session_status) == php_session_active) {
    1765           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change session name when session is active");
    1766           0 :                 RETURN_FALSE;
    1767             :         }
    1768             : 
    1769          45 :         if (name && SG(headers_sent)) {
    1770           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change session name when headers already sent");
    1771           0 :                 RETURN_FALSE;
    1772             :         }
    1773             : 
    1774          90 :         RETVAL_STRING(PS(session_name));
    1775             : 
    1776          45 :         if (name) {
    1777          31 :                 ini_name = zend_string_init("session.name", sizeof("session.name") - 1, 0);
    1778          31 :                 zend_alter_ini_entry(ini_name, name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    1779             :                 zend_string_release(ini_name);
    1780             :         }
    1781             : }
    1782             : /* }}} */
    1783             : 
    1784             : /* {{{ proto string session_module_name([string newname])
    1785             :    Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname */
    1786          54 : static PHP_FUNCTION(session_module_name)
    1787             : {
    1788          54 :         zend_string *name = NULL;
    1789             :         zend_string *ini_name;
    1790             : 
    1791          54 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
    1792          35 :                 return;
    1793             :         }
    1794             : 
    1795          53 :         if (name && PS(session_status) == php_session_active) {
    1796           1 :                 php_error_docref(NULL, E_WARNING, "Cannot change save handler module when session is active");
    1797           1 :                 RETURN_FALSE;
    1798             :         }
    1799             : 
    1800          52 :         if (name && SG(headers_sent)) {
    1801           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change save handler module when headers already sent");
    1802           0 :                 RETURN_FALSE;
    1803             :         }
    1804             : 
    1805             :         /* Set return_value to current module name */
    1806          52 :         if (PS(mod) && PS(mod)->s_name) {
    1807         104 :                 RETVAL_STRING(PS(mod)->s_name);
    1808             :         } else {
    1809           0 :                 RETVAL_EMPTY_STRING();
    1810             :         }
    1811             : 
    1812          52 :         if (name) {
    1813          38 :                 if (!_php_find_ps_module(ZSTR_VAL(name))) {
    1814          32 :                         php_error_docref(NULL, E_WARNING, "Cannot find named PHP session module (%s)", ZSTR_VAL(name));
    1815             : 
    1816             :                         zval_dtor(return_value);
    1817          32 :                         RETURN_FALSE;
    1818             :                 }
    1819           6 :                 if (PS(mod_data) || PS(mod_user_implemented)) {
    1820           0 :                         PS(mod)->s_close(&PS(mod_data));
    1821             :                 }
    1822           6 :                 PS(mod_data) = NULL;
    1823             : 
    1824           6 :                 ini_name = zend_string_init("session.save_handler", sizeof("session.save_handler") - 1, 0);
    1825           6 :                 zend_alter_ini_entry(ini_name, name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    1826             :                 zend_string_release(ini_name);
    1827             :         }
    1828             : }
    1829             : /* }}} */
    1830             : 
    1831             : /* {{{ proto bool session_set_save_handler(string open, string close, string read, string write, string destroy, string gc, string create_sid)
    1832             :    Sets user-level functions */
    1833         101 : static PHP_FUNCTION(session_set_save_handler)
    1834             : {
    1835         101 :         zval *args = NULL;
    1836         101 :         int i, num_args, argc = ZEND_NUM_ARGS();
    1837             :         zend_string *ini_name, *ini_val;
    1838             : 
    1839         101 :         if (PS(session_status) == php_session_active) {
    1840           2 :                 php_error_docref(NULL, E_WARNING, "Cannot change save handler when session is active");
    1841           2 :                 RETURN_FALSE;
    1842             :         }
    1843             : 
    1844          99 :         if (SG(headers_sent)) {
    1845           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change save handler when headers already sent");
    1846           0 :                 RETURN_FALSE;
    1847             :         }
    1848             : 
    1849          99 :         if (argc > 0 && argc <= 2) {
    1850          28 :                 zval *obj = NULL;
    1851             :                 zend_string *func_name;
    1852             :                 zend_function *current_mptr;
    1853          28 :                 zend_bool register_shutdown = 1;
    1854             : 
    1855          28 :                 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &obj, php_session_iface_entry, &register_shutdown) == FAILURE) {
    1856           1 :                         RETURN_FALSE;
    1857             :                 }
    1858             : 
    1859             :                 /* For compatibility reason, implemeted interface is not checked */
    1860             :                 /* Find implemented methods - SessionHandlerInterface */
    1861          27 :                 i = 0;
    1862         351 :                 ZEND_HASH_FOREACH_STR_KEY(&php_session_iface_entry->function_table, func_name) {
    1863         324 :                         if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
    1864         324 :                                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1865          24 :                                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1866             :                                 }
    1867             : 
    1868         162 :                                 array_init_size(&PS(mod_user_names).names[i], 2);
    1869         162 :                                 Z_ADDREF_P(obj);
    1870         162 :                                 add_next_index_zval(&PS(mod_user_names).names[i], obj);
    1871         162 :                                 add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
    1872             :                         } else {
    1873           0 :                                 php_error_docref(NULL, E_ERROR, "Session handler's function table is corrupt");
    1874           0 :                                 RETURN_FALSE;
    1875             :                         }
    1876             : 
    1877         162 :                         ++i;
    1878             :                 } ZEND_HASH_FOREACH_END();
    1879             : 
    1880             :                 /* Find implemented methods - SessionIdInterface (optional) */
    1881          81 :                 ZEND_HASH_FOREACH_STR_KEY(&php_session_id_iface_entry->function_table, func_name) {
    1882          54 :                         if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
    1883          52 :                                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1884           2 :                                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1885             :                                 }
    1886          26 :                                 array_init_size(&PS(mod_user_names).names[i], 2);
    1887          26 :                                 Z_ADDREF_P(obj);
    1888          26 :                                 add_next_index_zval(&PS(mod_user_names).names[i], obj);
    1889          26 :                                 add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
    1890             :                         } else {
    1891           2 :                                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1892           0 :                                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1893           0 :                                         ZVAL_UNDEF(&PS(mod_user_names).names[i]);
    1894             :                                 }
    1895             :                         }
    1896             : 
    1897          27 :                         ++i;
    1898             :                 } ZEND_HASH_FOREACH_END();
    1899             : 
    1900             :                 /* Find implemented methods - SessionUpdateTimestampInterface (optional) */
    1901         135 :                 ZEND_HASH_FOREACH_STR_KEY(&php_session_update_timestamp_iface_entry->function_table, func_name) {
    1902         108 :                         if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
    1903           8 :                                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1904           0 :                                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1905             :                                 }
    1906           4 :                                 array_init_size(&PS(mod_user_names).names[i], 2);
    1907           4 :                                 Z_ADDREF_P(obj);
    1908           4 :                                 add_next_index_zval(&PS(mod_user_names).names[i], obj);
    1909           4 :                                 add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
    1910             :                         } else {
    1911         100 :                                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1912           0 :                                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1913           0 :                                         ZVAL_UNDEF(&PS(mod_user_names).names[i]);
    1914             :                                 }
    1915             :                         }
    1916          54 :                         ++i;
    1917             :                 } ZEND_HASH_FOREACH_END();
    1918             : 
    1919          27 :                 if (register_shutdown) {
    1920             :                         /* create shutdown function */
    1921             :                         php_shutdown_function_entry shutdown_function_entry;
    1922          25 :                         shutdown_function_entry.arg_count = 1;
    1923          25 :                         shutdown_function_entry.arguments = (zval *) safe_emalloc(sizeof(zval), 1, 0);
    1924             : 
    1925          50 :                         ZVAL_STRING(&shutdown_function_entry.arguments[0], "session_register_shutdown");
    1926             : 
    1927             :                         /* add shutdown function, removing the old one if it exists */
    1928          25 :                         if (!register_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1, &shutdown_function_entry)) {
    1929           0 :                                 zval_ptr_dtor(&shutdown_function_entry.arguments[0]);
    1930           0 :                                 efree(shutdown_function_entry.arguments);
    1931           0 :                                 php_error_docref(NULL, E_WARNING, "Unable to register session shutdown function");
    1932           0 :                                 RETURN_FALSE;
    1933             :                         }
    1934             :                 } else {
    1935             :                         /* remove shutdown function */
    1936           2 :                         remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
    1937             :                 }
    1938             : 
    1939          27 :                 if (PS(mod) && PS(session_status) != php_session_active && PS(mod) != &ps_mod_user) {
    1940          23 :                         ini_name = zend_string_init("session.save_handler", sizeof("session.save_handler") - 1, 0);
    1941          23 :                         ini_val = zend_string_init("user", sizeof("user") - 1, 0);
    1942          23 :                         PS(set_handler) = 1;
    1943          23 :                         zend_alter_ini_entry(ini_name, ini_val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    1944          23 :                         PS(set_handler) = 0;
    1945             :                         zend_string_release(ini_val);
    1946             :                         zend_string_release(ini_name);
    1947             :                 }
    1948             : 
    1949          27 :                 RETURN_TRUE;
    1950             :         }
    1951             : 
    1952             :         /* Set procedural save handler functions */
    1953          71 :         if (argc < 6 || PS_NUM_APIS < argc) {
    1954           0 :                 WRONG_PARAM_COUNT;
    1955             :         }
    1956             : 
    1957          71 :         if (zend_parse_parameters(argc, "+", &args, &num_args) == FAILURE) {
    1958           0 :                 return;
    1959             :         }
    1960             : 
    1961             :         /* remove shutdown function */
    1962          71 :         remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
    1963             : 
    1964             :         /* At this point argc can only be between 6 and PS_NUM_APIS */
    1965         349 :         for (i = 0; i < argc; i++) {
    1966         308 :                 if (!zend_is_callable(&args[i], 0, NULL)) {
    1967          30 :                         zend_string *name = zend_get_callable_name(&args[i]);
    1968          30 :                         php_error_docref(NULL, E_WARNING, "Argument %d is not a valid callback", i+1);
    1969             :                         zend_string_release(name);
    1970          30 :                         RETURN_FALSE;
    1971             :                 }
    1972             :         }
    1973             : 
    1974          41 :         if (PS(mod) && PS(mod) != &ps_mod_user) {
    1975          27 :                 ini_name = zend_string_init("session.save_handler", sizeof("session.save_handler") - 1, 0);
    1976          27 :                 ini_val = zend_string_init("user", sizeof("user") - 1, 0);
    1977          27 :                 PS(set_handler) = 1;
    1978          27 :                 zend_alter_ini_entry(ini_name, ini_val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    1979          27 :                 PS(set_handler) = 0;
    1980             :                 zend_string_release(ini_val);
    1981             :                 zend_string_release(ini_name);
    1982             :         }
    1983             : 
    1984         304 :         for (i = 0; i < argc; i++) {
    1985         526 :                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    1986          93 :                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    1987             :                 }
    1988         263 :                 ZVAL_COPY(&PS(mod_user_names).names[i], &args[i]);
    1989             :         }
    1990             : 
    1991          41 :         RETURN_TRUE;
    1992             : }
    1993             : /* }}} */
    1994             : 
    1995             : /* {{{ proto string session_save_path([string newname])
    1996             :    Return the current save path passed to module_name. If newname is given, the save path is replaced with newname */
    1997          55 : static PHP_FUNCTION(session_save_path)
    1998             : {
    1999          55 :         zend_string *name = NULL;
    2000             :         zend_string *ini_name;
    2001             : 
    2002          55 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
    2003           4 :                 return;
    2004             :         }
    2005             : 
    2006          54 :         if (name && PS(session_status) == php_session_active) {
    2007           2 :                 php_error_docref(NULL, E_WARNING, "Cannot change save path when session is active");
    2008           2 :                 RETURN_FALSE;
    2009             :         }
    2010             : 
    2011          52 :         if (name && SG(headers_sent)) {
    2012           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change save path when headers already sent");
    2013           0 :                 RETURN_FALSE;
    2014             :         }
    2015             : 
    2016         104 :         RETVAL_STRING(PS(save_path));
    2017             : 
    2018          52 :         if (name) {
    2019          36 :                 if (memchr(ZSTR_VAL(name), '\0', ZSTR_LEN(name)) != NULL) {
    2020           0 :                         php_error_docref(NULL, E_WARNING, "The save_path cannot contain NULL characters");
    2021             :                         zval_dtor(return_value);
    2022           0 :                         RETURN_FALSE;
    2023             :                 }
    2024          36 :                 ini_name = zend_string_init("session.save_path", sizeof("session.save_path") - 1, 0);
    2025          36 :                 zend_alter_ini_entry(ini_name, name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    2026             :                 zend_string_release(ini_name);
    2027             :         }
    2028             : }
    2029             : /* }}} */
    2030             : 
    2031             : /* {{{ proto string session_id([string newid])
    2032             :    Return the current session id. If newid is given, the session id is replaced with newid */
    2033         259 : static PHP_FUNCTION(session_id)
    2034             : {
    2035         259 :         zend_string *name = NULL;
    2036         259 :         int argc = ZEND_NUM_ARGS();
    2037             : 
    2038         259 :         if (zend_parse_parameters(argc, "|S", &name) == FAILURE) {
    2039           2 :                 return;
    2040             :         }
    2041             : 
    2042         258 :         if (name && PS(use_cookies) && SG(headers_sent)) {
    2043           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change session id when headers already sent");
    2044           0 :                 RETURN_FALSE;
    2045             :         }
    2046             : 
    2047         258 :         if (PS(id)) {
    2048             :                 /* keep compatibility for "\0" characters ???
    2049             :                  * see: ext/session/tests/session_id_error3.phpt */
    2050         189 :                 size_t len = strlen(ZSTR_VAL(PS(id)));
    2051         189 :                 if (UNEXPECTED(len != ZSTR_LEN(PS(id)))) {
    2052           2 :                         RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(PS(id)), len, 0));
    2053             :                 } else {
    2054         188 :                         RETVAL_STR_COPY(PS(id));
    2055             :                 }
    2056             :         } else {
    2057          69 :                 RETVAL_EMPTY_STRING();
    2058             :         }
    2059             : 
    2060         258 :         if (name) {
    2061         100 :                 if (PS(id)) {
    2062          54 :                         zend_string_release(PS(id));
    2063             :                 }
    2064         200 :                 PS(id) = zend_string_copy(name);
    2065             :         }
    2066             : }
    2067             : /* }}} */
    2068             : 
    2069             : /* {{{ proto bool session_regenerate_id([bool delete_old_session])
    2070             :    Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session. */
    2071          43 : static PHP_FUNCTION(session_regenerate_id)
    2072             : {
    2073          43 :         zend_bool del_ses = 0;
    2074             :         zend_string *data;
    2075             : 
    2076          43 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &del_ses) == FAILURE) {
    2077           2 :                 return;
    2078             :         }
    2079             : 
    2080          41 :         if (PS(session_status) != php_session_active) {
    2081          26 :                 php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active");
    2082          26 :                 RETURN_FALSE;
    2083             :         }
    2084             : 
    2085          15 :         if (SG(headers_sent)) {
    2086           0 :                 php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent");
    2087           0 :                 RETURN_FALSE;
    2088             :         }
    2089             : 
    2090             :         /* Process old session data */
    2091          15 :         if (del_ses) {
    2092           4 :                 if (PS(mod)->s_destroy(&PS(mod_data), PS(id)) == FAILURE) {
    2093           0 :                         PS(mod)->s_close(&PS(mod_data));
    2094           0 :                         PS(session_status) = php_session_none;
    2095           0 :                         php_error_docref(NULL, E_WARNING, "Session object destruction failed.  ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2096           0 :                         RETURN_FALSE;
    2097             :                 }
    2098             :         } else {
    2099             :                 int ret;
    2100          11 :                 data = php_session_encode();
    2101          11 :                 if (data) {
    2102           3 :                         ret = PS(mod)->s_write(&PS(mod_data), PS(id), data, PS(gc_maxlifetime));
    2103           3 :                         zend_string_release(data);
    2104             :                 } else {
    2105           8 :                         ret = PS(mod)->s_write(&PS(mod_data), PS(id), ZSTR_EMPTY_ALLOC(), PS(gc_maxlifetime));
    2106             :                 }
    2107          11 :                 if (ret == FAILURE) {
    2108           0 :                         PS(mod)->s_close(&PS(mod_data));
    2109           0 :                         PS(session_status) = php_session_none;
    2110           0 :                         php_error_docref(NULL, E_WARNING, "Session write failed. ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2111           0 :                         RETURN_FALSE;
    2112             :                 }
    2113             :         }
    2114          15 :         PS(mod)->s_close(&PS(mod_data));
    2115             : 
    2116             :         /* New session data */
    2117          15 :         if (PS(session_vars)) {
    2118          14 :                 zend_string_release(PS(session_vars));
    2119          14 :                 PS(session_vars) = NULL;
    2120             :         }
    2121          15 :         zend_string_release(PS(id));
    2122          15 :         PS(id) = NULL;
    2123             : 
    2124          15 :         if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
    2125           0 :                 PS(session_status) = php_session_none;
    2126           0 :                 zend_throw_error(NULL, "Failed to open session: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2127           0 :                 RETURN_FALSE;
    2128             :         }
    2129             : 
    2130          15 :         PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
    2131          15 :         if (!PS(id)) {
    2132           0 :                 PS(session_status) = php_session_none;
    2133           0 :                 zend_throw_error(NULL, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2134           0 :                 RETURN_FALSE;
    2135             :         }
    2136          16 :         if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
    2137           1 :                 PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == FAILURE) {
    2138           1 :                 zend_string_release(PS(id));
    2139           1 :                 PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
    2140           1 :                 if (!PS(id)) {
    2141           0 :                         PS(mod)->s_close(&PS(mod_data));
    2142           0 :                         PS(session_status) = php_session_none;
    2143           0 :                         zend_throw_error(NULL, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2144           0 :                         RETURN_FALSE;
    2145             :                 }
    2146             :         }
    2147             :         /* Read is required to make new session data at this point. */
    2148          15 :         if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) {
    2149           0 :                 PS(mod)->s_close(&PS(mod_data));
    2150           0 :                 PS(session_status) = php_session_none;
    2151           0 :                 zend_throw_error(NULL, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
    2152           0 :                 RETURN_FALSE;
    2153             :         }
    2154          15 :         if (data) {
    2155          15 :                 zend_string_release(data);
    2156             :         }
    2157             : 
    2158          15 :         if (PS(use_cookies)) {
    2159          13 :                 PS(send_cookie) = 1;
    2160             :         }
    2161          15 :         if (php_session_reset_id() == FAILURE) {
    2162           0 :                 RETURN_FALSE;
    2163             :         }
    2164             : 
    2165          15 :         RETURN_TRUE;
    2166             : }
    2167             : /* }}} */
    2168             : 
    2169             : /* {{{ proto string session_create_id([string prefix])
    2170             :    Generate new session ID. Intended for user save handlers. */
    2171             : /* This is not used yet */
    2172           4 : static PHP_FUNCTION(session_create_id)
    2173             : {
    2174           4 :         zend_string *prefix = NULL, *new_id;
    2175           4 :         smart_str id = {0};
    2176             : 
    2177           4 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &prefix) == FAILURE) {
    2178           0 :                 return;
    2179             :         }
    2180             : 
    2181           4 :         if (prefix && ZSTR_LEN(prefix)) {
    2182           3 :                 if (php_session_valid_key(ZSTR_VAL(prefix)) == FAILURE) {
    2183             :                         /* E_ERROR raised for security reason. */
    2184           0 :                         php_error_docref(NULL, E_WARNING, "Prefix cannot contain special characters. Only aphanumeric, ',', '-' are allowed");
    2185           0 :                         RETURN_FALSE;
    2186             :                 } else {
    2187           3 :                         smart_str_append(&id, prefix);
    2188             :                 }
    2189             :         }
    2190             : 
    2191           4 :         if (!PS(in_save_handler) && PS(session_status) == php_session_active) {
    2192           0 :                 int limit = 3;
    2193           0 :                 while (limit--) {
    2194           0 :                         new_id = PS(mod)->s_create_sid(&PS(mod_data));
    2195           0 :                         if (!PS(mod)->s_validate_sid) {
    2196           0 :                                 break;
    2197             :                         } else {
    2198             :                                 /* Detect collision and retry */
    2199           0 :                                 if (PS(mod)->s_validate_sid(&PS(mod_data), new_id) == FAILURE) {
    2200             :                                         zend_string_release(new_id);
    2201           0 :                                         continue;
    2202             :                                 }
    2203           0 :                                 break;
    2204             :                         }
    2205             :                 }
    2206             :         } else {
    2207           4 :                 new_id = php_session_create_id(NULL);
    2208             :         }
    2209             : 
    2210           4 :         if (new_id) {
    2211             :                 smart_str_append(&id, new_id);
    2212             :                 zend_string_release(new_id);
    2213             :         } else {
    2214             :                 smart_str_free(&id);
    2215           0 :                 php_error_docref(NULL, E_WARNING, "Failed to create new ID");
    2216           0 :                 RETURN_FALSE;
    2217             :         }
    2218             :         smart_str_0(&id);
    2219           4 :         RETVAL_NEW_STR(id.s);
    2220             : }
    2221             : /* }}} */
    2222             : 
    2223             : /* {{{ proto string session_cache_limiter([string new_cache_limiter])
    2224             :    Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter */
    2225          49 : static PHP_FUNCTION(session_cache_limiter)
    2226             : {
    2227          49 :         zend_string *limiter = NULL;
    2228             :         zend_string *ini_name;
    2229             : 
    2230          49 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &limiter) == FAILURE) {
    2231           5 :                 return;
    2232             :         }
    2233             : 
    2234          48 :         if (limiter && PS(session_status) == php_session_active) {
    2235           3 :                 php_error_docref(NULL, E_WARNING, "Cannot change cache limiter when session is active");
    2236           3 :                 RETURN_FALSE;
    2237             :         }
    2238             : 
    2239          45 :         if (limiter && SG(headers_sent)) {
    2240           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change cache limiter when headers already sent");
    2241           0 :                 RETURN_FALSE;
    2242             :         }
    2243             : 
    2244          90 :         RETVAL_STRING(PS(cache_limiter));
    2245             : 
    2246          45 :         if (limiter) {
    2247          27 :                 ini_name = zend_string_init("session.cache_limiter", sizeof("session.cache_limiter") - 1, 0);
    2248          27 :                 zend_alter_ini_entry(ini_name, limiter, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    2249             :                 zend_string_release(ini_name);
    2250             :         }
    2251             : }
    2252             : /* }}} */
    2253             : 
    2254             : /* {{{ proto int session_cache_expire([int new_cache_expire])
    2255             :    Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire */
    2256          45 : static PHP_FUNCTION(session_cache_expire)
    2257             : {
    2258          45 :         zval *expires = NULL;
    2259             :         zend_string *ini_name;
    2260             : 
    2261          45 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &expires) == FAILURE) {
    2262           0 :                 return;
    2263             :         }
    2264             : 
    2265          45 :         if (expires && PS(session_status) == php_session_active) {
    2266           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change cache expire when session is active");
    2267           0 :                 RETURN_LONG(PS(cache_expire));
    2268             :         }
    2269             : 
    2270          45 :         if (expires && SG(headers_sent)) {
    2271           0 :                 php_error_docref(NULL, E_WARNING, "Cannot change cache expire when headers already sent");
    2272           0 :                 RETURN_FALSE;
    2273             :         }
    2274             : 
    2275          45 :         RETVAL_LONG(PS(cache_expire));
    2276             : 
    2277          45 :         if (expires) {
    2278          88 :                 convert_to_string_ex(expires);
    2279          31 :                 ini_name = zend_string_init("session.cache_expire", sizeof("session.cache_expire") - 1, 0);
    2280          31 :                 zend_alter_ini_entry(ini_name, Z_STR_P(expires), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
    2281             :                 zend_string_release(ini_name);
    2282             :         }
    2283             : }
    2284             : /* }}} */
    2285             : 
    2286             : /* {{{ proto string session_encode(void)
    2287             :    Serializes the current setup and returns the serialized representation */
    2288         142 : static PHP_FUNCTION(session_encode)
    2289             : {
    2290             :         zend_string *enc;
    2291             : 
    2292         142 :         if (zend_parse_parameters_none() == FAILURE) {
    2293          24 :                 return;
    2294             :         }
    2295             : 
    2296         118 :         enc = php_session_encode();
    2297         118 :         if (enc == NULL) {
    2298          28 :                 RETURN_FALSE;
    2299             :         }
    2300             : 
    2301          90 :         RETURN_STR(enc);
    2302             : }
    2303             : /* }}} */
    2304             : 
    2305             : /* {{{ proto bool session_decode(string data)
    2306             :    Deserializes data and reinitializes the variables */
    2307         150 : static PHP_FUNCTION(session_decode)
    2308             : {
    2309         150 :         zend_string *str = NULL;
    2310             : 
    2311         150 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
    2312           1 :                 return;
    2313             :         }
    2314             : 
    2315         149 :         if (PS(session_status) != php_session_active) {
    2316           5 :                 php_error_docref(NULL, E_WARNING, "Session is not active. You cannot decode session data");
    2317           5 :                 RETURN_FALSE;
    2318             :         }
    2319             : 
    2320         144 :         if (php_session_decode(str) == FAILURE) {
    2321          41 :                 RETURN_FALSE;
    2322             :         }
    2323         103 :         RETURN_TRUE;
    2324             : }
    2325             : /* }}} */
    2326             : 
    2327           9 : static int php_session_start_set_ini(zend_string *varname, zend_string *new_value) {
    2328             :         int ret;
    2329           9 :         smart_str buf ={0};
    2330             :         smart_str_appends(&buf, "session");
    2331             :         smart_str_appendc(&buf, '.');
    2332             :         smart_str_append(&buf, varname);
    2333             :         smart_str_0(&buf);
    2334           9 :         ret = zend_alter_ini_entry_ex(buf.s, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
    2335             :         smart_str_free(&buf);
    2336           9 :         return ret;
    2337             : }
    2338             : 
    2339             : /* {{{ proto bool session_start([array options])
    2340             : +   Begin session */
    2341         424 : static PHP_FUNCTION(session_start)
    2342             : {
    2343         424 :         zval *options = NULL;
    2344             :         zval *value;
    2345             :         zend_ulong num_idx;
    2346             :         zend_string *str_idx;
    2347         424 :         zend_long read_and_close = 0;
    2348             : 
    2349         424 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a", &options) == FAILURE) {
    2350          24 :                 RETURN_FALSE;
    2351             :         }
    2352             : 
    2353         400 :         if (PS(session_status) == php_session_active) {
    2354           5 :                 php_error_docref(NULL, E_NOTICE, "A session had already been started - ignoring");
    2355           5 :                 RETURN_TRUE;
    2356             :         }
    2357             : 
    2358             :         /*
    2359             :          * TODO: To prevent unusable session with trans sid, actual output started status is
    2360             :          * required. i.e. There shouldn't be any outputs in output buffer, otherwise session
    2361             :          * module is unable to rewrite output.
    2362             :          */
    2363         395 :         if (PS(use_cookies) && SG(headers_sent)) {
    2364           1 :                 php_error_docref(NULL, E_WARNING, "Cannot start session when headers already sent");
    2365           1 :                 RETURN_FALSE;
    2366             :         }
    2367             : 
    2368             :         /* set options */
    2369         394 :         if (options) {
    2370          27 :                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), num_idx, str_idx, value) {
    2371           9 :                         if (str_idx) {
    2372           9 :                                 switch(Z_TYPE_P(value)) {
    2373           9 :                                         case IS_STRING:
    2374             :                                         case IS_TRUE:
    2375             :                                         case IS_FALSE:
    2376             :                                         case IS_LONG:
    2377           9 :                                                 if (zend_string_equals_literal(str_idx, "read_and_close")) {
    2378           0 :                                                         read_and_close = zval_get_long(value);
    2379             :                                                 } else {
    2380           9 :                                                         zend_string *val = zval_get_string(value);
    2381           9 :                                                         if (php_session_start_set_ini(str_idx, val) == FAILURE) {
    2382           0 :                                                                 php_error_docref(NULL, E_WARNING, "Setting option '%s' failed", ZSTR_VAL(str_idx));
    2383             :                                                         }
    2384             :                                                         zend_string_release(val);
    2385             :                                                 }
    2386           9 :                                                 break;
    2387           0 :                                         default:
    2388           0 :                                                 php_error_docref(NULL, E_WARNING, "Option(%s) value must be string, boolean or long", ZSTR_VAL(str_idx));
    2389           0 :                                                 break;
    2390             :                                 }
    2391           0 :                         }
    2392             :                         (void) num_idx;
    2393             :                 } ZEND_HASH_FOREACH_END();
    2394             :         }
    2395             : 
    2396         394 :         php_session_start();
    2397             : 
    2398         394 :         if (PS(session_status) != php_session_active) {
    2399          22 :                 IF_SESSION_VARS() {
    2400           8 :                         zval *sess_var = Z_REFVAL(PS(http_session_vars));
    2401           8 :                         SEPARATE_ARRAY(sess_var);
    2402             :                         /* Clean $_SESSION. */
    2403           8 :                         zend_hash_clean(Z_ARRVAL_P(sess_var));
    2404             :                 }
    2405          14 :                 RETURN_FALSE;
    2406             :         }
    2407             : 
    2408         380 :         if (read_and_close) {
    2409           0 :                 php_session_flush(0);
    2410             :         }
    2411             : 
    2412         380 :         RETURN_TRUE;
    2413             : }
    2414             : /* }}} */
    2415             : 
    2416             : /* {{{ proto bool session_destroy(void)
    2417             :    Destroy the current session and all data associated with it */
    2418         235 : static PHP_FUNCTION(session_destroy)
    2419             : {
    2420         235 :         if (zend_parse_parameters_none() == FAILURE) {
    2421          24 :                 return;
    2422             :         }
    2423             : 
    2424         211 :         RETURN_BOOL(php_session_destroy() == SUCCESS);
    2425             : }
    2426             : /* }}} */
    2427             : 
    2428             : /* {{{ proto bool session_unset(void)
    2429             :    Unset all registered variables */
    2430          63 : static PHP_FUNCTION(session_unset)
    2431             : {
    2432          63 :         if (zend_parse_parameters_none() == FAILURE) {
    2433          24 :                 return;
    2434             :         }
    2435             : 
    2436          39 :         if (PS(session_status) != php_session_active) {
    2437          35 :                 RETURN_FALSE;
    2438             :         }
    2439             : 
    2440           8 :         IF_SESSION_VARS() {
    2441           4 :                 zval *sess_var = Z_REFVAL(PS(http_session_vars));
    2442           4 :                 SEPARATE_ARRAY(sess_var);
    2443             : 
    2444             :                 /* Clean $_SESSION. */
    2445           4 :                 zend_hash_clean(Z_ARRVAL_P(sess_var));
    2446             :         }
    2447           4 :         RETURN_TRUE;
    2448             : }
    2449             : /* }}} */
    2450             : 
    2451             : /* {{{ proto int session_gc(void)
    2452             :    Perform GC and return number of deleted sessions */
    2453           3 : static PHP_FUNCTION(session_gc)
    2454             : {
    2455             :         zend_long num;
    2456             : 
    2457           3 :         if (zend_parse_parameters_none() == FAILURE) {
    2458           0 :                 return;
    2459             :         }
    2460             : 
    2461           3 :         if (PS(session_status) != php_session_active) {
    2462           1 :                 php_error_docref(NULL, E_WARNING, "Session is not active");
    2463           1 :                 RETURN_FALSE;
    2464             :         }
    2465             : 
    2466           2 :         num = php_session_gc(1);
    2467           2 :         if (num < 0) {
    2468           0 :                 RETURN_FALSE;
    2469             :         }
    2470             : 
    2471           2 :         RETURN_LONG(num);
    2472             : }
    2473             : /* }}} */
    2474             : 
    2475             : 
    2476             : /* {{{ proto bool session_write_close(void)
    2477             :    Write session data and end session */
    2478         198 : static PHP_FUNCTION(session_write_close)
    2479             : {
    2480         198 :         if (zend_parse_parameters_none() == FAILURE) {
    2481          48 :                 return;
    2482             :         }
    2483             : 
    2484         150 :         if (PS(session_status) != php_session_active) {
    2485          26 :                 RETURN_FALSE;
    2486             :         }
    2487         124 :         php_session_flush(1);
    2488         123 :         RETURN_TRUE;
    2489             : }
    2490             : /* }}} */
    2491             : 
    2492             : /* {{{ proto bool session_abort(void)
    2493             :    Abort session and end session. Session data will not be written */
    2494           1 : static PHP_FUNCTION(session_abort)
    2495             : {
    2496           1 :         if (zend_parse_parameters_none() == FAILURE) {
    2497           0 :                 return;
    2498             :         }
    2499             : 
    2500           1 :         if (PS(session_status) != php_session_active) {
    2501           0 :                 RETURN_FALSE;
    2502             :         }
    2503           1 :         php_session_abort();
    2504           1 :         RETURN_TRUE;
    2505             : }
    2506             : /* }}} */
    2507             : 
    2508             : /* {{{ proto bool session_reset(void)
    2509             :    Reset session data from saved session data */
    2510           1 : static PHP_FUNCTION(session_reset)
    2511             : {
    2512           1 :         if (zend_parse_parameters_none() == FAILURE) {
    2513           0 :                 return;
    2514             :         }
    2515             : 
    2516           1 :         if (PS(session_status) != php_session_active) {
    2517           0 :                 RETURN_FALSE;
    2518             :         }
    2519           1 :         php_session_reset();
    2520           1 :         RETURN_TRUE;
    2521             : }
    2522             : /* }}} */
    2523             : 
    2524             : /* {{{ proto int session_status(void)
    2525             :    Returns the current session status */
    2526          56 : static PHP_FUNCTION(session_status)
    2527             : {
    2528          56 :         if (zend_parse_parameters_none() == FAILURE) {
    2529           0 :                 return;
    2530             :         }
    2531             : 
    2532          56 :         RETURN_LONG(PS(session_status));
    2533             : }
    2534             : /* }}} */
    2535             : 
    2536             : /* {{{ proto void session_register_shutdown(void)
    2537             :    Registers session_write_close() as a shutdown function */
    2538          23 : static PHP_FUNCTION(session_register_shutdown)
    2539             : {
    2540             :         php_shutdown_function_entry shutdown_function_entry;
    2541             : 
    2542             :         /* This function is registered itself as a shutdown function by
    2543             :          * session_set_save_handler($obj). The reason we now register another
    2544             :          * shutdown function is in case the user registered their own shutdown
    2545             :          * function after calling session_set_save_handler(), which expects
    2546             :          * the session still to be available.
    2547             :          */
    2548             : 
    2549          23 :         shutdown_function_entry.arg_count = 1;
    2550          23 :         shutdown_function_entry.arguments = (zval *) safe_emalloc(sizeof(zval), 1, 0);
    2551             : 
    2552          46 :         ZVAL_STRING(&shutdown_function_entry.arguments[0], "session_write_close");
    2553             : 
    2554          23 :         if (!append_user_shutdown_function(shutdown_function_entry)) {
    2555           0 :                 zval_ptr_dtor(&shutdown_function_entry.arguments[0]);
    2556           0 :                 efree(shutdown_function_entry.arguments);
    2557             : 
    2558             :                 /* Unable to register shutdown function, presumably because of lack
    2559             :                  * of memory, so flush the session now. It would be done in rshutdown
    2560             :                  * anyway but the handler will have had it's dtor called by then.
    2561             :                  * If the user does have a later shutdown function which needs the
    2562             :                  * session then tough luck.
    2563             :                  */
    2564           0 :                 php_session_flush(1);
    2565           0 :                 php_error_docref(NULL, E_WARNING, "Unable to register session flush function");
    2566             :         }
    2567          23 : }
    2568             : /* }}} */
    2569             : 
    2570             : /* {{{ arginfo */
    2571             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_name, 0, 0, 0)
    2572             :         ZEND_ARG_INFO(0, name)
    2573             : ZEND_END_ARG_INFO()
    2574             : 
    2575             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_module_name, 0, 0, 0)
    2576             :         ZEND_ARG_INFO(0, module)
    2577             : ZEND_END_ARG_INFO()
    2578             : 
    2579             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_save_path, 0, 0, 0)
    2580             :         ZEND_ARG_INFO(0, path)
    2581             : ZEND_END_ARG_INFO()
    2582             : 
    2583             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_id, 0, 0, 0)
    2584             :         ZEND_ARG_INFO(0, id)
    2585             : ZEND_END_ARG_INFO()
    2586             : 
    2587             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_create_id, 0, 0, 0)
    2588             :         ZEND_ARG_INFO(0, prefix)
    2589             : ZEND_END_ARG_INFO()
    2590             : 
    2591             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_regenerate_id, 0, 0, 0)
    2592             :         ZEND_ARG_INFO(0, delete_old_session)
    2593             : ZEND_END_ARG_INFO()
    2594             : 
    2595             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_decode, 0, 0, 1)
    2596             :         ZEND_ARG_INFO(0, data)
    2597             : ZEND_END_ARG_INFO()
    2598             : 
    2599             : ZEND_BEGIN_ARG_INFO(arginfo_session_void, 0)
    2600             : ZEND_END_ARG_INFO()
    2601             : 
    2602             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_save_handler, 0, 0, 1)
    2603             :         ZEND_ARG_INFO(0, open)
    2604             :         ZEND_ARG_INFO(0, close)
    2605             :         ZEND_ARG_INFO(0, read)
    2606             :         ZEND_ARG_INFO(0, write)
    2607             :         ZEND_ARG_INFO(0, destroy)
    2608             :         ZEND_ARG_INFO(0, gc)
    2609             :         ZEND_ARG_INFO(0, create_sid)
    2610             :         ZEND_ARG_INFO(0, validate_sid)
    2611             :         ZEND_ARG_INFO(0, update_timestamp)
    2612             : ZEND_END_ARG_INFO()
    2613             : 
    2614             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_limiter, 0, 0, 0)
    2615             :         ZEND_ARG_INFO(0, cache_limiter)
    2616             : ZEND_END_ARG_INFO()
    2617             : 
    2618             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_expire, 0, 0, 0)
    2619             :         ZEND_ARG_INFO(0, new_cache_expire)
    2620             : ZEND_END_ARG_INFO()
    2621             : 
    2622             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_cookie_params, 0, 0, 1)
    2623             :         ZEND_ARG_INFO(0, lifetime)
    2624             :         ZEND_ARG_INFO(0, path)
    2625             :         ZEND_ARG_INFO(0, domain)
    2626             :         ZEND_ARG_INFO(0, secure)
    2627             :         ZEND_ARG_INFO(0, httponly)
    2628             : ZEND_END_ARG_INFO()
    2629             : 
    2630             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_open, 0)
    2631             :         ZEND_ARG_INFO(0, save_path)
    2632             :         ZEND_ARG_INFO(0, session_name)
    2633             : ZEND_END_ARG_INFO()
    2634             : 
    2635             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_close, 0)
    2636             : ZEND_END_ARG_INFO()
    2637             : 
    2638             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_read, 0)
    2639             :         ZEND_ARG_INFO(0, key)
    2640             : ZEND_END_ARG_INFO()
    2641             : 
    2642             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_write, 0)
    2643             :         ZEND_ARG_INFO(0, key)
    2644             :         ZEND_ARG_INFO(0, val)
    2645             : ZEND_END_ARG_INFO()
    2646             : 
    2647             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_destroy, 0)
    2648             :         ZEND_ARG_INFO(0, key)
    2649             : ZEND_END_ARG_INFO()
    2650             : 
    2651             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_gc, 0)
    2652             :         ZEND_ARG_INFO(0, maxlifetime)
    2653             : ZEND_END_ARG_INFO()
    2654             : 
    2655             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_create_sid, 0)
    2656             : ZEND_END_ARG_INFO()
    2657             : 
    2658             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_validateId, 0)
    2659             :         ZEND_ARG_INFO(0, key)
    2660             : ZEND_END_ARG_INFO()
    2661             : 
    2662             : ZEND_BEGIN_ARG_INFO(arginfo_session_class_updateTimestamp, 0)
    2663             :         ZEND_ARG_INFO(0, key)
    2664             :         ZEND_ARG_INFO(0, val)
    2665             : ZEND_END_ARG_INFO()
    2666             : 
    2667             : ZEND_BEGIN_ARG_INFO_EX(arginfo_session_start, 0, 0, 0)
    2668             :         ZEND_ARG_INFO(0, options) /* array */
    2669             : ZEND_END_ARG_INFO()
    2670             : /* }}} */
    2671             : 
    2672             : /* {{{ session_functions[]
    2673             :  */
    2674             : static const zend_function_entry session_functions[] = {
    2675             :         PHP_FE(session_name,              arginfo_session_name)
    2676             :         PHP_FE(session_module_name,       arginfo_session_module_name)
    2677             :         PHP_FE(session_save_path,         arginfo_session_save_path)
    2678             :         PHP_FE(session_id,                arginfo_session_id)
    2679             :         PHP_FE(session_create_id,         arginfo_session_create_id)
    2680             :         PHP_FE(session_regenerate_id,     arginfo_session_regenerate_id)
    2681             :         PHP_FE(session_decode,            arginfo_session_decode)
    2682             :         PHP_FE(session_encode,            arginfo_session_void)
    2683             :         PHP_FE(session_start,             arginfo_session_start)
    2684             :         PHP_FE(session_destroy,           arginfo_session_void)
    2685             :         PHP_FE(session_unset,             arginfo_session_void)
    2686             :         PHP_FE(session_gc,                arginfo_session_void)
    2687             :         PHP_FE(session_set_save_handler,  arginfo_session_set_save_handler)
    2688             :         PHP_FE(session_cache_limiter,     arginfo_session_cache_limiter)
    2689             :         PHP_FE(session_cache_expire,      arginfo_session_cache_expire)
    2690             :         PHP_FE(session_set_cookie_params, arginfo_session_set_cookie_params)
    2691             :         PHP_FE(session_get_cookie_params, arginfo_session_void)
    2692             :         PHP_FE(session_write_close,       arginfo_session_void)
    2693             :         PHP_FE(session_abort,             arginfo_session_void)
    2694             :         PHP_FE(session_reset,             arginfo_session_void)
    2695             :         PHP_FE(session_status,            arginfo_session_void)
    2696             :         PHP_FE(session_register_shutdown, arginfo_session_void)
    2697             :         PHP_FALIAS(session_commit, session_write_close, arginfo_session_void)
    2698             :         PHP_FE_END
    2699             : };
    2700             : /* }}} */
    2701             : 
    2702             : /* {{{ SessionHandlerInterface functions[]
    2703             : */
    2704             : static const zend_function_entry php_session_iface_functions[] = {
    2705             :         PHP_ABSTRACT_ME(SessionHandlerInterface, open, arginfo_session_class_open)
    2706             :         PHP_ABSTRACT_ME(SessionHandlerInterface, close, arginfo_session_class_close)
    2707             :         PHP_ABSTRACT_ME(SessionHandlerInterface, read, arginfo_session_class_read)
    2708             :         PHP_ABSTRACT_ME(SessionHandlerInterface, write, arginfo_session_class_write)
    2709             :         PHP_ABSTRACT_ME(SessionHandlerInterface, destroy, arginfo_session_class_destroy)
    2710             :         PHP_ABSTRACT_ME(SessionHandlerInterface, gc, arginfo_session_class_gc)
    2711             :         PHP_FE_END
    2712             : };
    2713             : /* }}} */
    2714             : 
    2715             : /* {{{ SessionIdInterface functions[]
    2716             : */
    2717             : static const zend_function_entry php_session_id_iface_functions[] = {
    2718             :         PHP_ABSTRACT_ME(SessionIdInterface, create_sid, arginfo_session_class_create_sid)
    2719             :         PHP_FE_END
    2720             : };
    2721             : /* }}} */
    2722             : 
    2723             : /* {{{ SessionUpdateTimestampHandler functions[]
    2724             :  */
    2725             : static const zend_function_entry php_session_update_timestamp_iface_functions[] = {
    2726             :         PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, validateId, arginfo_session_class_validateId)
    2727             :         PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, updateTimestamp, arginfo_session_class_updateTimestamp)
    2728             :         PHP_FE_END
    2729             : };
    2730             : /* }}} */
    2731             : 
    2732             : /* {{{ SessionHandler functions[]
    2733             :  */
    2734             : static const zend_function_entry php_session_class_functions[] = {
    2735             :         PHP_ME(SessionHandler, open, arginfo_session_class_open, ZEND_ACC_PUBLIC)
    2736             :         PHP_ME(SessionHandler, close, arginfo_session_class_close, ZEND_ACC_PUBLIC)
    2737             :         PHP_ME(SessionHandler, read, arginfo_session_class_read, ZEND_ACC_PUBLIC)
    2738             :         PHP_ME(SessionHandler, write, arginfo_session_class_write, ZEND_ACC_PUBLIC)
    2739             :         PHP_ME(SessionHandler, destroy, arginfo_session_class_destroy, ZEND_ACC_PUBLIC)
    2740             :         PHP_ME(SessionHandler, gc, arginfo_session_class_gc, ZEND_ACC_PUBLIC)
    2741             :         PHP_ME(SessionHandler, create_sid, arginfo_session_class_create_sid, ZEND_ACC_PUBLIC)
    2742             :         PHP_FE_END
    2743             : };
    2744             : /* }}} */
    2745             : 
    2746             : /* ********************************
    2747             :    * Module Setup and Destruction *
    2748             :    ******************************** */
    2749             : 
    2750       25144 : static int php_rinit_session(zend_bool auto_start) /* {{{ */
    2751             : {
    2752       25144 :         php_rinit_session_globals();
    2753             : 
    2754       25144 :         if (PS(mod) == NULL) {
    2755             :                 char *value;
    2756             : 
    2757           4 :                 value = zend_ini_string("session.save_handler", sizeof("session.save_handler") - 1, 0);
    2758           4 :                 if (value) {
    2759           4 :                         PS(mod) = _php_find_ps_module(value);
    2760             :                 }
    2761             :         }
    2762             : 
    2763       25144 :         if (PS(serializer) == NULL) {
    2764             :                 char *value;
    2765             : 
    2766           8 :                 value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler") - 1, 0);
    2767           8 :                 if (value) {
    2768           8 :                         PS(serializer) = _php_find_ps_serializer(value);
    2769             :                 }
    2770             :         }
    2771             : 
    2772       25144 :         if (PS(mod) == NULL || PS(serializer) == NULL) {
    2773             :                 /* current status is unusable */
    2774           8 :                 PS(session_status) = php_session_disabled;
    2775           8 :                 return SUCCESS;
    2776             :         }
    2777             : 
    2778       25136 :         if (auto_start) {
    2779          16 :                 php_session_start();
    2780             :         }
    2781             : 
    2782       25136 :         return SUCCESS;
    2783             : } /* }}} */
    2784             : 
    2785       25134 : static PHP_RINIT_FUNCTION(session) /* {{{ */
    2786             : {
    2787       25134 :         return php_rinit_session(PS(auto_start));
    2788             : }
    2789             : /* }}} */
    2790             : 
    2791       25186 : static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */
    2792             : {
    2793             :         int i;
    2794             : 
    2795       25186 :         if (PS(session_status) == php_session_active) {
    2796          57 :                 zend_try {
    2797          57 :                         php_session_flush(1);
    2798          57 :                 } zend_end_try();
    2799             :         }
    2800       25186 :         php_rshutdown_session_globals();
    2801             : 
    2802             :         /* this should NOT be done in php_rshutdown_session_globals() */
    2803      251860 :         for (i = 0; i < PS_NUM_APIS; i++) {
    2804      453348 :                 if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
    2805         336 :                         zval_ptr_dtor(&PS(mod_user_names).names[i]);
    2806         336 :                         ZVAL_UNDEF(&PS(mod_user_names).names[i]);
    2807             :                 }
    2808             :         }
    2809             : 
    2810       25186 :         return SUCCESS;
    2811             : }
    2812             : /* }}} */
    2813             : 
    2814       25184 : static PHP_GINIT_FUNCTION(ps) /* {{{ */
    2815             : {
    2816             :         int i;
    2817             : 
    2818             : #if defined(COMPILE_DL_SESSION) && defined(ZTS)
    2819             :         ZEND_TSRMLS_CACHE_UPDATE();
    2820             : #endif
    2821             : 
    2822       25184 :         ps_globals->save_path = NULL;
    2823       25184 :         ps_globals->session_name = NULL;
    2824       25184 :         ps_globals->id = NULL;
    2825       25184 :         ps_globals->mod = NULL;
    2826       25184 :         ps_globals->serializer = NULL;
    2827       25184 :         ps_globals->mod_data = NULL;
    2828       25184 :         ps_globals->session_status = php_session_none;
    2829       25184 :         ps_globals->default_mod = NULL;
    2830       25184 :         ps_globals->mod_user_implemented = 0;
    2831       25184 :         ps_globals->mod_user_is_open = 0;
    2832       25184 :         ps_globals->session_vars = NULL;
    2833       25184 :         ps_globals->set_handler = 0;
    2834      251840 :         for (i = 0; i < PS_NUM_APIS; i++) {
    2835      226656 :                 ZVAL_UNDEF(&ps_globals->mod_user_names.names[i]);
    2836             :         }
    2837       25184 :         ZVAL_UNDEF(&ps_globals->http_session_vars);
    2838       25184 : }
    2839             : /* }}} */
    2840             : 
    2841       25184 : static PHP_MINIT_FUNCTION(session) /* {{{ */
    2842             : {
    2843             :         zend_class_entry ce;
    2844             : 
    2845       25184 :         zend_register_auto_global(zend_string_init("_SESSION", sizeof("_SESSION") - 1, 1), 0, NULL);
    2846             : 
    2847       25184 :         my_module_number = module_number;
    2848       25184 :         PS(module_number) = module_number;
    2849             : 
    2850       25184 :         PS(session_status) = php_session_none;
    2851       25184 :         REGISTER_INI_ENTRIES();
    2852             : 
    2853             : #ifdef HAVE_LIBMM
    2854             :         PHP_MINIT(ps_mm) (INIT_FUNC_ARGS_PASSTHRU);
    2855             : #endif
    2856       25184 :         php_session_rfc1867_orig_callback = php_rfc1867_callback;
    2857       25184 :         php_rfc1867_callback = php_session_rfc1867_callback;
    2858             : 
    2859             :         /* Register interfaces */
    2860       25184 :         INIT_CLASS_ENTRY(ce, PS_IFACE_NAME, php_session_iface_functions);
    2861       25184 :         php_session_iface_entry = zend_register_internal_class(&ce);
    2862       25184 :         php_session_iface_entry->ce_flags |= ZEND_ACC_INTERFACE;
    2863             : 
    2864       25184 :         INIT_CLASS_ENTRY(ce, PS_SID_IFACE_NAME, php_session_id_iface_functions);
    2865       25184 :         php_session_id_iface_entry = zend_register_internal_class(&ce);
    2866       25184 :         php_session_id_iface_entry->ce_flags |= ZEND_ACC_INTERFACE;
    2867             : 
    2868       25184 :         INIT_CLASS_ENTRY(ce, PS_UPDATE_TIMESTAMP_IFACE_NAME, php_session_update_timestamp_iface_functions);
    2869       25184 :         php_session_update_timestamp_iface_entry = zend_register_internal_class(&ce);
    2870       25184 :         php_session_update_timestamp_iface_entry->ce_flags |= ZEND_ACC_INTERFACE;
    2871             : 
    2872             :         /* Register base class */
    2873       25184 :         INIT_CLASS_ENTRY(ce, PS_CLASS_NAME, php_session_class_functions);
    2874       25184 :         php_session_class_entry = zend_register_internal_class(&ce);
    2875       25184 :         zend_class_implements(php_session_class_entry, 1, php_session_iface_entry);
    2876       25184 :         zend_class_implements(php_session_class_entry, 1, php_session_id_iface_entry);
    2877             : 
    2878       25184 :         REGISTER_LONG_CONSTANT("PHP_SESSION_DISABLED", php_session_disabled, CONST_CS | CONST_PERSISTENT);
    2879       25184 :         REGISTER_LONG_CONSTANT("PHP_SESSION_NONE", php_session_none, CONST_CS | CONST_PERSISTENT);
    2880       25184 :         REGISTER_LONG_CONSTANT("PHP_SESSION_ACTIVE", php_session_active, CONST_CS | CONST_PERSISTENT);
    2881             : 
    2882       25184 :         return SUCCESS;
    2883             : }
    2884             : /* }}} */
    2885             : 
    2886       25233 : static PHP_MSHUTDOWN_FUNCTION(session) /* {{{ */
    2887             : {
    2888       25233 :         UNREGISTER_INI_ENTRIES();
    2889             : 
    2890             : #ifdef HAVE_LIBMM
    2891             :         PHP_MSHUTDOWN(ps_mm) (SHUTDOWN_FUNC_ARGS_PASSTHRU);
    2892             : #endif
    2893             : 
    2894             :         /* reset rfc1867 callbacks */
    2895       25233 :         php_session_rfc1867_orig_callback = NULL;
    2896       25233 :         if (php_rfc1867_callback == php_session_rfc1867_callback) {
    2897       25233 :                 php_rfc1867_callback = NULL;
    2898             :         }
    2899             : 
    2900       25233 :         ps_serializers[PREDEFINED_SERIALIZERS].name = NULL;
    2901       25233 :         memset(&ps_modules[PREDEFINED_MODULES], 0, (MAX_MODULES-PREDEFINED_MODULES)*sizeof(ps_module *));
    2902             : 
    2903       25233 :         return SUCCESS;
    2904             : }
    2905             : /* }}} */
    2906             : 
    2907         148 : static PHP_MINFO_FUNCTION(session) /* {{{ */
    2908             : {
    2909             :         ps_module **mod;
    2910             :         ps_serializer *ser;
    2911         148 :         smart_str save_handlers = {0};
    2912         148 :         smart_str ser_handlers = {0};
    2913             :         int i;
    2914             : 
    2915             :         /* Get save handlers */
    2916        4884 :         for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
    2917        4736 :                 if (*mod && (*mod)->s_name) {
    2918         296 :                         smart_str_appends(&save_handlers, (*mod)->s_name);
    2919             :                         smart_str_appendc(&save_handlers, ' ');
    2920             :                 }
    2921             :         }
    2922             : 
    2923             :         /* Get serializer handlers */
    2924        4884 :         for (i = 0, ser = ps_serializers; i < MAX_SERIALIZERS; i++, ser++) {
    2925        4736 :                 if (ser && ser->name) {
    2926         592 :                         smart_str_appends(&ser_handlers, ser->name);
    2927             :                         smart_str_appendc(&ser_handlers, ' ');
    2928             :                 }
    2929             :         }
    2930             : 
    2931         148 :         php_info_print_table_start();
    2932         148 :         php_info_print_table_row(2, "Session Support", "enabled" );
    2933             : 
    2934         148 :         if (save_handlers.s) {
    2935             :                 smart_str_0(&save_handlers);
    2936         148 :                 php_info_print_table_row(2, "Registered save handlers", ZSTR_VAL(save_handlers.s));
    2937             :                 smart_str_free(&save_handlers);
    2938             :         } else {
    2939           0 :                 php_info_print_table_row(2, "Registered save handlers", "none");
    2940             :         }
    2941             : 
    2942         148 :         if (ser_handlers.s) {
    2943             :                 smart_str_0(&ser_handlers);
    2944         148 :                 php_info_print_table_row(2, "Registered serializer handlers", ZSTR_VAL(ser_handlers.s));
    2945             :                 smart_str_free(&ser_handlers);
    2946             :         } else {
    2947           0 :                 php_info_print_table_row(2, "Registered serializer handlers", "none");
    2948             :         }
    2949             : 
    2950         148 :         php_info_print_table_end();
    2951             : 
    2952         148 :         DISPLAY_INI_ENTRIES();
    2953         148 : }
    2954             : /* }}} */
    2955             : 
    2956             : static const zend_module_dep session_deps[] = { /* {{{ */
    2957             :         ZEND_MOD_OPTIONAL("hash")
    2958             :         ZEND_MOD_REQUIRED("spl")
    2959             :         ZEND_MOD_END
    2960             : };
    2961             : /* }}} */
    2962             : 
    2963             : /* ************************
    2964             :    * Upload hook handling *
    2965             :    ************************ */
    2966             : 
    2967          12 : static zend_bool early_find_sid_in(zval *dest, int where, php_session_rfc1867_progress *progress) /* {{{ */
    2968             : {
    2969             :         zval *ppid;
    2970             : 
    2971          24 :         if (Z_ISUNDEF(PG(http_globals)[where])) {
    2972           0 :                 return 0;
    2973             :         }
    2974             : 
    2975          12 :         if ((ppid = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[where]), PS(session_name), progress->sname_len))
    2976           8 :                         && Z_TYPE_P(ppid) == IS_STRING) {
    2977             :                 zval_dtor(dest);
    2978           8 :                 ZVAL_DEREF(ppid);
    2979           8 :                 ZVAL_COPY(dest, ppid);
    2980           8 :                 return 1;
    2981             :         }
    2982             : 
    2983           4 :         return 0;
    2984             : } /* }}} */
    2985             : 
    2986          10 : static void php_session_rfc1867_early_find_sid(php_session_rfc1867_progress *progress) /* {{{ */
    2987             : {
    2988             : 
    2989          10 :         if (PS(use_cookies)) {
    2990           9 :                 sapi_module.treat_data(PARSE_COOKIE, NULL, NULL);
    2991           9 :                 if (early_find_sid_in(&progress->sid, TRACK_VARS_COOKIE, progress)) {
    2992           6 :                         progress->apply_trans_sid = 0;
    2993           6 :                         return;
    2994             :                 }
    2995             :         }
    2996           4 :         if (PS(use_only_cookies)) {
    2997           1 :                 return;
    2998             :         }
    2999           3 :         sapi_module.treat_data(PARSE_GET, NULL, NULL);
    3000           3 :         early_find_sid_in(&progress->sid, TRACK_VARS_GET, progress);
    3001             : } /* }}} */
    3002             : 
    3003          19 : static zend_bool php_check_cancel_upload(php_session_rfc1867_progress *progress) /* {{{ */
    3004             : {
    3005             :         zval *progress_ary, *cancel_upload;
    3006             : 
    3007          38 :         if ((progress_ary = zend_symtable_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), progress->key.s)) == NULL) {
    3008          10 :                 return 0;
    3009             :         }
    3010           9 :         if (Z_TYPE_P(progress_ary) != IS_ARRAY) {
    3011           0 :                 return 0;
    3012             :         }
    3013           9 :         if ((cancel_upload = zend_hash_str_find(Z_ARRVAL_P(progress_ary), "cancel_upload", sizeof("cancel_upload") - 1)) == NULL) {
    3014           9 :                 return 0;
    3015             :         }
    3016           0 :         return Z_TYPE_P(cancel_upload) == IS_TRUE;
    3017             : } /* }}} */
    3018             : 
    3019          69 : static void php_session_rfc1867_update(php_session_rfc1867_progress *progress, int force_update) /* {{{ */
    3020             : {
    3021          69 :         if (!force_update) {
    3022          60 :                 if (Z_LVAL_P(progress->post_bytes_processed) < progress->next_update) {
    3023          15 :                         return;
    3024             :                 }
    3025             : #ifdef HAVE_GETTIMEOFDAY
    3026          45 :                 if (PS(rfc1867_min_freq) > 0.0) {
    3027          45 :                         struct timeval tv = {0};
    3028             :                         double dtv;
    3029          45 :                         gettimeofday(&tv, NULL);
    3030          45 :                         dtv = (double) tv.tv_sec + tv.tv_usec / 1000000.0;
    3031          45 :                         if (dtv < progress->next_update_time) {
    3032          35 :                                 return;
    3033             :                         }
    3034          10 :                         progress->next_update_time = dtv + PS(rfc1867_min_freq);
    3035             :                 }
    3036             : #endif
    3037          10 :                 progress->next_update = Z_LVAL_P(progress->post_bytes_processed) + progress->update_step;
    3038             :         }
    3039             : 
    3040          19 :         php_session_initialize();
    3041          19 :         PS(session_status) = php_session_active;
    3042          38 :         IF_SESSION_VARS() {
    3043          19 :                 zval *sess_var = Z_REFVAL(PS(http_session_vars));
    3044          19 :                 SEPARATE_ARRAY(sess_var);
    3045             : 
    3046          19 :                 progress->cancel_upload |= php_check_cancel_upload(progress);
    3047          19 :                 Z_TRY_ADDREF(progress->data);
    3048          19 :                 zend_hash_update(Z_ARRVAL_P(sess_var), progress->key.s, &progress->data);
    3049             :         }
    3050          19 :         php_session_flush(1);
    3051             : } /* }}} */
    3052             : 
    3053           1 : static void php_session_rfc1867_cleanup(php_session_rfc1867_progress *progress) /* {{{ */
    3054             : {
    3055           1 :         php_session_initialize();
    3056           1 :         PS(session_status) = php_session_active;
    3057           2 :         IF_SESSION_VARS() {
    3058           1 :                 zval *sess_var = Z_REFVAL(PS(http_session_vars));
    3059           1 :                 SEPARATE_ARRAY(sess_var);
    3060           1 :                 zend_hash_del(Z_ARRVAL_P(sess_var), progress->key.s);
    3061             :         }
    3062           1 :         php_session_flush(1);
    3063           1 : } /* }}} */
    3064             : 
    3065         281 : static int php_session_rfc1867_callback(unsigned int event, void *event_data, void **extra) /* {{{ */
    3066             : {
    3067             :         php_session_rfc1867_progress *progress;
    3068         281 :         int retval = SUCCESS;
    3069             : 
    3070         281 :         if (php_session_rfc1867_orig_callback) {
    3071           0 :                 retval = php_session_rfc1867_orig_callback(event, event_data, extra);
    3072             :         }
    3073         281 :         if (!PS(rfc1867_enabled)) {
    3074           9 :                 return retval;
    3075             :         }
    3076             : 
    3077         272 :         progress = PS(rfc1867_progress);
    3078             : 
    3079         272 :         switch(event) {
    3080          34 :                 case MULTIPART_EVENT_START: {
    3081          34 :                         multipart_event_start *data = (multipart_event_start *) event_data;
    3082          34 :                         progress = ecalloc(1, sizeof(php_session_rfc1867_progress));
    3083          34 :                         progress->content_length = data->content_length;
    3084          34 :                         progress->sname_len  = strlen(PS(session_name));
    3085          34 :                         PS(rfc1867_progress) = progress;
    3086             :                 }
    3087          34 :                 break;
    3088          58 :                 case MULTIPART_EVENT_FORMDATA: {
    3089          58 :                         multipart_event_formdata *data = (multipart_event_formdata *) event_data;
    3090             :                         size_t value_len;
    3091             : 
    3092         116 :                         if (Z_TYPE(progress->sid) && progress->key.s) {
    3093           1 :                                 break;
    3094             :                         }
    3095             : 
    3096             :                         /* orig callback may have modified *data->newlength */
    3097          57 :                         if (data->newlength) {
    3098           0 :                                 value_len = *data->newlength;
    3099             :                         } else {
    3100          57 :                                 value_len = data->length;
    3101             :                         }
    3102             : 
    3103          57 :                         if (data->name && data->value && value_len) {
    3104          56 :                                 size_t name_len = strlen(data->name);
    3105             : 
    3106          56 :                                 if (name_len == progress->sname_len && memcmp(data->name, PS(session_name), name_len) == 0) {
    3107          11 :                                         zval_dtor(&progress->sid);
    3108          22 :                                         ZVAL_STRINGL(&progress->sid, (*data->value), value_len);
    3109          45 :                                 } else if (name_len == strlen(PS(rfc1867_name)) && memcmp(data->name, PS(rfc1867_name), name_len + 1) == 0) {
    3110          10 :                                         smart_str_free(&progress->key);
    3111          10 :                                         smart_str_appends(&progress->key, PS(rfc1867_prefix));
    3112          10 :                                         smart_str_appendl(&progress->key, *data->value, value_len);
    3113          10 :                                         smart_str_0(&progress->key);
    3114             : 
    3115          10 :                                         progress->apply_trans_sid = APPLY_TRANS_SID;
    3116          10 :                                         php_session_rfc1867_early_find_sid(progress);
    3117             :                                 }
    3118             :                         }
    3119             :                 }
    3120          57 :                 break;
    3121          50 :                 case MULTIPART_EVENT_FILE_START: {
    3122          50 :                         multipart_event_file_start *data = (multipart_event_file_start *) event_data;
    3123             : 
    3124             :                         /* Do nothing when $_POST["PHP_SESSION_UPLOAD_PROGRESS"] is not set
    3125             :                          * or when we have no session id */
    3126         100 :                         if (!Z_TYPE(progress->sid) || !progress->key.s) {
    3127             :                                 break;
    3128             :                         }
    3129             : 
    3130             :                         /* First FILE_START event, initializing data */
    3131          40 :                         if (Z_ISUNDEF(progress->data)) {
    3132             : 
    3133          10 :                                 if (PS(rfc1867_freq) >= 0) {
    3134           7 :                                         progress->update_step = PS(rfc1867_freq);
    3135           3 :                                 } else if (PS(rfc1867_freq) < 0) { /* % of total size */
    3136           3 :                                         progress->update_step = progress->content_length * -PS(rfc1867_freq) / 100;
    3137             :                                 }
    3138          10 :                                 progress->next_update = 0;
    3139          10 :                                 progress->next_update_time = 0.0;
    3140             : 
    3141          10 :                                 array_init(&progress->data);
    3142          10 :                                 array_init(&progress->files);
    3143             : 
    3144          10 :                                 add_assoc_long_ex(&progress->data, "start_time", sizeof("start_time") - 1, (zend_long)sapi_get_request_time());
    3145          10 :                                 add_assoc_long_ex(&progress->data, "content_length",  sizeof("content_length") - 1, progress->content_length);
    3146          10 :                                 add_assoc_long_ex(&progress->data, "bytes_processed", sizeof("bytes_processed") - 1, data->post_bytes_processed);
    3147          10 :                                 add_assoc_bool_ex(&progress->data, "done", sizeof("done") - 1, 0);
    3148          10 :                                 add_assoc_zval_ex(&progress->data, "files", sizeof("files") - 1, &progress->files);
    3149             : 
    3150          10 :                                 progress->post_bytes_processed = zend_hash_str_find(Z_ARRVAL(progress->data), "bytes_processed", sizeof("bytes_processed") - 1);
    3151             : 
    3152          10 :                                 php_rinit_session(0);
    3153          20 :                                 PS(id) = zend_string_init(Z_STRVAL(progress->sid), Z_STRLEN(progress->sid), 0);
    3154          10 :                                 if (progress->apply_trans_sid) {
    3155             :                                         /* Enable trans sid by modifying flags */
    3156           0 :                                         PS(use_trans_sid) = 1;
    3157           0 :                                         PS(use_only_cookies) = 0;
    3158             :                                 }
    3159          10 :                                 PS(send_cookie) = 0;
    3160             :                         }
    3161             : 
    3162          20 :                         array_init(&progress->current_file);
    3163             : 
    3164             :                         /* Each uploaded file has its own array. Trying to make it close to $_FILES entries. */
    3165          20 :                         add_assoc_string_ex(&progress->current_file, "field_name", sizeof("field_name") - 1, data->name);
    3166          20 :                         add_assoc_string_ex(&progress->current_file, "name", sizeof("name") - 1, *data->filename);
    3167          20 :                         add_assoc_null_ex(&progress->current_file, "tmp_name", sizeof("tmp_name") - 1);
    3168          20 :                         add_assoc_long_ex(&progress->current_file, "error", sizeof("error") - 1, 0);
    3169             : 
    3170          20 :                         add_assoc_bool_ex(&progress->current_file, "done", sizeof("done") - 1, 0);
    3171          20 :                         add_assoc_long_ex(&progress->current_file, "start_time", sizeof("start_time") - 1, (zend_long)time(NULL));
    3172          20 :                         add_assoc_long_ex(&progress->current_file, "bytes_processed", sizeof("bytes_processed") - 1, 0);
    3173             : 
    3174          20 :                         add_next_index_zval(&progress->files, &progress->current_file);
    3175             : 
    3176          20 :                         progress->current_file_bytes_processed = zend_hash_str_find(Z_ARRVAL(progress->current_file), "bytes_processed", sizeof("bytes_processed") - 1);
    3177             : 
    3178          20 :                         Z_LVAL_P(progress->current_file_bytes_processed) =  data->post_bytes_processed;
    3179          20 :                         php_session_rfc1867_update(progress, 0);
    3180             :                 }
    3181          20 :                 break;
    3182          46 :                 case MULTIPART_EVENT_FILE_DATA: {
    3183          46 :                         multipart_event_file_data *data = (multipart_event_file_data *) event_data;
    3184             : 
    3185          92 :                         if (!Z_TYPE(progress->sid) || !progress->key.s) {
    3186             :                                 break;
    3187             :                         }
    3188             : 
    3189          20 :                         Z_LVAL_P(progress->current_file_bytes_processed) = data->offset + data->length;
    3190          20 :                         Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed;
    3191             : 
    3192          20 :                         php_session_rfc1867_update(progress, 0);
    3193             :                 }
    3194          20 :                 break;
    3195          50 :                 case MULTIPART_EVENT_FILE_END: {
    3196          50 :                         multipart_event_file_end *data = (multipart_event_file_end *) event_data;
    3197             : 
    3198         100 :                         if (!Z_TYPE(progress->sid) || !progress->key.s) {
    3199             :                                 break;
    3200             :                         }
    3201             : 
    3202          20 :                         if (data->temp_filename) {
    3203          20 :                                 add_assoc_string_ex(&progress->current_file, "tmp_name",  sizeof("tmp_name") - 1, data->temp_filename);
    3204             :                         }
    3205             : 
    3206          20 :                         add_assoc_long_ex(&progress->current_file, "error", sizeof("error") - 1, data->cancel_upload);
    3207          20 :                         add_assoc_bool_ex(&progress->current_file, "done", sizeof("done") - 1,  1);
    3208             : 
    3209          20 :                         Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed;
    3210             : 
    3211          20 :                         php_session_rfc1867_update(progress, 0);
    3212             :                 }
    3213          20 :                 break;
    3214          34 :                 case MULTIPART_EVENT_END: {
    3215          34 :                         multipart_event_end *data = (multipart_event_end *) event_data;
    3216             : 
    3217          68 :                         if (Z_TYPE(progress->sid) && progress->key.s) {
    3218          10 :                                 if (PS(rfc1867_cleanup)) {
    3219           1 :                                         php_session_rfc1867_cleanup(progress);
    3220             :                                 } else {
    3221           9 :                                         SEPARATE_ARRAY(&progress->data);
    3222           9 :                                         add_assoc_bool_ex(&progress->data, "done", sizeof("done") - 1, 1);
    3223           9 :                                         Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed;
    3224           9 :                                         php_session_rfc1867_update(progress, 1);
    3225             :                                 }
    3226          10 :                                 php_rshutdown_session_globals();
    3227             :                         }
    3228             : 
    3229          68 :                         if (!Z_ISUNDEF(progress->data)) {
    3230          10 :                                 zval_ptr_dtor(&progress->data);
    3231             :                         }
    3232          34 :                         zval_ptr_dtor(&progress->sid);
    3233          34 :                         smart_str_free(&progress->key);
    3234          34 :                         efree(progress);
    3235          34 :                         progress = NULL;
    3236          34 :                         PS(rfc1867_progress) = NULL;
    3237             :                 }
    3238          34 :                 break;
    3239             :         }
    3240             : 
    3241         272 :         if (progress && progress->cancel_upload) {
    3242           0 :                 return FAILURE;
    3243             :         }
    3244         272 :         return retval;
    3245             : 
    3246             : } /* }}} */
    3247             : 
    3248             : zend_module_entry session_module_entry = {
    3249             :         STANDARD_MODULE_HEADER_EX,
    3250             :         NULL,
    3251             :         session_deps,
    3252             :         "session",
    3253             :         session_functions,
    3254             :         PHP_MINIT(session), PHP_MSHUTDOWN(session),
    3255             :         PHP_RINIT(session), PHP_RSHUTDOWN(session),
    3256             :         PHP_MINFO(session),
    3257             :         PHP_SESSION_VERSION,
    3258             :         PHP_MODULE_GLOBALS(ps),
    3259             :         PHP_GINIT(ps),
    3260             :         NULL,
    3261             :         NULL,
    3262             :         STANDARD_MODULE_PROPERTIES_EX
    3263             : };
    3264             : 
    3265             : #ifdef COMPILE_DL_SESSION
    3266             : #ifdef ZTS
    3267             : ZEND_TSRMLS_CACHE_DEFINE()
    3268             : #endif
    3269             : ZEND_GET_MODULE(session)
    3270             : #endif
    3271             : 
    3272             : /*
    3273             :  * Local variables:
    3274             :  * tab-width: 4
    3275             :  * c-basic-offset: 4
    3276             :  * End:
    3277             :  * vim600: noet sw=4 ts=4 fdm=marker
    3278             :  * vim<600: sw=4 ts=4
    3279             :  */

Generated by: LCOV version 1.10

Generated at Wed, 17 Jul 2019 14:54:06 +0000 (30 hours ago)

Copyright © 2005-2019 The PHP Group
All rights reserved.