Back to index

php5  5.3.10
session.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Sascha Schumann <sascha@schumann.cx>                        |
00016    |          Andrei Zmievski <andrei@php.net>                            |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /* $Id: session.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025 
00026 #include "php.h"
00027 
00028 #ifdef PHP_WIN32
00029 # include "win32/winutil.h"
00030 # include "win32/time.h"
00031 #else
00032 #include <sys/time.h>
00033 #endif
00034 
00035 #include <sys/stat.h>
00036 #include <fcntl.h>
00037 
00038 #include "php_ini.h"
00039 #include "SAPI.h"
00040 #include "php_session.h"
00041 #include "ext/standard/md5.h"
00042 #include "ext/standard/sha1.h"
00043 #include "ext/standard/php_var.h"
00044 #include "ext/date/php_date.h"
00045 #include "ext/standard/php_lcg.h"
00046 #include "ext/standard/url_scanner_ex.h"
00047 #include "ext/standard/php_rand.h" /* for RAND_MAX */
00048 #include "ext/standard/info.h"
00049 #include "ext/standard/php_smart_str.h"
00050 #include "ext/standard/url.h"
00051 
00052 #include "mod_files.h"
00053 #include "mod_user.h"
00054 
00055 #ifdef HAVE_LIBMM
00056 #include "mod_mm.h"
00057 #endif
00058 
00059 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(ps);
00060 
00061 /* ***********
00062    * Helpers *
00063    *********** */
00064 
00065 #define IF_SESSION_VARS() \
00066        if (PS(http_session_vars) && PS(http_session_vars)->type == IS_ARRAY)
00067 
00068 #define SESSION_CHECK_ACTIVE_STATE \
00069        if (PS(session_status) == php_session_active) {  \
00070               php_error_docref(NULL TSRMLS_CC, E_WARNING, "A session is active. You cannot change the session module's ini settings at this time");       \
00071               return FAILURE;      \
00072        }
00073 
00074 /* Dispatched by RINIT and by php_session_destroy */
00075 static inline void php_rinit_session_globals(TSRMLS_D) /* {{{ */
00076 {
00077        PS(id) = NULL;
00078        PS(session_status) = php_session_none;
00079        PS(mod_data) = NULL;
00080        /* Do NOT init PS(mod_user_names) here! */
00081        PS(http_session_vars) = NULL;
00082 }
00083 /* }}} */
00084 
00085 /* Dispatched by RSHUTDOWN and by php_session_destroy */
00086 static inline void php_rshutdown_session_globals(TSRMLS_D) /* {{{ */
00087 {
00088        if (PS(http_session_vars)) {
00089               zval_ptr_dtor(&PS(http_session_vars));
00090               PS(http_session_vars) = NULL;
00091        }
00092        /* Do NOT destroy PS(mod_user_names) here! */
00093        if (PS(mod_data)) {
00094               zend_try {
00095                      PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
00096               } zend_end_try();
00097        }
00098        if (PS(id)) {
00099               efree(PS(id));
00100        }
00101 }
00102 /* }}} */
00103 
00104 static int php_session_destroy(TSRMLS_D) /* {{{ */
00105 {
00106        int retval = SUCCESS;
00107 
00108        if (PS(session_status) != php_session_active) {
00109               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Trying to destroy uninitialized session");
00110               return FAILURE;
00111        }
00112 
00113        if (PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) {
00114               retval = FAILURE;
00115               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session object destruction failed");
00116        }
00117 
00118        php_rshutdown_session_globals(TSRMLS_C);
00119        php_rinit_session_globals(TSRMLS_C);
00120 
00121        return retval;
00122 }
00123 /* }}} */
00124 
00125 PHPAPI void php_add_session_var(char *name, size_t namelen TSRMLS_DC) /* {{{ */
00126 {
00127        zval **sym_track = NULL;
00128 
00129        IF_SESSION_VARS() {
00130               zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, (void *) &sym_track);
00131        } else {
00132               return;
00133        }
00134 
00135        /* Set up a proper reference between $_SESSION["x"] and $x. */
00136 
00137        if (PG(register_globals)) {
00138               zval **sym_global = NULL;
00139 
00140               if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void *) &sym_global) == SUCCESS) {
00141                      if ((Z_TYPE_PP(sym_global) == IS_ARRAY && Z_ARRVAL_PP(sym_global) == &EG(symbol_table)) || *sym_global == PS(http_session_vars)) {
00142                             return;
00143                      }
00144               }
00145 
00146               if (sym_global == NULL && sym_track == NULL) {
00147                      zval *empty_var;
00148 
00149                      ALLOC_INIT_ZVAL(empty_var); /* this sets refcount to 1 */
00150                      Z_SET_REFCOUNT_P(empty_var, 0); /* our module does not maintain a ref */
00151                      /* The next call will increase refcount by NR_OF_SYM_TABLES==2 */
00152                      zend_set_hash_symbol(empty_var, name, namelen, 1, 2, Z_ARRVAL_P(PS(http_session_vars)), &EG(symbol_table));
00153               } else if (sym_global == NULL) {
00154                      SEPARATE_ZVAL_IF_NOT_REF(sym_track);
00155                      zend_set_hash_symbol(*sym_track, name, namelen, 1, 1, &EG(symbol_table));
00156               } else if (sym_track == NULL) {
00157                      SEPARATE_ZVAL_IF_NOT_REF(sym_global);
00158                      zend_set_hash_symbol(*sym_global, name, namelen, 1, 1, Z_ARRVAL_P(PS(http_session_vars)));
00159               }
00160        } else {
00161               if (sym_track == NULL) {
00162                      zval *empty_var;
00163 
00164                      ALLOC_INIT_ZVAL(empty_var);
00165                      ZEND_SET_SYMBOL_WITH_LENGTH(Z_ARRVAL_P(PS(http_session_vars)), name, namelen+1, empty_var, 1, 0);
00166               }
00167        }
00168 }
00169 /* }}} */
00170 
00171 PHPAPI void php_set_session_var(char *name, size_t namelen, zval *state_val, php_unserialize_data_t *var_hash TSRMLS_DC) /* {{{ */
00172 {
00173        if (PG(register_globals)) {
00174               zval **old_symbol;
00175               if (zend_hash_find(&EG(symbol_table),name,namelen+1,(void *)&old_symbol) == SUCCESS) {
00176                      if ((Z_TYPE_PP(old_symbol) == IS_ARRAY && Z_ARRVAL_PP(old_symbol) == &EG(symbol_table)) || *old_symbol == PS(http_session_vars)) {
00177                             return;
00178                      }
00179 
00180                      /* A global symbol with the same name exists already. That
00181                       * symbol might have been created by other means (e.g. $_GET).
00182                       *
00183                       * hash_update in zend_set_hash_symbol is not good, because
00184                       * it will leave referenced variables (such as local instances
00185                       * of a global variable) dangling.
00186                       *
00187                       * BTW: if you use register_globals references between
00188                       * session-vars won't work because of this very reason! */
00189 
00190                      REPLACE_ZVAL_VALUE(old_symbol,state_val,1);
00191 
00192                      /* The following line will update the reference table used for
00193                       * unserialization.  It is optional, because some storage
00194                       * formats may not be able to represent references. */
00195 
00196                      if (var_hash) {
00197                             PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash,state_val,*old_symbol);
00198                      }
00199 
00200                      zend_set_hash_symbol(*old_symbol, name, namelen, 1, 1, Z_ARRVAL_P(PS(http_session_vars)));
00201               } else {
00202                      zend_set_hash_symbol(state_val, name, namelen, 1, 2, Z_ARRVAL_P(PS(http_session_vars)), &EG(symbol_table));
00203               }
00204        } else IF_SESSION_VARS() {
00205               zend_set_hash_symbol(state_val, name, namelen, PZVAL_IS_REF(state_val), 1, Z_ARRVAL_P(PS(http_session_vars)));
00206        }
00207 }
00208 /* }}} */
00209 
00210 PHPAPI int php_get_session_var(char *name, size_t namelen, zval ***state_var TSRMLS_DC) /* {{{ */
00211 {
00212        int ret = FAILURE;
00213 
00214        IF_SESSION_VARS() {
00215               ret = zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, (void **) state_var);
00216 
00217               /* If register_globals is enabled, and
00218                * if there is an entry for the slot in $_SESSION, and
00219                * if that entry is still set to NULL, and
00220                * if the global var exists, then
00221                * we prefer the same key in the global sym table. */
00222 
00223               if (PG(register_globals) && ret == SUCCESS && Z_TYPE_PP(*state_var) == IS_NULL) {
00224                      zval **tmp;
00225 
00226                      if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
00227                             *state_var = tmp;
00228                      }
00229               }
00230        }
00231        return ret;
00232 }
00233 /* }}} */
00234 
00235 static void php_session_track_init(TSRMLS_D) /* {{{ */
00236 {
00237        zval *session_vars = NULL;
00238 
00239        /* Unconditionally destroy existing arrays -- possible dirty data */
00240        zend_delete_global_variable("HTTP_SESSION_VARS", sizeof("HTTP_SESSION_VARS")-1 TSRMLS_CC);
00241        zend_delete_global_variable("_SESSION", sizeof("_SESSION")-1 TSRMLS_CC);
00242 
00243        if (PS(http_session_vars)) {
00244               zval_ptr_dtor(&PS(http_session_vars));
00245        }
00246 
00247        MAKE_STD_ZVAL(session_vars);
00248        array_init(session_vars);
00249        PS(http_session_vars) = session_vars;
00250 
00251        if (PG(register_long_arrays)) {
00252               ZEND_SET_GLOBAL_VAR_WITH_LENGTH("HTTP_SESSION_VARS", sizeof("HTTP_SESSION_VARS"), PS(http_session_vars), 3, 1);
00253               ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 3, 1);
00254        }
00255        else {
00256               ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 2, 1);
00257        }
00258 }
00259 /* }}} */
00260 
00261 static char *php_session_encode(int *newlen TSRMLS_DC) /* {{{ */
00262 {
00263        char *ret = NULL;
00264 
00265        IF_SESSION_VARS() {
00266               if (!PS(serializer)) {
00267                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to encode session object");
00268                      ret = NULL;
00269               } else if (PS(serializer)->encode(&ret, newlen TSRMLS_CC) == FAILURE) {
00270                      ret = NULL;
00271               }
00272        } else {
00273               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot encode non-existent session");
00274        }
00275        return ret;
00276 }
00277 /* }}} */
00278 
00279 static void php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */
00280 {
00281        if (!PS(serializer)) {
00282               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
00283               return;
00284        }
00285        if (PS(serializer)->decode(val, vallen TSRMLS_CC) == FAILURE) {
00286               php_session_destroy(TSRMLS_C);
00287               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to decode session object. Session has been destroyed");
00288        }
00289 }
00290 /* }}} */
00291 
00292 /*
00293  * Note that we cannot use the BASE64 alphabet here, because
00294  * it contains "/" and "+": both are unacceptable for simple inclusion
00295  * into URLs.
00296  */
00297 
00298 static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-";
00299 
00300 enum {
00301        PS_HASH_FUNC_MD5,
00302        PS_HASH_FUNC_SHA1,
00303        PS_HASH_FUNC_OTHER
00304 };
00305 
00306 /* returns a pointer to the byte after the last valid character in out */
00307 static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {{{ */
00308 {
00309        unsigned char *p, *q;
00310        unsigned short w;
00311        int mask;
00312        int have;
00313 
00314        p = (unsigned char *) in;
00315        q = (unsigned char *)in + inlen;
00316 
00317        w = 0;
00318        have = 0;
00319        mask = (1 << nbits) - 1;
00320 
00321        while (1) {
00322               if (have < nbits) {
00323                      if (p < q) {
00324                             w |= *p++ << have;
00325                             have += 8;
00326                      } else {
00327                             /* consumed everything? */
00328                             if (have == 0) break;
00329                             /* No? We need a final round */
00330                             have = nbits;
00331                      }
00332               }
00333 
00334               /* consume nbits */
00335               *out++ = hexconvtab[w & mask];
00336               w >>= nbits;
00337               have -= nbits;
00338        }
00339 
00340        *out = '\0';
00341        return out;
00342 }
00343 /* }}} */
00344 
00345 PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
00346 {
00347        PHP_MD5_CTX md5_context;
00348        PHP_SHA1_CTX sha1_context;
00349 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00350        void *hash_context;
00351 #endif
00352        unsigned char *digest;
00353        int digest_len;
00354        int j;
00355        char *buf, *outid;
00356        struct timeval tv;
00357        zval **array;
00358        zval **token;
00359        char *remote_addr = NULL;
00360 
00361        gettimeofday(&tv, NULL);
00362 
00363        if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &array) == SUCCESS &&
00364               Z_TYPE_PP(array) == IS_ARRAY &&
00365               zend_hash_find(Z_ARRVAL_PP(array), "REMOTE_ADDR", sizeof("REMOTE_ADDR"), (void **) &token) == SUCCESS
00366        ) {
00367               remote_addr = Z_STRVAL_PP(token);
00368        }
00369 
00370        /* maximum 15+19+19+10 bytes */
00371        spprintf(&buf, 0, "%.15s%ld%ld%0.8F", remote_addr ? remote_addr : "", tv.tv_sec, (long int)tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10);
00372 
00373        switch (PS(hash_func)) {
00374               case PS_HASH_FUNC_MD5:
00375                      PHP_MD5Init(&md5_context);
00376                      PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
00377                      digest_len = 16;
00378                      break;
00379               case PS_HASH_FUNC_SHA1:
00380                      PHP_SHA1Init(&sha1_context);
00381                      PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf));
00382                      digest_len = 20;
00383                      break;
00384 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00385               case PS_HASH_FUNC_OTHER:
00386                      if (!PS(hash_ops)) {
00387                             php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
00388                             efree(buf);
00389                             return NULL;
00390                      }
00391 
00392                      hash_context = emalloc(PS(hash_ops)->context_size);
00393                      PS(hash_ops)->hash_init(hash_context);
00394                      PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf));
00395                      digest_len = PS(hash_ops)->digest_size;
00396                      break;
00397 #endif /* HAVE_HASH_EXT */
00398               default:
00399                      php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
00400                      efree(buf);
00401                      return NULL;
00402        }
00403        efree(buf);
00404 
00405        if (PS(entropy_length) > 0) {
00406 #ifdef PHP_WIN32
00407               unsigned char rbuf[2048];
00408               size_t toread = PS(entropy_length);
00409 
00410               if (php_win32_get_random_bytes(rbuf, (size_t) toread) == SUCCESS){
00411 
00412                      switch (PS(hash_func)) {
00413                             case PS_HASH_FUNC_MD5:
00414                                    PHP_MD5Update(&md5_context, rbuf, toread);
00415                                    break;
00416                             case PS_HASH_FUNC_SHA1:
00417                                    PHP_SHA1Update(&sha1_context, rbuf, toread);
00418                                    break;
00419 # if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00420                             case PS_HASH_FUNC_OTHER:
00421                                    PS(hash_ops)->hash_update(hash_context, rbuf, toread);
00422                                    break;
00423 # endif /* HAVE_HASH_EXT */
00424                      }
00425               }
00426 #else
00427               int fd;
00428 
00429               fd = VCWD_OPEN(PS(entropy_file), O_RDONLY);
00430               if (fd >= 0) {
00431                      unsigned char rbuf[2048];
00432                      int n;
00433                      int to_read = PS(entropy_length);
00434 
00435                      while (to_read > 0) {
00436                             n = read(fd, rbuf, MIN(to_read, sizeof(rbuf)));
00437                             if (n <= 0) break;
00438 
00439                             switch (PS(hash_func)) {
00440                                    case PS_HASH_FUNC_MD5:
00441                                           PHP_MD5Update(&md5_context, rbuf, n);
00442                                           break;
00443                                    case PS_HASH_FUNC_SHA1:
00444                                           PHP_SHA1Update(&sha1_context, rbuf, n);
00445                                           break;
00446 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00447                                    case PS_HASH_FUNC_OTHER:
00448                                           PS(hash_ops)->hash_update(hash_context, rbuf, n);
00449                                           break;
00450 #endif /* HAVE_HASH_EXT */
00451                             }
00452                             to_read -= n;
00453                      }
00454                      close(fd);
00455               }
00456 #endif
00457        }
00458 
00459        digest = emalloc(digest_len + 1);
00460        switch (PS(hash_func)) {
00461               case PS_HASH_FUNC_MD5:
00462                      PHP_MD5Final(digest, &md5_context);
00463                      break;
00464               case PS_HASH_FUNC_SHA1:
00465                      PHP_SHA1Final(digest, &sha1_context);
00466                      break;
00467 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00468               case PS_HASH_FUNC_OTHER:
00469                      PS(hash_ops)->hash_final(digest, hash_context);
00470                      efree(hash_context);
00471                      break;
00472 #endif /* HAVE_HASH_EXT */
00473        }
00474 
00475        if (PS(hash_bits_per_character) < 4
00476                      || PS(hash_bits_per_character) > 6) {
00477               PS(hash_bits_per_character) = 4;
00478 
00479               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now");
00480        }
00481        
00482        outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5)));
00483        j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid);
00484        efree(digest);
00485 
00486        if (newlen) {
00487               *newlen = j;
00488        }
00489 
00490        return outid;
00491 }
00492 /* }}} */
00493 
00494 static void php_session_initialize(TSRMLS_D) /* {{{ */
00495 {
00496        char *val;
00497        int vallen;
00498 
00499        /* check session name for invalid characters */
00500        if (PS(id) && strpbrk(PS(id), "\r\n\t <>'\"\\")) {
00501               efree(PS(id));
00502               PS(id) = NULL;
00503        }
00504 
00505        if (!PS(mod)) {
00506               php_error_docref(NULL TSRMLS_CC, E_ERROR, "No storage module chosen - failed to initialize session");
00507               return;
00508        }
00509 
00510        /* Open session handler first */
00511        if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name) TSRMLS_CC) == FAILURE) {
00512               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
00513               return;
00514        }
00515 
00516        /* If there is no ID, use session module to create one */
00517        if (!PS(id)) {
00518 new_session:
00519               PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC);
00520               if (PS(use_cookies)) {
00521                      PS(send_cookie) = 1;
00522               }
00523        }
00524 
00525        /* Read data */
00526        /* Question: if you create a SID here, should you also try to read data?
00527         * I'm not sure, but while not doing so will remove one session operation
00528         * it could prove usefull for those sites which wish to have "default"
00529         * session information. */
00530        php_session_track_init(TSRMLS_C);
00531        PS(invalid_session_id) = 0;
00532        if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, &vallen TSRMLS_CC) == SUCCESS) {
00533               php_session_decode(val, vallen TSRMLS_CC);
00534               efree(val);
00535        } else if (PS(invalid_session_id)) { /* address instances where the session read fails due to an invalid id */
00536               PS(invalid_session_id) = 0;
00537               efree(PS(id));
00538               PS(id) = NULL;
00539               goto new_session;
00540        }
00541 }
00542 /* }}} */
00543 
00544 static int migrate_global(HashTable *ht, HashPosition *pos TSRMLS_DC) /* {{{ */
00545 {
00546        char *str;
00547        uint str_len;
00548        ulong num_key;
00549        int n;
00550        zval **val;
00551        int ret = 0;
00552 
00553        n = zend_hash_get_current_key_ex(ht, &str, &str_len, &num_key, 0, pos);
00554 
00555        switch (n) {
00556               case HASH_KEY_IS_STRING:
00557                      if (zend_hash_find(&EG(symbol_table), str, str_len, (void **) &val) == SUCCESS &&
00558                             val && Z_TYPE_PP(val) != IS_NULL
00559                      ) {
00560                             ZEND_SET_SYMBOL_WITH_LENGTH(ht, str, str_len, *val, Z_REFCOUNT_PP(val) + 1, 1);
00561                             ret = 1;
00562                      }
00563                      break;
00564               case HASH_KEY_IS_LONG:
00565                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "The session bug compatibility code will not "
00566                                    "try to locate the global variable $%lu due to its "
00567                                    "numeric nature", num_key);
00568                      break;
00569        }
00570        return ret;
00571 }
00572 /* }}} */
00573 
00574 static void php_session_save_current_state(TSRMLS_D) /* {{{ */
00575 {
00576        int ret = FAILURE;
00577 
00578        IF_SESSION_VARS() {
00579               if (PS(bug_compat) && !PG(register_globals)) {
00580                      HashTable *ht = Z_ARRVAL_P(PS(http_session_vars));
00581                      HashPosition pos;
00582                      zval **val;
00583                      int do_warn = 0;
00584 
00585                      zend_hash_internal_pointer_reset_ex(ht, &pos);
00586 
00587                      while (zend_hash_get_current_data_ex(ht, (void **) &val, &pos) != FAILURE) {
00588                             if (Z_TYPE_PP(val) == IS_NULL) {
00589                                    if (migrate_global(ht, &pos TSRMLS_CC)) {
00590                                           do_warn = 1;
00591                                    }
00592                             }
00593                             zend_hash_move_forward_ex(ht, &pos);
00594                      }
00595 
00596                      if (do_warn && PS(bug_compat_warn)) {
00597                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively");
00598                      }
00599               }
00600 
00601               if (PS(mod_data)) {
00602                      char *val;
00603                      int vallen;
00604 
00605                      val = php_session_encode(&vallen TSRMLS_CC);
00606                      if (val) {
00607                             ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, vallen TSRMLS_CC);
00608                             efree(val);
00609                      } else {
00610                             ret = PS(mod)->s_write(&PS(mod_data), PS(id), "", 0 TSRMLS_CC);
00611                      }
00612               }
00613 
00614               if (ret == FAILURE) {
00615                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write session data (%s). Please "
00616                                    "verify that the current setting of session.save_path "
00617                                    "is correct (%s)",
00618                                    PS(mod)->s_name,
00619                                    PS(save_path));
00620               }
00621        }
00622 
00623        if (PS(mod_data)) {
00624               PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
00625        }
00626 }
00627 /* }}} */
00628 
00629 /* *************************
00630    * INI Settings/Handlers *
00631    ************************* */
00632 
00633 static PHP_INI_MH(OnUpdateSaveHandler) /* {{{ */
00634 {
00635        ps_module *tmp;
00636        SESSION_CHECK_ACTIVE_STATE;
00637 
00638        tmp = _php_find_ps_module(new_value TSRMLS_CC);
00639 
00640        if (PG(modules_activated) && !tmp) {
00641               int err_type;
00642 
00643               if (stage == ZEND_INI_STAGE_RUNTIME) {
00644                      err_type = E_WARNING;
00645               } else {
00646                      err_type = E_ERROR;
00647               }
00648 
00649               /* Do not output error when restoring ini options. */
00650               if (stage != ZEND_INI_STAGE_DEACTIVATE) {
00651                      php_error_docref(NULL TSRMLS_CC, err_type, "Cannot find save handler '%s'", new_value);
00652               }
00653               return FAILURE;
00654        }
00655        PS(mod) = tmp;
00656 
00657        return SUCCESS;
00658 }
00659 /* }}} */
00660 
00661 static PHP_INI_MH(OnUpdateSerializer) /* {{{ */
00662 {
00663        const ps_serializer *tmp;
00664        SESSION_CHECK_ACTIVE_STATE;
00665 
00666        tmp = _php_find_ps_serializer(new_value TSRMLS_CC);
00667 
00668        if (PG(modules_activated) && !tmp) {
00669               int err_type;
00670 
00671               if (stage == ZEND_INI_STAGE_RUNTIME) {
00672                      err_type = E_WARNING;
00673               } else {
00674                      err_type = E_ERROR;
00675               }
00676 
00677               /* Do not output error when restoring ini options. */
00678               if (stage != ZEND_INI_STAGE_DEACTIVATE) {
00679                      php_error_docref(NULL TSRMLS_CC, err_type, "Cannot find serialization handler '%s'", new_value);
00680               }
00681               return FAILURE;
00682        }
00683        PS(serializer) = tmp;
00684 
00685        return SUCCESS;
00686 }
00687 /* }}} */
00688 
00689 static PHP_INI_MH(OnUpdateTransSid) /* {{{ */
00690 {
00691        SESSION_CHECK_ACTIVE_STATE;
00692 
00693        if (!strncasecmp(new_value, "on", sizeof("on"))) {
00694               PS(use_trans_sid) = (zend_bool) 1;
00695        } else {
00696               PS(use_trans_sid) = (zend_bool) atoi(new_value);
00697        }
00698 
00699        return SUCCESS;
00700 }
00701 /* }}} */
00702 
00703 static PHP_INI_MH(OnUpdateSaveDir) /* {{{ */
00704 {
00705        /* Only do the safemode/open_basedir check at runtime */
00706        if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) {
00707               char *p;
00708 
00709               if (memchr(new_value, '\0', new_value_length) != NULL) {
00710                      return FAILURE;
00711               }
00712 
00713               /* we do not use zend_memrchr() since path can contain ; itself */
00714               if ((p = strchr(new_value, ';'))) {
00715                      char *p2;
00716                      p++;
00717                      if ((p2 = strchr(p, ';'))) {
00718                             p = p2 + 1;
00719                      }
00720               } else {
00721                      p = new_value;
00722               }
00723 
00724               if (PG(safe_mode) && *p && (!php_checkuid(p, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
00725                      return FAILURE;
00726               }
00727 
00728               if (PG(open_basedir) && *p && php_check_open_basedir(p TSRMLS_CC)) {
00729                      return FAILURE;
00730               }
00731        }
00732 
00733        OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
00734        return SUCCESS;
00735 }
00736 /* }}} */
00737 
00738 static PHP_INI_MH(OnUpdateHashFunc) /* {{{ */
00739 {
00740        long val;
00741        char *endptr = NULL;
00742 
00743 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
00744        PS(hash_ops) = NULL;
00745 #endif
00746 
00747        val = strtol(new_value, &endptr, 10);
00748        if (endptr && (*endptr == '\0')) {
00749               /* Numeric value */
00750               PS(hash_func) = val ? 1 : 0;
00751 
00752               return SUCCESS;
00753        }
00754 
00755        if (new_value_length == (sizeof("md5") - 1) &&
00756               strncasecmp(new_value, "md5", sizeof("md5") - 1) == 0) {
00757               PS(hash_func) = PS_HASH_FUNC_MD5;
00758 
00759               return SUCCESS;
00760        }
00761 
00762        if (new_value_length == (sizeof("sha1") - 1) &&
00763               strncasecmp(new_value, "sha1", sizeof("sha1") - 1) == 0) {
00764               PS(hash_func) = PS_HASH_FUNC_SHA1;
00765 
00766               return SUCCESS;
00767        }
00768 
00769 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) /* {{{ */
00770 {
00771        php_hash_ops *ops = (php_hash_ops*)php_hash_fetch_ops(new_value, new_value_length);
00772 
00773        if (ops) {
00774               PS(hash_func) = PS_HASH_FUNC_OTHER;
00775               PS(hash_ops) = ops;
00776 
00777               return SUCCESS;
00778        }
00779 }
00780 #endif /* HAVE_HASH_EXT }}} */
00781 
00782        return FAILURE;
00783 }
00784 /* }}} */
00785 
00786 /* {{{ PHP_INI
00787  */
00788 PHP_INI_BEGIN()
00789        STD_PHP_INI_BOOLEAN("session.bug_compat_42",    "1",         PHP_INI_ALL, OnUpdateBool,   bug_compat,         php_ps_globals,    ps_globals)
00790        STD_PHP_INI_BOOLEAN("session.bug_compat_warn",  "1",         PHP_INI_ALL, OnUpdateBool,   bug_compat_warn,    php_ps_globals,    ps_globals)
00791        STD_PHP_INI_ENTRY("session.save_path",          "",          PHP_INI_ALL, OnUpdateSaveDir,save_path,          php_ps_globals,    ps_globals)
00792        STD_PHP_INI_ENTRY("session.name",               "PHPSESSID", PHP_INI_ALL, OnUpdateString, session_name,       php_ps_globals,    ps_globals)
00793        PHP_INI_ENTRY("session.save_handler",           "files",     PHP_INI_ALL, OnUpdateSaveHandler)
00794        STD_PHP_INI_BOOLEAN("session.auto_start",       "0",         PHP_INI_ALL, OnUpdateBool,   auto_start,         php_ps_globals,    ps_globals)
00795        STD_PHP_INI_ENTRY("session.gc_probability",     "1",         PHP_INI_ALL, OnUpdateLong,   gc_probability,     php_ps_globals,    ps_globals)
00796        STD_PHP_INI_ENTRY("session.gc_divisor",         "100",       PHP_INI_ALL, OnUpdateLong,   gc_divisor,         php_ps_globals,    ps_globals)
00797        STD_PHP_INI_ENTRY("session.gc_maxlifetime",     "1440",      PHP_INI_ALL, OnUpdateLong,   gc_maxlifetime,     php_ps_globals,    ps_globals)
00798        PHP_INI_ENTRY("session.serialize_handler",      "php",       PHP_INI_ALL, OnUpdateSerializer)
00799        STD_PHP_INI_ENTRY("session.cookie_lifetime",    "0",         PHP_INI_ALL, OnUpdateLong,   cookie_lifetime,    php_ps_globals,    ps_globals)
00800        STD_PHP_INI_ENTRY("session.cookie_path",        "/",         PHP_INI_ALL, OnUpdateString, cookie_path,        php_ps_globals,    ps_globals)
00801        STD_PHP_INI_ENTRY("session.cookie_domain",      "",          PHP_INI_ALL, OnUpdateString, cookie_domain,      php_ps_globals,    ps_globals)
00802        STD_PHP_INI_BOOLEAN("session.cookie_secure",    "",          PHP_INI_ALL, OnUpdateBool,   cookie_secure,      php_ps_globals,    ps_globals)
00803        STD_PHP_INI_BOOLEAN("session.cookie_httponly",  "",          PHP_INI_ALL, OnUpdateBool,   cookie_httponly,    php_ps_globals,    ps_globals)
00804        STD_PHP_INI_BOOLEAN("session.use_cookies",      "1",         PHP_INI_ALL, OnUpdateBool,   use_cookies,        php_ps_globals,    ps_globals)
00805        STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1",         PHP_INI_ALL, OnUpdateBool,   use_only_cookies,   php_ps_globals,    ps_globals)
00806        STD_PHP_INI_ENTRY("session.referer_check",      "",          PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals,    ps_globals)
00807        STD_PHP_INI_ENTRY("session.entropy_file",       "",          PHP_INI_ALL, OnUpdateString, entropy_file,       php_ps_globals,    ps_globals)
00808        STD_PHP_INI_ENTRY("session.entropy_length",     "0",         PHP_INI_ALL, OnUpdateLong,   entropy_length,     php_ps_globals,    ps_globals)
00809        STD_PHP_INI_ENTRY("session.cache_limiter",      "nocache",   PHP_INI_ALL, OnUpdateString, cache_limiter,      php_ps_globals,    ps_globals)
00810        STD_PHP_INI_ENTRY("session.cache_expire",       "180",       PHP_INI_ALL, OnUpdateLong,   cache_expire,       php_ps_globals,    ps_globals)
00811        PHP_INI_ENTRY("session.use_trans_sid",          "0",         PHP_INI_ALL, OnUpdateTransSid)
00812        PHP_INI_ENTRY("session.hash_function",          "0",         PHP_INI_ALL, OnUpdateHashFunc)
00813        STD_PHP_INI_ENTRY("session.hash_bits_per_character", "4",    PHP_INI_ALL, OnUpdateLong,   hash_bits_per_character, php_ps_globals, ps_globals)
00814 
00815        /* Commented out until future discussion */
00816        /* PHP_INI_ENTRY("session.encode_sources", "globals,track", PHP_INI_ALL, NULL) */
00817 PHP_INI_END()
00818 /* }}} */
00819 
00820 /* ***************
00821    * Serializers *
00822    *************** */
00823 
00824 #define PS_BIN_NR_OF_BITS 8
00825 #define PS_BIN_UNDEF (1<<(PS_BIN_NR_OF_BITS-1))
00826 #define PS_BIN_MAX (PS_BIN_UNDEF-1)
00827 
00828 PS_SERIALIZER_ENCODE_FUNC(php_binary) /* {{{ */
00829 {
00830        smart_str buf = {0};
00831        php_serialize_data_t var_hash;
00832        PS_ENCODE_VARS;
00833 
00834        PHP_VAR_SERIALIZE_INIT(var_hash);
00835 
00836        PS_ENCODE_LOOP(
00837                      if (key_length > PS_BIN_MAX) continue;
00838                      smart_str_appendc(&buf, (unsigned char) key_length);
00839                      smart_str_appendl(&buf, key, key_length);
00840                      php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
00841               } else {
00842                      if (key_length > PS_BIN_MAX) continue;
00843                      smart_str_appendc(&buf, (unsigned char) (key_length & PS_BIN_UNDEF));
00844                      smart_str_appendl(&buf, key, key_length);
00845        );
00846 
00847        if (newlen) {
00848               *newlen = buf.len;
00849        }
00850        smart_str_0(&buf);
00851        *newstr = buf.c;
00852        PHP_VAR_SERIALIZE_DESTROY(var_hash);
00853 
00854        return SUCCESS;
00855 }
00856 /* }}} */
00857 
00858 PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
00859 {
00860        const char *p;
00861        char *name;
00862        const char *endptr = val + vallen;
00863        zval *current;
00864        int namelen;
00865        int has_value;
00866        php_unserialize_data_t var_hash;
00867 
00868        PHP_VAR_UNSERIALIZE_INIT(var_hash);
00869 
00870        for (p = val; p < endptr; ) {
00871               zval **tmp;
00872               namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);
00873 
00874               if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
00875                      return FAILURE;
00876               }
00877 
00878               has_value = *p & PS_BIN_UNDEF ? 0 : 1;
00879 
00880               name = estrndup(p + 1, namelen);
00881 
00882               p += namelen + 1;
00883 
00884               if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
00885                      if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {
00886                             efree(name);
00887                             continue;
00888                      }
00889               }
00890 
00891               if (has_value) {
00892                      ALLOC_INIT_ZVAL(current);
00893                      if (php_var_unserialize(&current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
00894                             php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
00895                      }
00896                      zval_ptr_dtor(&current);
00897               }
00898               PS_ADD_VARL(name, namelen);
00899               efree(name);
00900        }
00901 
00902        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
00903 
00904        return SUCCESS;
00905 }
00906 /* }}} */
00907 
00908 #define PS_DELIMITER '|'
00909 #define PS_UNDEF_MARKER '!'
00910 
00911 PS_SERIALIZER_ENCODE_FUNC(php) /* {{{ */
00912 {
00913        smart_str buf = {0};
00914        php_serialize_data_t var_hash;
00915        PS_ENCODE_VARS;
00916 
00917        PHP_VAR_SERIALIZE_INIT(var_hash);
00918 
00919        PS_ENCODE_LOOP(
00920                      smart_str_appendl(&buf, key, key_length);
00921                      if (memchr(key, PS_DELIMITER, key_length) || memchr(key, PS_UNDEF_MARKER, key_length)) {
00922                             PHP_VAR_SERIALIZE_DESTROY(var_hash);
00923                             smart_str_free(&buf);
00924                             return FAILURE;
00925                      }
00926                      smart_str_appendc(&buf, PS_DELIMITER);
00927 
00928                      php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
00929               } else {
00930                      smart_str_appendc(&buf, PS_UNDEF_MARKER);
00931                      smart_str_appendl(&buf, key, key_length);
00932                      smart_str_appendc(&buf, PS_DELIMITER);
00933        );
00934 
00935        if (newlen) {
00936               *newlen = buf.len;
00937        }
00938        smart_str_0(&buf);
00939        *newstr = buf.c;
00940 
00941        PHP_VAR_SERIALIZE_DESTROY(var_hash);
00942        return SUCCESS;
00943 }
00944 /* }}} */
00945 
00946 PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
00947 {
00948        const char *p, *q;
00949        char *name;
00950        const char *endptr = val + vallen;
00951        zval *current;
00952        int namelen;
00953        int has_value;
00954        php_unserialize_data_t var_hash;
00955 
00956        PHP_VAR_UNSERIALIZE_INIT(var_hash);
00957 
00958        p = val;
00959 
00960        while (p < endptr) {
00961               zval **tmp;
00962               q = p;
00963               while (*q != PS_DELIMITER) {
00964                      if (++q >= endptr) goto break_outer_loop;
00965               }
00966               if (p[0] == PS_UNDEF_MARKER) {
00967                      p++;
00968                      has_value = 0;
00969               } else {
00970                      has_value = 1;
00971               }
00972 
00973               namelen = q - p;
00974               name = estrndup(p, namelen);
00975               q++;
00976 
00977               if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
00978                      if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {
00979                             goto skip;
00980                      }
00981               }
00982 
00983               if (has_value) {
00984                      ALLOC_INIT_ZVAL(current);
00985                      if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
00986                             php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
00987                      }
00988                      zval_ptr_dtor(&current);
00989               }
00990               PS_ADD_VARL(name, namelen);
00991 skip:
00992               efree(name);
00993 
00994               p = q;
00995        }
00996 break_outer_loop:
00997 
00998        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
00999 
01000        return SUCCESS;
01001 }
01002 /* }}} */
01003 
01004 #define MAX_SERIALIZERS 10
01005 #define PREDEFINED_SERIALIZERS 2
01006 
01007 static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = {
01008        PS_SERIALIZER_ENTRY(php),
01009        PS_SERIALIZER_ENTRY(php_binary)
01010 };
01011 
01012 PHPAPI int php_session_register_serializer(const char *name, int (*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */
01013 {
01014        int ret = -1;
01015        int i;
01016 
01017        for (i = 0; i < MAX_SERIALIZERS; i++) {
01018               if (ps_serializers[i].name == NULL) {
01019                      ps_serializers[i].name = name;
01020                      ps_serializers[i].encode = encode;
01021                      ps_serializers[i].decode = decode;
01022                      ps_serializers[i + 1].name = NULL;
01023                      ret = 0;
01024                      break;
01025               }
01026        }
01027        return ret;
01028 }
01029 /* }}} */
01030 
01031 /* *******************
01032    * Storage Modules *
01033    ******************* */
01034 
01035 #define MAX_MODULES 10
01036 #define PREDEFINED_MODULES 2
01037 
01038 static ps_module *ps_modules[MAX_MODULES + 1] = {
01039        ps_files_ptr,
01040        ps_user_ptr
01041 };
01042 
01043 PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
01044 {
01045        int ret = -1;
01046        int i;
01047 
01048        for (i = 0; i < MAX_MODULES; i++) {
01049               if (!ps_modules[i]) {
01050                      ps_modules[i] = ptr;
01051                      ret = 0;
01052                      break;
01053               }
01054        }
01055        return ret;
01056 }
01057 /* }}} */
01058 
01059 /* ******************
01060    * Cache Limiters *
01061    ****************** */
01062 
01063 typedef struct {
01064        char *name;
01065        void (*func)(TSRMLS_D);
01066 } php_session_cache_limiter_t;
01067 
01068 #define CACHE_LIMITER(name) _php_cache_limiter_##name
01069 #define CACHE_LIMITER_FUNC(name) static void CACHE_LIMITER(name)(TSRMLS_D)
01070 #define CACHE_LIMITER_ENTRY(name) { #name, CACHE_LIMITER(name) },
01071 #define ADD_HEADER(a) sapi_add_header(a, strlen(a), 1);
01072 #define MAX_STR 512
01073 
01074 static char *month_names[] = {
01075        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
01076        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
01077 };
01078 
01079 static char *week_days[] = {
01080        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
01081 };
01082 
01083 static inline void strcpy_gmt(char *ubuf, time_t *when) /* {{{ */
01084 {
01085        char buf[MAX_STR];
01086        struct tm tm, *res;
01087        int n;
01088 
01089        res = php_gmtime_r(when, &tm);
01090 
01091        if (!res) {
01092               buf[0] = '\0';
01093               return;
01094        }
01095 
01096        n = slprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT", /* SAFE */
01097                             week_days[tm.tm_wday], tm.tm_mday,
01098                             month_names[tm.tm_mon], tm.tm_year + 1900,
01099                             tm.tm_hour, tm.tm_min,
01100                             tm.tm_sec);
01101        memcpy(ubuf, buf, n);
01102        ubuf[n] = '\0';
01103 }
01104 /* }}} */
01105 
01106 static inline void last_modified(TSRMLS_D) /* {{{ */
01107 {
01108        const char *path;
01109        struct stat sb;
01110        char buf[MAX_STR + 1];
01111 
01112        path = SG(request_info).path_translated;
01113        if (path) {
01114               if (VCWD_STAT(path, &sb) == -1) {
01115                      return;
01116               }
01117 
01118 #define LAST_MODIFIED "Last-Modified: "
01119               memcpy(buf, LAST_MODIFIED, sizeof(LAST_MODIFIED) - 1);
01120               strcpy_gmt(buf + sizeof(LAST_MODIFIED) - 1, &sb.st_mtime);
01121               ADD_HEADER(buf);
01122        }
01123 }
01124 /* }}} */
01125 
01126 #define EXPIRES "Expires: "
01127 CACHE_LIMITER_FUNC(public) /* {{{ */
01128 {
01129        char buf[MAX_STR + 1];
01130        struct timeval tv;
01131        time_t now;
01132 
01133        gettimeofday(&tv, NULL);
01134        now = tv.tv_sec + PS(cache_expire) * 60;
01135        memcpy(buf, EXPIRES, sizeof(EXPIRES) - 1);
01136        strcpy_gmt(buf + sizeof(EXPIRES) - 1, &now);
01137        ADD_HEADER(buf);
01138 
01139        snprintf(buf, sizeof(buf) , "Cache-Control: public, max-age=%ld", PS(cache_expire) * 60); /* SAFE */
01140        ADD_HEADER(buf);
01141 
01142        last_modified(TSRMLS_C);
01143 }
01144 /* }}} */
01145 
01146 CACHE_LIMITER_FUNC(private_no_expire) /* {{{ */
01147 {
01148        char buf[MAX_STR + 1];
01149 
01150        snprintf(buf, sizeof(buf), "Cache-Control: private, max-age=%ld, pre-check=%ld", PS(cache_expire) * 60, PS(cache_expire) * 60); /* SAFE */
01151        ADD_HEADER(buf);
01152 
01153        last_modified(TSRMLS_C);
01154 }
01155 /* }}} */
01156 
01157 CACHE_LIMITER_FUNC(private) /* {{{ */
01158 {
01159        ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
01160        CACHE_LIMITER(private_no_expire)(TSRMLS_C);
01161 }
01162 /* }}} */
01163 
01164 CACHE_LIMITER_FUNC(nocache) /* {{{ */
01165 {
01166        ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
01167 
01168        /* For HTTP/1.1 conforming clients and the rest (MSIE 5) */
01169        ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
01170 
01171        /* For HTTP/1.0 conforming clients */
01172        ADD_HEADER("Pragma: no-cache");
01173 }
01174 /* }}} */
01175 
01176 static php_session_cache_limiter_t php_session_cache_limiters[] = {
01177        CACHE_LIMITER_ENTRY(public)
01178        CACHE_LIMITER_ENTRY(private)
01179        CACHE_LIMITER_ENTRY(private_no_expire)
01180        CACHE_LIMITER_ENTRY(nocache)
01181        {0}
01182 };
01183 
01184 static int php_session_cache_limiter(TSRMLS_D) /* {{{ */
01185 {
01186        php_session_cache_limiter_t *lim;
01187 
01188        if (PS(cache_limiter)[0] == '\0') return 0;
01189 
01190        if (SG(headers_sent)) {
01191               char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
01192               int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
01193 
01194               if (output_start_filename) {
01195                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
01196               } else {
01197                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent");
01198               }
01199               return -2;
01200        }
01201 
01202        for (lim = php_session_cache_limiters; lim->name; lim++) {
01203               if (!strcasecmp(lim->name, PS(cache_limiter))) {
01204                      lim->func(TSRMLS_C);
01205                      return 0;
01206               }
01207        }
01208 
01209        return -1;
01210 }
01211 /* }}} */
01212 
01213 /* *********************
01214    * Cookie Management *
01215    ********************* */
01216 
01217 #define COOKIE_SET_COOKIE "Set-Cookie: "
01218 #define COOKIE_EXPIRES      "; expires="
01219 #define COOKIE_PATH         "; path="
01220 #define COOKIE_DOMAIN       "; domain="
01221 #define COOKIE_SECURE       "; secure"
01222 #define COOKIE_HTTPONLY     "; HttpOnly"
01223 
01224 static void php_session_send_cookie(TSRMLS_D) /* {{{ */
01225 {
01226        smart_str ncookie = {0};
01227        char *date_fmt = NULL;
01228        char *e_session_name, *e_id;
01229 
01230        if (SG(headers_sent)) {
01231               char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
01232               int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
01233 
01234               if (output_start_filename) {
01235                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cookie - headers already sent by (output started at %s:%d)", output_start_filename, output_start_lineno);
01236               } else {
01237                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cookie - headers already sent");
01238               }
01239               return;
01240        }
01241 
01242        /* URL encode session_name and id because they might be user supplied */
01243        e_session_name = php_url_encode(PS(session_name), strlen(PS(session_name)), NULL);
01244        e_id = php_url_encode(PS(id), strlen(PS(id)), NULL);
01245 
01246        smart_str_appends(&ncookie, COOKIE_SET_COOKIE);
01247        smart_str_appends(&ncookie, e_session_name);
01248        smart_str_appendc(&ncookie, '=');
01249        smart_str_appends(&ncookie, e_id);
01250 
01251        efree(e_session_name);
01252        efree(e_id);
01253 
01254        if (PS(cookie_lifetime) > 0) {
01255               struct timeval tv;
01256               time_t t;
01257 
01258               gettimeofday(&tv, NULL);
01259               t = tv.tv_sec + PS(cookie_lifetime);
01260 
01261               if (t > 0) {
01262                      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 TSRMLS_CC);
01263                      smart_str_appends(&ncookie, COOKIE_EXPIRES);
01264                      smart_str_appends(&ncookie, date_fmt);
01265                      efree(date_fmt);
01266               }
01267        }
01268 
01269        if (PS(cookie_path)[0]) {
01270               smart_str_appends(&ncookie, COOKIE_PATH);
01271               smart_str_appends(&ncookie, PS(cookie_path));
01272        }
01273 
01274        if (PS(cookie_domain)[0]) {
01275               smart_str_appends(&ncookie, COOKIE_DOMAIN);
01276               smart_str_appends(&ncookie, PS(cookie_domain));
01277        }
01278 
01279        if (PS(cookie_secure)) {
01280               smart_str_appends(&ncookie, COOKIE_SECURE);
01281        }
01282 
01283        if (PS(cookie_httponly)) {
01284               smart_str_appends(&ncookie, COOKIE_HTTPONLY);
01285        }
01286 
01287        smart_str_0(&ncookie);
01288 
01289        /*     'replace' must be 0 here, else a previous Set-Cookie
01290               header, probably sent with setcookie() will be replaced! */
01291        sapi_add_header_ex(ncookie.c, ncookie.len, 0, 0 TSRMLS_CC);
01292 }
01293 /* }}} */
01294 
01295 PHPAPI ps_module *_php_find_ps_module(char *name TSRMLS_DC) /* {{{ */
01296 {
01297        ps_module *ret = NULL;
01298        ps_module **mod;
01299        int i;
01300 
01301        for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
01302               if (*mod && !strcasecmp(name, (*mod)->s_name)) {
01303                      ret = *mod;
01304                      break;
01305               }
01306        }
01307        return ret;
01308 }
01309 /* }}} */
01310 
01311 PHPAPI const ps_serializer *_php_find_ps_serializer(char *name TSRMLS_DC) /* {{{ */
01312 {
01313        const ps_serializer *ret = NULL;
01314        const ps_serializer *mod;
01315 
01316        for (mod = ps_serializers; mod->name; mod++) {
01317               if (!strcasecmp(name, mod->name)) {
01318                      ret = mod;
01319                      break;
01320               }
01321        }
01322        return ret;
01323 }
01324 /* }}} */
01325 
01326 #define PPID2SID \
01327               convert_to_string((*ppid)); \
01328               PS(id) = estrndup(Z_STRVAL_PP(ppid), Z_STRLEN_PP(ppid))
01329 
01330 static void php_session_reset_id(TSRMLS_D) /* {{{ */
01331 {
01332        int module_number = PS(module_number);
01333 
01334        if (PS(use_cookies) && PS(send_cookie)) {
01335               php_session_send_cookie(TSRMLS_C);
01336               PS(send_cookie) = 0;
01337        }
01338 
01339        /* if the SID constant exists, destroy it. */
01340        zend_hash_del(EG(zend_constants), "sid", sizeof("sid"));
01341 
01342        if (PS(define_sid)) {
01343               smart_str var = {0};
01344 
01345               smart_str_appends(&var, PS(session_name));
01346               smart_str_appendc(&var, '=');
01347               smart_str_appends(&var, PS(id));
01348               smart_str_0(&var);
01349               REGISTER_STRINGL_CONSTANT("SID", var.c, var.len, 0);
01350        } else {
01351               REGISTER_STRINGL_CONSTANT("SID", STR_EMPTY_ALLOC(), 0, 0);
01352        }
01353 
01354        if (PS(apply_trans_sid)) {
01355               php_url_scanner_reset_vars(TSRMLS_C);
01356               php_url_scanner_add_var(PS(session_name), strlen(PS(session_name)), PS(id), strlen(PS(id)), 1 TSRMLS_CC);
01357        }
01358 }
01359 /* }}} */
01360 
01361 PHPAPI void php_session_start(TSRMLS_D) /* {{{ */
01362 {
01363        zval **ppid;
01364        zval **data;
01365        char *p, *value;
01366        int nrand;
01367        int lensess;
01368 
01369        if (PS(use_only_cookies)) {
01370               PS(apply_trans_sid) = 0;
01371        } else {
01372               PS(apply_trans_sid) = PS(use_trans_sid);
01373        }
01374 
01375        switch (PS(session_status)) {
01376               case php_session_active:
01377                      php_error(E_NOTICE, "A session had already been started - ignoring session_start()");
01378                      return;
01379                      break;
01380 
01381               case php_session_disabled:
01382                      value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0);
01383                      if (!PS(mod) && value) {
01384                             PS(mod) = _php_find_ps_module(value TSRMLS_CC);
01385                             if (!PS(mod)) {
01386                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find save handler '%s' - session startup failed", value);
01387                                    return;
01388                             }
01389                      }
01390                      value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
01391                      if (!PS(serializer) && value) {
01392                             PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
01393                             if (!PS(serializer)) {
01394                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find serialization handler '%s' - session startup failed", value);
01395                                    return;
01396                             }
01397                      }
01398                      PS(session_status) = php_session_none;
01399                      /* fallthrough */
01400 
01401               default:
01402               case php_session_none:
01403                      PS(define_sid) = 1;
01404                      PS(send_cookie) = 1;
01405        }
01406 
01407        lensess = strlen(PS(session_name));
01408 
01409        /* Cookies are preferred, because initially
01410         * cookie and get variables will be available. */
01411 
01412        if (!PS(id)) {
01413               if (PS(use_cookies) && zend_hash_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE"), (void **) &data) == SUCCESS &&
01414                             Z_TYPE_PP(data) == IS_ARRAY &&
01415                             zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
01416               ) {
01417                      PPID2SID;
01418                      PS(apply_trans_sid) = 0;
01419                      PS(send_cookie) = 0;
01420                      PS(define_sid) = 0;
01421               }
01422 
01423               if (!PS(use_only_cookies) && !PS(id) &&
01424                             zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void **) &data) == SUCCESS &&
01425                             Z_TYPE_PP(data) == IS_ARRAY &&
01426                             zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
01427               ) {
01428                      PPID2SID;
01429                      PS(send_cookie) = 0;
01430               }
01431 
01432               if (!PS(use_only_cookies) && !PS(id) &&
01433                             zend_hash_find(&EG(symbol_table), "_POST", sizeof("_POST"), (void **) &data) == SUCCESS &&
01434                             Z_TYPE_PP(data) == IS_ARRAY &&
01435                             zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
01436               ) {
01437                      PPID2SID;
01438                      PS(send_cookie) = 0;
01439               }
01440        }
01441 
01442        /* Check the REQUEST_URI symbol for a string of the form
01443         * '<session-name>=<session-id>' to allow URLs of the form
01444         * http://yoursite/<session-name>=<session-id>/script.php */
01445 
01446        if (!PS(use_only_cookies) && !PS(id) && PG(http_globals)[TRACK_VARS_SERVER] &&
01447                      zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &data) == SUCCESS &&
01448                      Z_TYPE_PP(data) == IS_STRING &&
01449                      (p = strstr(Z_STRVAL_PP(data), PS(session_name))) &&
01450                      p[lensess] == '='
01451        ) {
01452               char *q;
01453 
01454               p += lensess + 1;
01455               if ((q = strpbrk(p, "/?\\"))) {
01456                      PS(id) = estrndup(p, q - p);
01457                      PS(send_cookie) = 0;
01458               }
01459        }
01460 
01461        /* Check whether the current request was referred to by
01462         * an external site which invalidates the previously found id. */
01463 
01464        if (PS(id) &&
01465                      PS(extern_referer_chk)[0] != '\0' &&
01466                      PG(http_globals)[TRACK_VARS_SERVER] &&
01467                      zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER"), (void **) &data) == SUCCESS &&
01468                      Z_TYPE_PP(data) == IS_STRING &&
01469                      Z_STRLEN_PP(data) != 0 &&
01470                      strstr(Z_STRVAL_PP(data), PS(extern_referer_chk)) == NULL
01471        ) {
01472               efree(PS(id));
01473               PS(id) = NULL;
01474               PS(send_cookie) = 1;
01475               if (PS(use_trans_sid) && !PS(use_only_cookies)) {
01476                      PS(apply_trans_sid) = 1;
01477               }
01478        }
01479 
01480        php_session_initialize(TSRMLS_C);
01481 
01482        if (!PS(use_cookies) && PS(send_cookie)) {
01483               if (PS(use_trans_sid) && !PS(use_only_cookies)) {
01484                      PS(apply_trans_sid) = 1;
01485               }
01486               PS(send_cookie) = 0;
01487        }
01488 
01489        php_session_reset_id(TSRMLS_C);
01490 
01491        PS(session_status) = php_session_active;
01492 
01493        php_session_cache_limiter(TSRMLS_C);
01494 
01495        if (PS(mod_data) && PS(gc_probability) > 0) {
01496               int nrdels = -1;
01497 
01498               nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
01499               if (nrand < PS(gc_probability)) {
01500                      PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC);
01501 #ifdef SESSION_DEBUG
01502                      if (nrdels != -1) {
01503                             php_error_docref(NULL TSRMLS_CC, E_NOTICE, "purged %d expired session objects", nrdels);
01504                      }
01505 #endif
01506               }
01507        }
01508 }
01509 /* }}} */
01510 
01511 static void php_session_flush(TSRMLS_D) /* {{{ */
01512 {
01513        if (PS(session_status) == php_session_active) {
01514               PS(session_status) = php_session_none;
01515               zend_try {
01516                      php_session_save_current_state(TSRMLS_C);
01517               } zend_end_try();
01518        }
01519 }
01520 /* }}} */
01521 
01522 PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t *newlen TSRMLS_DC) /* {{{ */
01523 {
01524        if (PS(apply_trans_sid) && (PS(session_status) == php_session_active)) {
01525               *new = php_url_scanner_adapt_single_url(url, urllen, PS(session_name), PS(id), newlen TSRMLS_CC);
01526        }
01527 }
01528 /* }}} */
01529 
01530 /* ********************************
01531    * Userspace exported functions *
01532    ******************************** */
01533 
01534 /* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])
01535    Set session cookie parameters */
01536 static PHP_FUNCTION(session_set_cookie_params)
01537 {
01538        zval **lifetime = NULL;
01539        char *path = NULL, *domain = NULL;
01540        int path_len, domain_len, argc = ZEND_NUM_ARGS();
01541        zend_bool secure = 0, httponly = 0;
01542 
01543        if (!PS(use_cookies) ||
01544               zend_parse_parameters(argc TSRMLS_CC, "Z|ssbb", &lifetime, &path, &path_len, &domain, &domain_len, &secure, &httponly) == FAILURE) {
01545               return;
01546        }
01547 
01548        convert_to_string_ex(lifetime);
01549 
01550        zend_alter_ini_entry("session.cookie_lifetime", sizeof("session.cookie_lifetime"), Z_STRVAL_PP(lifetime), Z_STRLEN_PP(lifetime), PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01551 
01552        if (path) {
01553               zend_alter_ini_entry("session.cookie_path", sizeof("session.cookie_path"), path, path_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01554        }
01555        if (domain) {
01556               zend_alter_ini_entry("session.cookie_domain", sizeof("session.cookie_domain"), domain, domain_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01557        }
01558 
01559        if (argc > 3) {
01560               zend_alter_ini_entry("session.cookie_secure", sizeof("session.cookie_secure"), secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01561        }
01562        if (argc > 4) {
01563               zend_alter_ini_entry("session.cookie_httponly", sizeof("session.cookie_httponly"), httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01564        }
01565 }
01566 /* }}} */
01567 
01568 /* {{{ proto array session_get_cookie_params(void)
01569    Return the session cookie parameters */
01570 static PHP_FUNCTION(session_get_cookie_params)
01571 {
01572        if (zend_parse_parameters_none() == FAILURE) {
01573               return;
01574        }
01575 
01576        array_init(return_value);
01577 
01578        add_assoc_long(return_value, "lifetime", PS(cookie_lifetime));
01579        add_assoc_string(return_value, "path", PS(cookie_path), 1);
01580        add_assoc_string(return_value, "domain", PS(cookie_domain), 1);
01581        add_assoc_bool(return_value, "secure", PS(cookie_secure));
01582        add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
01583 }
01584 /* }}} */
01585 
01586 /* {{{ proto string session_name([string newname])
01587    Return the current session name. If newname is given, the session name is replaced with newname */
01588 static PHP_FUNCTION(session_name)
01589 {
01590        char *name = NULL;
01591        int name_len;
01592 
01593        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
01594               return;
01595        }
01596 
01597        RETVAL_STRING(PS(session_name), 1);
01598 
01599        if (name) {
01600               zend_alter_ini_entry("session.name", sizeof("session.name"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01601        }
01602 }
01603 /* }}} */
01604 
01605 /* {{{ proto string session_module_name([string newname])
01606    Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname */
01607 static PHP_FUNCTION(session_module_name)
01608 {
01609        char *name = NULL;
01610        int name_len;
01611 
01612        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
01613               return;
01614        }
01615 
01616        /* Set return_value to current module name */
01617        if (PS(mod) && PS(mod)->s_name) {
01618               RETVAL_STRING(safe_estrdup(PS(mod)->s_name), 0);
01619        } else {
01620               RETVAL_EMPTY_STRING();
01621        }
01622 
01623        if (name) {
01624               if (!_php_find_ps_module(name TSRMLS_CC)) {
01625                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find named PHP session module (%s)", name);
01626 
01627                      zval_dtor(return_value);
01628                      RETURN_FALSE;
01629               }
01630               if (PS(mod_data)) {
01631                      PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
01632               }
01633               PS(mod_data) = NULL;
01634 
01635               zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01636        }
01637 }
01638 /* }}} */
01639 
01640 /* {{{ proto void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc)
01641    Sets user-level functions */
01642 static PHP_FUNCTION(session_set_save_handler)
01643 {
01644        zval ***args = NULL;
01645        int i, num_args, argc = ZEND_NUM_ARGS();
01646        char *name;
01647 
01648        if (PS(session_status) != php_session_none) {
01649               RETURN_FALSE;
01650        }
01651 
01652        if (argc != 6) {
01653               WRONG_PARAM_COUNT;
01654        }
01655 
01656        if (zend_parse_parameters(argc TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
01657               return;
01658        }
01659 
01660        for (i = 0; i < 6; i++) {
01661               if (!zend_is_callable(*args[i], 0, &name TSRMLS_CC)) {
01662                      efree(args);
01663                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument %d is not a valid callback", i+1);
01664                      efree(name);
01665                      RETURN_FALSE;
01666               }
01667               efree(name);
01668        }
01669 
01670        zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01671 
01672        for (i = 0; i < 6; i++) {
01673               if (PS(mod_user_names).names[i] != NULL) {
01674                      zval_ptr_dtor(&PS(mod_user_names).names[i]);
01675               }
01676               Z_ADDREF_PP(args[i]);
01677               PS(mod_user_names).names[i] = *args[i];
01678        }
01679 
01680        efree(args);
01681        RETURN_TRUE;
01682 }
01683 /* }}} */
01684 
01685 /* {{{ proto string session_save_path([string newname])
01686    Return the current save path passed to module_name. If newname is given, the save path is replaced with newname */
01687 static PHP_FUNCTION(session_save_path)
01688 {
01689        char *name = NULL;
01690        int name_len;
01691 
01692        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
01693               return;
01694        }
01695 
01696        RETVAL_STRING(PS(save_path), 1);
01697 
01698        if (name) {
01699               if (memchr(name, '\0', name_len) != NULL) {
01700                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "The save_path cannot contain NULL characters");
01701                      zval_dtor(return_value);
01702                      RETURN_FALSE;
01703               }
01704               zend_alter_ini_entry("session.save_path", sizeof("session.save_path"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01705        }
01706 }
01707 /* }}} */
01708 
01709 /* {{{ proto string session_id([string newid])
01710    Return the current session id. If newid is given, the session id is replaced with newid */
01711 static PHP_FUNCTION(session_id)
01712 {
01713        char *name = NULL;
01714        int name_len;
01715 
01716        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
01717               return;
01718        }
01719 
01720        if (PS(id)) {
01721               RETVAL_STRING(PS(id), 1);
01722        } else {
01723               RETVAL_EMPTY_STRING();
01724        }
01725 
01726        if (name) {
01727               if (PS(id)) {
01728                      efree(PS(id));
01729               }
01730               PS(id) = estrndup(name, name_len);
01731        }
01732 }
01733 /* }}} */
01734 
01735 /* {{{ proto bool session_regenerate_id([bool delete_old_session])
01736    Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session. */
01737 static PHP_FUNCTION(session_regenerate_id)
01738 {
01739        zend_bool del_ses = 0;
01740 
01741        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &del_ses) == FAILURE) {
01742               return;
01743        }
01744 
01745        if (SG(headers_sent) && PS(use_cookies)) {
01746               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot regenerate session id - headers already sent");
01747               RETURN_FALSE;
01748        }
01749 
01750        if (PS(session_status) == php_session_active) {
01751               if (PS(id)) {
01752                      if (del_ses && PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) {
01753                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session object destruction failed");
01754                             RETURN_FALSE;
01755                      }
01756                      efree(PS(id));
01757                      PS(id) = NULL;
01758               }
01759 
01760               PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC);
01761 
01762               PS(send_cookie) = 1;
01763               php_session_reset_id(TSRMLS_C);
01764 
01765               RETURN_TRUE;
01766        }
01767        RETURN_FALSE;
01768 }
01769 /* }}} */
01770 
01771 /* {{{ proto string session_cache_limiter([string new_cache_limiter])
01772    Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter */
01773 static PHP_FUNCTION(session_cache_limiter)
01774 {
01775        char *limiter = NULL;
01776        int limiter_len;
01777 
01778        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &limiter, &limiter_len) == FAILURE) {
01779               return;
01780        }
01781 
01782        RETVAL_STRING(PS(cache_limiter), 1);
01783 
01784        if (limiter) {
01785               zend_alter_ini_entry("session.cache_limiter", sizeof("session.cache_limiter"), limiter, limiter_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
01786        }
01787 }
01788 /* }}} */
01789 
01790 /* {{{ proto int session_cache_expire([int new_cache_expire])
01791    Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire */
01792 static PHP_FUNCTION(session_cache_expire)
01793 {
01794        zval **expires = NULL;
01795        int argc = ZEND_NUM_ARGS();
01796 
01797        if (zend_parse_parameters(argc TSRMLS_CC, "|Z", &expires) == FAILURE) {
01798               return;
01799        }
01800 
01801        RETVAL_LONG(PS(cache_expire));
01802 
01803        if (argc == 1) {
01804               convert_to_string_ex(expires);
01805               zend_alter_ini_entry("session.cache_expire", sizeof("session.cache_expire"), Z_STRVAL_PP(expires), Z_STRLEN_PP(expires), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
01806        }
01807 }
01808 /* }}} */
01809 
01810 /* {{{ static void php_register_var(zval** entry TSRMLS_DC) */
01811 static void php_register_var(zval** entry TSRMLS_DC)
01812 {
01813        zval **value;
01814 
01815        if (Z_TYPE_PP(entry) == IS_ARRAY) {
01816               if (Z_ARRVAL_PP(entry)->nApplyCount > 1) {
01817                      return;
01818               }
01819 
01820               zend_hash_internal_pointer_reset(Z_ARRVAL_PP(entry));
01821               Z_ARRVAL_PP(entry)->nApplyCount++;
01822 
01823               while (zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void**)&value) == SUCCESS) {
01824                      php_register_var(value TSRMLS_CC);
01825                      zend_hash_move_forward(Z_ARRVAL_PP(entry));
01826               }
01827 
01828               Z_ARRVAL_PP(entry)->nApplyCount--;
01829        } else {
01830               convert_to_string_ex(entry);
01831 
01832               if ((strcmp(Z_STRVAL_PP(entry), "HTTP_SESSION_VARS") != 0) &&
01833                      (strcmp(Z_STRVAL_PP(entry), "_SESSION") != 0)
01834               ) {
01835                      PS_ADD_VARL(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry));
01836               }
01837        }
01838 }
01839 /* }}} */
01840 
01841 /* {{{ proto string session_encode(void)
01842    Serializes the current setup and returns the serialized representation */
01843 static PHP_FUNCTION(session_encode)
01844 {
01845        int len;
01846        char *enc;
01847 
01848        if (zend_parse_parameters_none() == FAILURE) {
01849               return;
01850        }
01851 
01852        enc = php_session_encode(&len TSRMLS_CC);
01853        if (enc == NULL) {
01854               RETURN_FALSE;
01855        }
01856 
01857        RETVAL_STRINGL(enc, len, 0);
01858 }
01859 /* }}} */
01860 
01861 /* {{{ proto bool session_decode(string data)
01862    Deserializes data and reinitializes the variables */
01863 static PHP_FUNCTION(session_decode)
01864 {
01865        char *str;
01866        int str_len;
01867 
01868        if (PS(session_status) == php_session_none) {
01869               RETURN_FALSE;
01870        }
01871 
01872        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
01873               return;
01874        }
01875 
01876        php_session_decode(str, str_len TSRMLS_CC);
01877 
01878        RETURN_TRUE;
01879 }
01880 /* }}} */
01881 
01882 /* {{{ proto bool session_start(void)
01883    Begin session - reinitializes freezed variables, registers browsers etc */
01884 static PHP_FUNCTION(session_start)
01885 {
01886        /* skipping check for non-zero args for performance reasons here ?*/
01887        php_session_start(TSRMLS_C);
01888 
01889        if (PS(session_status) != php_session_active) {
01890               RETURN_FALSE;
01891        }
01892        RETURN_TRUE;
01893 }
01894 /* }}} */
01895 
01896 /* {{{ proto bool session_destroy(void)
01897    Destroy the current session and all data associated with it */
01898 static PHP_FUNCTION(session_destroy)
01899 {
01900        if (zend_parse_parameters_none() == FAILURE) {
01901               return;
01902        }
01903 
01904        RETURN_BOOL(php_session_destroy(TSRMLS_C) == SUCCESS);
01905 }
01906 /* }}} */
01907 
01908 /* {{{ proto void session_unset(void)
01909    Unset all registered variables */
01910 static PHP_FUNCTION(session_unset)
01911 {
01912        if (PS(session_status) == php_session_none) {
01913               RETURN_FALSE;
01914        }
01915 
01916        IF_SESSION_VARS() {
01917               HashTable *ht_sess_var;
01918 
01919               SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars));
01920               ht_sess_var = Z_ARRVAL_P(PS(http_session_vars));
01921 
01922               if (PG(register_globals)) {
01923                      uint str_len;
01924                      char *str;
01925                      ulong num_key;
01926                      HashPosition pos;
01927 
01928                      zend_hash_internal_pointer_reset_ex(ht_sess_var, &pos);
01929 
01930                      while (zend_hash_get_current_key_ex(ht_sess_var, &str, &str_len, &num_key, 0, &pos) == HASH_KEY_IS_STRING) {
01931                             zend_delete_global_variable(str, str_len - 1 TSRMLS_CC);
01932                             zend_hash_move_forward_ex(ht_sess_var, &pos);
01933                      }
01934               }
01935 
01936               /* Clean $_SESSION. */
01937               zend_hash_clean(ht_sess_var);
01938        }
01939 }
01940 /* }}} */
01941 
01942 /* {{{ proto void session_write_close(void)
01943    Write session data and end session */
01944 static PHP_FUNCTION(session_write_close)
01945 {
01946        php_session_flush(TSRMLS_C);
01947 }
01948 /* }}} */
01949 
01950 /* {{{ proto bool session_register(mixed var_names [, mixed ...])
01951    Adds varname(s) to the list of variables which are freezed at the session end */
01952 static PHP_FUNCTION(session_register)
01953 {
01954        zval ***args = NULL;
01955        int num_args, i;
01956 
01957        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
01958               return;
01959        }
01960 
01961        if (PS(session_status) == php_session_none || PS(session_status) == php_session_disabled) {
01962               php_session_start(TSRMLS_C);
01963        }
01964 
01965        if (PS(session_status) == php_session_disabled) {
01966               if (args) {
01967                      efree(args);
01968               }
01969               RETURN_FALSE;
01970        }
01971 
01972        for (i = 0; i < num_args; i++) {
01973               if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
01974                      SEPARATE_ZVAL(args[i]);
01975               }
01976               php_register_var(args[i] TSRMLS_CC);
01977        }
01978 
01979        if (args) {
01980               efree(args);
01981        }
01982 
01983        RETURN_TRUE;
01984 }
01985 /* }}} */
01986 
01987 /* {{{ proto bool session_unregister(string varname)
01988    Removes varname from the list of variables which are freezed at the session end */
01989 static PHP_FUNCTION(session_unregister)
01990 {
01991        char *p_name;
01992        int p_name_len;
01993 
01994        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) {
01995               return;
01996        }
01997 
01998        IF_SESSION_VARS() {
01999               SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars));
02000               PS_DEL_VARL(p_name, p_name_len);
02001        }
02002 
02003        RETURN_TRUE;
02004 }
02005 /* }}} */
02006 
02007 /* {{{ proto bool session_is_registered(string varname)
02008    Checks if a variable is registered in session */
02009 static PHP_FUNCTION(session_is_registered)
02010 {
02011        zval *p_var;
02012        char *p_name;
02013        int p_name_len;
02014 
02015        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) {
02016               return;
02017        }
02018 
02019        if (PS(session_status) == php_session_none) {
02020               RETURN_FALSE;
02021        }
02022 
02023        IF_SESSION_VARS() {
02024               if (zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), p_name, p_name_len+1, (void **)&p_var) == SUCCESS) {
02025                      RETURN_TRUE;
02026               }
02027        }
02028        RETURN_FALSE;
02029 }
02030 /* }}} */
02031 
02032 /* {{{ arginfo */
02033 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_name, 0, 0, 0)
02034        ZEND_ARG_INFO(0, name)
02035 ZEND_END_ARG_INFO()
02036 
02037 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_module_name, 0, 0, 0)
02038        ZEND_ARG_INFO(0, module)
02039 ZEND_END_ARG_INFO()
02040 
02041 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_save_path, 0, 0, 0)
02042        ZEND_ARG_INFO(0, path)
02043 ZEND_END_ARG_INFO()
02044 
02045 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_id, 0, 0, 0)
02046        ZEND_ARG_INFO(0, id)
02047 ZEND_END_ARG_INFO()
02048 
02049 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_regenerate_id, 0, 0, 0)
02050        ZEND_ARG_INFO(0, delete_old_session)
02051 ZEND_END_ARG_INFO()
02052 
02053 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_decode, 0, 0, 1)
02054        ZEND_ARG_INFO(0, data)
02055 ZEND_END_ARG_INFO()
02056 
02057 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_register, 0, 0, 1)
02058        ZEND_ARG_INFO(0, name)
02059        ZEND_ARG_INFO(0, ...)
02060 ZEND_END_ARG_INFO()
02061 
02062 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_unregister, 0, 0, 1)
02063        ZEND_ARG_INFO(0, name)
02064 ZEND_END_ARG_INFO()
02065 
02066 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_is_registered, 0, 0, 1)
02067        ZEND_ARG_INFO(0, name)
02068 ZEND_END_ARG_INFO()
02069 
02070 ZEND_BEGIN_ARG_INFO(arginfo_session_void, 0)
02071 ZEND_END_ARG_INFO()
02072 
02073 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_save_handler, 0, 0, 6)
02074        ZEND_ARG_INFO(0, open)
02075        ZEND_ARG_INFO(0, close)
02076        ZEND_ARG_INFO(0, read)
02077        ZEND_ARG_INFO(0, write)
02078        ZEND_ARG_INFO(0, destroy)
02079        ZEND_ARG_INFO(0, gc)
02080 ZEND_END_ARG_INFO()
02081 
02082 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_limiter, 0, 0, 0)
02083        ZEND_ARG_INFO(0, cache_limiter)
02084 ZEND_END_ARG_INFO()
02085 
02086 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_expire, 0, 0, 0)
02087        ZEND_ARG_INFO(0, new_cache_expire)
02088 ZEND_END_ARG_INFO()
02089 
02090 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_cookie_params, 0, 0, 1)
02091        ZEND_ARG_INFO(0, lifetime)
02092        ZEND_ARG_INFO(0, path)
02093        ZEND_ARG_INFO(0, domain)
02094        ZEND_ARG_INFO(0, secure)
02095        ZEND_ARG_INFO(0, httponly)
02096 ZEND_END_ARG_INFO()
02097 /* }}} */
02098 
02099 /* {{{ session_functions[]
02100  */
02101 static const zend_function_entry session_functions[] = {
02102        PHP_FE(session_name,              arginfo_session_name)
02103        PHP_FE(session_module_name,       arginfo_session_module_name)
02104        PHP_FE(session_save_path,         arginfo_session_save_path)
02105        PHP_FE(session_id,                arginfo_session_id)
02106        PHP_FE(session_regenerate_id,     arginfo_session_regenerate_id)
02107        PHP_FE(session_decode,            arginfo_session_decode)
02108        PHP_DEP_FE(session_register,      arginfo_session_register)
02109        PHP_DEP_FE(session_unregister,    arginfo_session_unregister)
02110        PHP_DEP_FE(session_is_registered, arginfo_session_is_registered)
02111        PHP_FE(session_encode,            arginfo_session_void)
02112        PHP_FE(session_start,             arginfo_session_void)
02113        PHP_FE(session_destroy,           arginfo_session_void)
02114        PHP_FE(session_unset,             arginfo_session_void)
02115        PHP_FE(session_set_save_handler,  arginfo_session_set_save_handler)
02116        PHP_FE(session_cache_limiter,     arginfo_session_cache_limiter)
02117        PHP_FE(session_cache_expire,      arginfo_session_cache_expire)
02118        PHP_FE(session_set_cookie_params, arginfo_session_set_cookie_params)
02119        PHP_FE(session_get_cookie_params, arginfo_session_void)
02120        PHP_FE(session_write_close,       arginfo_session_void)
02121        PHP_FALIAS(session_commit, session_write_close, arginfo_session_void)
02122        PHP_FE_END
02123 };
02124 /* }}} */
02125 
02126 /* ********************************
02127    * Module Setup and Destruction *
02128    ******************************** */
02129 
02130 static PHP_RINIT_FUNCTION(session) /* {{{ */
02131 {
02132        php_rinit_session_globals(TSRMLS_C);
02133 
02134        if (PS(mod) == NULL) {
02135               char *value;
02136 
02137               value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0);
02138               if (value) {
02139                      PS(mod) = _php_find_ps_module(value TSRMLS_CC);
02140               }
02141        }
02142 
02143        if (PS(serializer) == NULL) {
02144               char *value;
02145 
02146               value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
02147               if (value) {
02148                      PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
02149               }
02150        }
02151 
02152        if (PS(mod) == NULL || PS(serializer) == NULL) {
02153               /* current status is unusable */
02154               PS(session_status) = php_session_disabled;
02155               return SUCCESS;
02156        }
02157 
02158        if (PS(auto_start)) {
02159               php_session_start(TSRMLS_C);
02160        }
02161 
02162        return SUCCESS;
02163 }
02164 /* }}} */
02165 
02166 static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */
02167 {
02168        int i;
02169 
02170        php_session_flush(TSRMLS_C);
02171        php_rshutdown_session_globals(TSRMLS_C);
02172 
02173        /* this should NOT be done in php_rshutdown_session_globals() */
02174        for (i = 0; i < 6; i++) {
02175               if (PS(mod_user_names).names[i] != NULL) {
02176                      zval_ptr_dtor(&PS(mod_user_names).names[i]);
02177                      PS(mod_user_names).names[i] = NULL;
02178               }
02179        }
02180 
02181        return SUCCESS;
02182 }
02183 /* }}} */
02184 
02185 static PHP_GINIT_FUNCTION(ps) /* {{{ */
02186 {
02187        int i;
02188 
02189        ps_globals->save_path = NULL;
02190        ps_globals->session_name = NULL;
02191        ps_globals->id = NULL;
02192        ps_globals->mod = NULL;
02193        ps_globals->serializer = NULL;
02194        ps_globals->mod_data = NULL;
02195        ps_globals->session_status = php_session_none;
02196        for (i = 0; i < 6; i++) {
02197               ps_globals->mod_user_names.names[i] = NULL;
02198        }
02199        ps_globals->http_session_vars = NULL;
02200 }
02201 /* }}} */
02202 
02203 static PHP_MINIT_FUNCTION(session) /* {{{ */
02204 {
02205        zend_register_auto_global("_SESSION", sizeof("_SESSION")-1, NULL TSRMLS_CC);
02206 
02207        PS(module_number) = module_number; /* if we really need this var we need to init it in zts mode as well! */
02208 
02209        PS(session_status) = php_session_none;
02210        REGISTER_INI_ENTRIES();
02211 
02212 #ifdef HAVE_LIBMM
02213        PHP_MINIT(ps_mm) (INIT_FUNC_ARGS_PASSTHRU);
02214 #endif
02215        return SUCCESS;
02216 }
02217 /* }}} */
02218 
02219 static PHP_MSHUTDOWN_FUNCTION(session) /* {{{ */
02220 {
02221        UNREGISTER_INI_ENTRIES();
02222 
02223 #ifdef HAVE_LIBMM
02224        PHP_MSHUTDOWN(ps_mm) (SHUTDOWN_FUNC_ARGS_PASSTHRU);
02225 #endif
02226 
02227        ps_serializers[PREDEFINED_SERIALIZERS].name = NULL;
02228        memset(&ps_modules[PREDEFINED_MODULES], 0, (MAX_MODULES-PREDEFINED_MODULES)*sizeof(ps_module *));
02229 
02230        return SUCCESS;
02231 }
02232 /* }}} */
02233 
02234 static PHP_MINFO_FUNCTION(session) /* {{{ */
02235 {
02236        ps_module **mod;
02237        ps_serializer *ser;
02238        smart_str save_handlers = {0};
02239        smart_str ser_handlers = {0};
02240        int i;
02241 
02242        /* Get save handlers */
02243        for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
02244               if (*mod && (*mod)->s_name) {
02245                      smart_str_appends(&save_handlers, (*mod)->s_name);
02246                      smart_str_appendc(&save_handlers, ' ');
02247               }
02248        }
02249 
02250        /* Get serializer handlers */
02251        for (i = 0, ser = ps_serializers; i < MAX_SERIALIZERS; i++, ser++) {
02252               if (ser && ser->name) {
02253                      smart_str_appends(&ser_handlers, ser->name);
02254                      smart_str_appendc(&ser_handlers, ' ');
02255               }
02256        }
02257 
02258        php_info_print_table_start();
02259        php_info_print_table_row(2, "Session Support", "enabled" );
02260 
02261        if (save_handlers.c) {
02262               smart_str_0(&save_handlers);
02263               php_info_print_table_row(2, "Registered save handlers", save_handlers.c);
02264               smart_str_free(&save_handlers);
02265        } else {
02266               php_info_print_table_row(2, "Registered save handlers", "none");
02267        }
02268 
02269        if (ser_handlers.c) {
02270               smart_str_0(&ser_handlers);
02271               php_info_print_table_row(2, "Registered serializer handlers", ser_handlers.c);
02272               smart_str_free(&ser_handlers);
02273        } else {
02274               php_info_print_table_row(2, "Registered serializer handlers", "none");
02275        }
02276 
02277        php_info_print_table_end();
02278 
02279        DISPLAY_INI_ENTRIES();
02280 }
02281 /* }}} */
02282 
02283 static const zend_module_dep session_deps[] = { /* {{{ */
02284        ZEND_MOD_OPTIONAL("hash")
02285        ZEND_MOD_REQUIRED("spl")
02286        ZEND_MOD_END
02287 };
02288 /* }}} */
02289 
02290 zend_module_entry session_module_entry = {
02291        STANDARD_MODULE_HEADER_EX,
02292        NULL,
02293        session_deps,
02294        "session",
02295        session_functions,
02296        PHP_MINIT(session), PHP_MSHUTDOWN(session),
02297        PHP_RINIT(session), PHP_RSHUTDOWN(session),
02298        PHP_MINFO(session),
02299        NO_VERSION_YET,
02300        PHP_MODULE_GLOBALS(ps),
02301        PHP_GINIT(ps),
02302        NULL,
02303        NULL,
02304        STANDARD_MODULE_PROPERTIES_EX
02305 };
02306 
02307 #ifdef COMPILE_DL_SESSION
02308 ZEND_GET_MODULE(session)
02309 #endif
02310 
02311 /*
02312  * Local variables:
02313  * tab-width: 4
02314  * c-basic-offset: 4
02315  * End:
02316  * vim600: noet sw=4 ts=4 fdm=marker
02317  * vim<600: sw=4 ts=4
02318  */