1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Sascha Schumann <sascha@schumann.cx> |
16 | Andrei Zmievski <andrei@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 /* $Id$ */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "php.h"
27
28 #ifdef PHP_WIN32
29 # include "win32/winutil.h"
30 # include "win32/time.h"
31 #else
32 #include <sys/time.h>
33 #endif
34
35 #include <sys/stat.h>
36 #include <fcntl.h>
37
38 #include "php_ini.h"
39 #include "SAPI.h"
40 #include "php_session.h"
41 #include "ext/standard/md5.h"
42 #include "ext/standard/sha1.h"
43 #include "ext/standard/php_var.h"
44 #include "ext/date/php_date.h"
45 #include "ext/standard/php_lcg.h"
46 #include "ext/standard/url_scanner_ex.h"
47 #include "ext/standard/php_rand.h" /* for RAND_MAX */
48 #include "ext/standard/info.h"
49 #include "ext/standard/php_smart_str.h"
50 #include "ext/standard/url.h"
51
52 #include "mod_files.h"
53 #include "mod_user.h"
54
55 #ifdef HAVE_LIBMM
56 #include "mod_mm.h"
57 #endif
58
ZEND_DECLARE_MODULE_GLOBALS(ps)59 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(ps)
60
61 /* ***********
62 * Helpers *
63 *********** */
64
65 #define IF_SESSION_VARS() \
66 if (PS(http_session_vars) && PS(http_session_vars)->type == IS_ARRAY)
67
68 #define SESSION_CHECK_ACTIVE_STATE \
69 if (PS(session_status) == php_session_active) { \
70 php_error_docref(NULL TSRMLS_CC, E_WARNING, "A session is active. You cannot change the session module's ini settings at this time"); \
71 return FAILURE; \
72 }
73
74 /* Dispatched by RINIT and by php_session_destroy */
75 static inline void php_rinit_session_globals(TSRMLS_D) /* {{{ */
76 {
77 PS(id) = NULL;
78 PS(session_status) = php_session_none;
79 PS(mod_data) = NULL;
80 /* Do NOT init PS(mod_user_names) here! */
81 PS(http_session_vars) = NULL;
82 }
83 /* }}} */
84
85 /* Dispatched by RSHUTDOWN and by php_session_destroy */
php_rshutdown_session_globals(TSRMLS_D)86 static inline void php_rshutdown_session_globals(TSRMLS_D) /* {{{ */
87 {
88 if (PS(http_session_vars)) {
89 zval_ptr_dtor(&PS(http_session_vars));
90 PS(http_session_vars) = NULL;
91 }
92 /* Do NOT destroy PS(mod_user_names) here! */
93 if (PS(mod_data)) {
94 zend_try {
95 PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
96 } zend_end_try();
97 }
98 if (PS(id)) {
99 efree(PS(id));
100 }
101 }
102 /* }}} */
103
php_session_destroy(TSRMLS_D)104 static int php_session_destroy(TSRMLS_D) /* {{{ */
105 {
106 int retval = SUCCESS;
107
108 if (PS(session_status) != php_session_active) {
109 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Trying to destroy uninitialized session");
110 return FAILURE;
111 }
112
113 if (PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) {
114 retval = FAILURE;
115 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session object destruction failed");
116 }
117
118 php_rshutdown_session_globals(TSRMLS_C);
119 php_rinit_session_globals(TSRMLS_C);
120
121 return retval;
122 }
123 /* }}} */
124
php_add_session_var(char * name,size_t namelen TSRMLS_DC)125 PHPAPI void php_add_session_var(char *name, size_t namelen TSRMLS_DC) /* {{{ */
126 {
127 zval **sym_track = NULL;
128
129 IF_SESSION_VARS() {
130 zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, (void *) &sym_track);
131 } else {
132 return;
133 }
134
135 /* Set up a proper reference between $_SESSION["x"] and $x. */
136
137 if (PG(register_globals)) {
138 zval **sym_global = NULL;
139
140 if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void *) &sym_global) == SUCCESS) {
141 if ((Z_TYPE_PP(sym_global) == IS_ARRAY && Z_ARRVAL_PP(sym_global) == &EG(symbol_table)) || *sym_global == PS(http_session_vars)) {
142 return;
143 }
144 }
145
146 if (sym_global == NULL && sym_track == NULL) {
147 zval *empty_var;
148
149 ALLOC_INIT_ZVAL(empty_var); /* this sets refcount to 1 */
150 Z_SET_REFCOUNT_P(empty_var, 0); /* our module does not maintain a ref */
151 /* The next call will increase refcount by NR_OF_SYM_TABLES==2 */
152 zend_set_hash_symbol(empty_var, name, namelen, 1, 2, Z_ARRVAL_P(PS(http_session_vars)), &EG(symbol_table));
153 } else if (sym_global == NULL) {
154 SEPARATE_ZVAL_IF_NOT_REF(sym_track);
155 zend_set_hash_symbol(*sym_track, name, namelen, 1, 1, &EG(symbol_table));
156 } else if (sym_track == NULL) {
157 SEPARATE_ZVAL_IF_NOT_REF(sym_global);
158 zend_set_hash_symbol(*sym_global, name, namelen, 1, 1, Z_ARRVAL_P(PS(http_session_vars)));
159 }
160 } else {
161 if (sym_track == NULL) {
162 zval *empty_var;
163
164 ALLOC_INIT_ZVAL(empty_var);
165 ZEND_SET_SYMBOL_WITH_LENGTH(Z_ARRVAL_P(PS(http_session_vars)), name, namelen+1, empty_var, 1, 0);
166 }
167 }
168 }
169 /* }}} */
170
php_set_session_var(char * name,size_t namelen,zval * state_val,php_unserialize_data_t * var_hash TSRMLS_DC)171 PHPAPI void php_set_session_var(char *name, size_t namelen, zval *state_val, php_unserialize_data_t *var_hash TSRMLS_DC) /* {{{ */
172 {
173 if (PG(register_globals)) {
174 zval **old_symbol;
175 if (zend_hash_find(&EG(symbol_table),name,namelen+1,(void *)&old_symbol) == SUCCESS) {
176 if ((Z_TYPE_PP(old_symbol) == IS_ARRAY && Z_ARRVAL_PP(old_symbol) == &EG(symbol_table)) || *old_symbol == PS(http_session_vars)) {
177 return;
178 }
179
180 /* A global symbol with the same name exists already. That
181 * symbol might have been created by other means (e.g. $_GET).
182 *
183 * hash_update in zend_set_hash_symbol is not good, because
184 * it will leave referenced variables (such as local instances
185 * of a global variable) dangling.
186 *
187 * BTW: if you use register_globals references between
188 * session-vars won't work because of this very reason! */
189
190 REPLACE_ZVAL_VALUE(old_symbol,state_val,1);
191
192 /* The following line will update the reference table used for
193 * unserialization. It is optional, because some storage
194 * formats may not be able to represent references. */
195
196 if (var_hash) {
197 PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash,state_val,*old_symbol);
198 }
199
200 zend_set_hash_symbol(*old_symbol, name, namelen, 1, 1, Z_ARRVAL_P(PS(http_session_vars)));
201 } else {
202 zend_set_hash_symbol(state_val, name, namelen, 1, 2, Z_ARRVAL_P(PS(http_session_vars)), &EG(symbol_table));
203 }
204 } else IF_SESSION_VARS() {
205 zend_set_hash_symbol(state_val, name, namelen, PZVAL_IS_REF(state_val), 1, Z_ARRVAL_P(PS(http_session_vars)));
206 }
207 }
208 /* }}} */
209
php_get_session_var(char * name,size_t namelen,zval *** state_var TSRMLS_DC)210 PHPAPI int php_get_session_var(char *name, size_t namelen, zval ***state_var TSRMLS_DC) /* {{{ */
211 {
212 int ret = FAILURE;
213
214 IF_SESSION_VARS() {
215 ret = zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, (void **) state_var);
216
217 /* If register_globals is enabled, and
218 * if there is an entry for the slot in $_SESSION, and
219 * if that entry is still set to NULL, and
220 * if the global var exists, then
221 * we prefer the same key in the global sym table. */
222
223 if (PG(register_globals) && ret == SUCCESS && Z_TYPE_PP(*state_var) == IS_NULL) {
224 zval **tmp;
225
226 if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
227 *state_var = tmp;
228 }
229 }
230 }
231 return ret;
232 }
233 /* }}} */
234
php_session_track_init(TSRMLS_D)235 static void php_session_track_init(TSRMLS_D) /* {{{ */
236 {
237 zval *session_vars = NULL;
238
239 /* Unconditionally destroy existing arrays -- possible dirty data */
240 zend_delete_global_variable("HTTP_SESSION_VARS", sizeof("HTTP_SESSION_VARS")-1 TSRMLS_CC);
241 zend_delete_global_variable("_SESSION", sizeof("_SESSION")-1 TSRMLS_CC);
242
243 if (PS(http_session_vars)) {
244 zval_ptr_dtor(&PS(http_session_vars));
245 }
246
247 MAKE_STD_ZVAL(session_vars);
248 array_init(session_vars);
249 PS(http_session_vars) = session_vars;
250
251 if (PG(register_long_arrays)) {
252 ZEND_SET_GLOBAL_VAR_WITH_LENGTH("HTTP_SESSION_VARS", sizeof("HTTP_SESSION_VARS"), PS(http_session_vars), 3, 1);
253 ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 3, 1);
254 }
255 else {
256 ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 2, 1);
257 }
258 }
259 /* }}} */
260
php_session_encode(int * newlen TSRMLS_DC)261 static char *php_session_encode(int *newlen TSRMLS_DC) /* {{{ */
262 {
263 char *ret = NULL;
264
265 IF_SESSION_VARS() {
266 if (!PS(serializer)) {
267 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to encode session object");
268 ret = NULL;
269 } else if (PS(serializer)->encode(&ret, newlen TSRMLS_CC) == FAILURE) {
270 ret = NULL;
271 }
272 } else {
273 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot encode non-existent session");
274 }
275 return ret;
276 }
277 /* }}} */
278
php_session_decode(const char * val,int vallen TSRMLS_DC)279 static void php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */
280 {
281 if (!PS(serializer)) {
282 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
283 return;
284 }
285 if (PS(serializer)->decode(val, vallen TSRMLS_CC) == FAILURE) {
286 php_session_destroy(TSRMLS_C);
287 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to decode session object. Session has been destroyed");
288 }
289 }
290 /* }}} */
291
292 /*
293 * Note that we cannot use the BASE64 alphabet here, because
294 * it contains "/" and "+": both are unacceptable for simple inclusion
295 * into URLs.
296 */
297
298 static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-";
299
300 enum {
301 PS_HASH_FUNC_MD5,
302 PS_HASH_FUNC_SHA1,
303 PS_HASH_FUNC_OTHER
304 };
305
306 /* returns a pointer to the byte after the last valid character in out */
bin_to_readable(char * in,size_t inlen,char * out,char nbits)307 static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {{{ */
308 {
309 unsigned char *p, *q;
310 unsigned short w;
311 int mask;
312 int have;
313
314 p = (unsigned char *) in;
315 q = (unsigned char *)in + inlen;
316
317 w = 0;
318 have = 0;
319 mask = (1 << nbits) - 1;
320
321 while (1) {
322 if (have < nbits) {
323 if (p < q) {
324 w |= *p++ << have;
325 have += 8;
326 } else {
327 /* consumed everything? */
328 if (have == 0) break;
329 /* No? We need a final round */
330 have = nbits;
331 }
332 }
333
334 /* consume nbits */
335 *out++ = hexconvtab[w & mask];
336 w >>= nbits;
337 have -= nbits;
338 }
339
340 *out = '\0';
341 return out;
342 }
343 /* }}} */
344
php_session_create_id(PS_CREATE_SID_ARGS)345 PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
346 {
347 PHP_MD5_CTX md5_context;
348 PHP_SHA1_CTX sha1_context;
349 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
350 void *hash_context;
351 #endif
352 unsigned char *digest;
353 int digest_len;
354 int j;
355 char *buf, *outid;
356 struct timeval tv;
357 zval **array;
358 zval **token;
359 char *remote_addr = NULL;
360
361 gettimeofday(&tv, NULL);
362
363 if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &array) == SUCCESS &&
364 Z_TYPE_PP(array) == IS_ARRAY &&
365 zend_hash_find(Z_ARRVAL_PP(array), "REMOTE_ADDR", sizeof("REMOTE_ADDR"), (void **) &token) == SUCCESS &&
366 Z_TYPE_PP(token) == IS_STRING
367 ) {
368 remote_addr = Z_STRVAL_PP(token);
369 }
370
371 /* maximum 15+19+19+10 bytes */
372 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);
373
374 switch (PS(hash_func)) {
375 case PS_HASH_FUNC_MD5:
376 PHP_MD5Init(&md5_context);
377 PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
378 digest_len = 16;
379 break;
380 case PS_HASH_FUNC_SHA1:
381 PHP_SHA1Init(&sha1_context);
382 PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf));
383 digest_len = 20;
384 break;
385 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
386 case PS_HASH_FUNC_OTHER:
387 if (!PS(hash_ops)) {
388 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
389 efree(buf);
390 return NULL;
391 }
392
393 hash_context = emalloc(PS(hash_ops)->context_size);
394 PS(hash_ops)->hash_init(hash_context);
395 PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf));
396 digest_len = PS(hash_ops)->digest_size;
397 break;
398 #endif /* HAVE_HASH_EXT */
399 default:
400 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
401 efree(buf);
402 return NULL;
403 }
404 efree(buf);
405
406 if (PS(entropy_length) > 0) {
407 #ifdef PHP_WIN32
408 unsigned char rbuf[2048];
409 size_t toread = PS(entropy_length);
410
411 if (php_win32_get_random_bytes(rbuf, (size_t) toread) == SUCCESS){
412
413 switch (PS(hash_func)) {
414 case PS_HASH_FUNC_MD5:
415 PHP_MD5Update(&md5_context, rbuf, toread);
416 break;
417 case PS_HASH_FUNC_SHA1:
418 PHP_SHA1Update(&sha1_context, rbuf, toread);
419 break;
420 # if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
421 case PS_HASH_FUNC_OTHER:
422 PS(hash_ops)->hash_update(hash_context, rbuf, toread);
423 break;
424 # endif /* HAVE_HASH_EXT */
425 }
426 }
427 #else
428 int fd;
429
430 fd = VCWD_OPEN(PS(entropy_file), O_RDONLY);
431 if (fd >= 0) {
432 unsigned char rbuf[2048];
433 int n;
434 int to_read = PS(entropy_length);
435
436 while (to_read > 0) {
437 n = read(fd, rbuf, MIN(to_read, sizeof(rbuf)));
438 if (n <= 0) break;
439
440 switch (PS(hash_func)) {
441 case PS_HASH_FUNC_MD5:
442 PHP_MD5Update(&md5_context, rbuf, n);
443 break;
444 case PS_HASH_FUNC_SHA1:
445 PHP_SHA1Update(&sha1_context, rbuf, n);
446 break;
447 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
448 case PS_HASH_FUNC_OTHER:
449 PS(hash_ops)->hash_update(hash_context, rbuf, n);
450 break;
451 #endif /* HAVE_HASH_EXT */
452 }
453 to_read -= n;
454 }
455 close(fd);
456 }
457 #endif
458 }
459
460 digest = emalloc(digest_len + 1);
461 switch (PS(hash_func)) {
462 case PS_HASH_FUNC_MD5:
463 PHP_MD5Final(digest, &md5_context);
464 break;
465 case PS_HASH_FUNC_SHA1:
466 PHP_SHA1Final(digest, &sha1_context);
467 break;
468 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
469 case PS_HASH_FUNC_OTHER:
470 PS(hash_ops)->hash_final(digest, hash_context);
471 efree(hash_context);
472 break;
473 #endif /* HAVE_HASH_EXT */
474 }
475
476 if (PS(hash_bits_per_character) < 4
477 || PS(hash_bits_per_character) > 6) {
478 PS(hash_bits_per_character) = 4;
479
480 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");
481 }
482
483 outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5)));
484 j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid);
485 efree(digest);
486
487 if (newlen) {
488 *newlen = j;
489 }
490
491 return outid;
492 }
493 /* }}} */
494
php_session_initialize(TSRMLS_D)495 static void php_session_initialize(TSRMLS_D) /* {{{ */
496 {
497 char *val;
498 int vallen;
499
500 /* check session name for invalid characters */
501 if (PS(id) && strpbrk(PS(id), "\r\n\t <>'\"\\")) {
502 efree(PS(id));
503 PS(id) = NULL;
504 }
505
506 if (!PS(mod)) {
507 php_error_docref(NULL TSRMLS_CC, E_ERROR, "No storage module chosen - failed to initialize session");
508 return;
509 }
510
511 /* Open session handler first */
512 if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name) TSRMLS_CC) == FAILURE) {
513 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
514 return;
515 }
516
517 /* If there is no ID, use session module to create one */
518 if (!PS(id)) {
519 new_session:
520 PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC);
521 if (PS(use_cookies)) {
522 PS(send_cookie) = 1;
523 }
524 }
525
526 /* Read data */
527 /* Question: if you create a SID here, should you also try to read data?
528 * I'm not sure, but while not doing so will remove one session operation
529 * it could prove usefull for those sites which wish to have "default"
530 * session information. */
531 php_session_track_init(TSRMLS_C);
532 PS(invalid_session_id) = 0;
533 if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, &vallen TSRMLS_CC) == SUCCESS) {
534 php_session_decode(val, vallen TSRMLS_CC);
535 efree(val);
536 } else if (PS(invalid_session_id)) { /* address instances where the session read fails due to an invalid id */
537 PS(invalid_session_id) = 0;
538 efree(PS(id));
539 PS(id) = NULL;
540 goto new_session;
541 }
542 }
543 /* }}} */
544
migrate_global(HashTable * ht,HashPosition * pos TSRMLS_DC)545 static int migrate_global(HashTable *ht, HashPosition *pos TSRMLS_DC) /* {{{ */
546 {
547 char *str;
548 uint str_len;
549 ulong num_key;
550 int n;
551 zval **val;
552 int ret = 0;
553
554 n = zend_hash_get_current_key_ex(ht, &str, &str_len, &num_key, 0, pos);
555
556 switch (n) {
557 case HASH_KEY_IS_STRING:
558 if (zend_hash_find(&EG(symbol_table), str, str_len, (void **) &val) == SUCCESS &&
559 val && Z_TYPE_PP(val) != IS_NULL
560 ) {
561 ZEND_SET_SYMBOL_WITH_LENGTH(ht, str, str_len, *val, Z_REFCOUNT_PP(val) + 1, 1);
562 ret = 1;
563 }
564 break;
565 case HASH_KEY_IS_LONG:
566 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "The session bug compatibility code will not "
567 "try to locate the global variable $%lu due to its "
568 "numeric nature", num_key);
569 break;
570 }
571 return ret;
572 }
573 /* }}} */
574
php_session_save_current_state(TSRMLS_D)575 static void php_session_save_current_state(TSRMLS_D) /* {{{ */
576 {
577 int ret = FAILURE;
578
579 IF_SESSION_VARS() {
580 if (PS(bug_compat) && !PG(register_globals)) {
581 HashTable *ht = Z_ARRVAL_P(PS(http_session_vars));
582 HashPosition pos;
583 zval **val;
584 int do_warn = 0;
585
586 zend_hash_internal_pointer_reset_ex(ht, &pos);
587
588 while (zend_hash_get_current_data_ex(ht, (void **) &val, &pos) != FAILURE) {
589 if (Z_TYPE_PP(val) == IS_NULL) {
590 if (migrate_global(ht, &pos TSRMLS_CC)) {
591 do_warn = 1;
592 }
593 }
594 zend_hash_move_forward_ex(ht, &pos);
595 }
596
597 if (do_warn && PS(bug_compat_warn)) {
598 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");
599 }
600 }
601
602 if (PS(mod_data)) {
603 char *val;
604 int vallen;
605
606 val = php_session_encode(&vallen TSRMLS_CC);
607 if (val) {
608 ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, vallen TSRMLS_CC);
609 efree(val);
610 } else {
611 ret = PS(mod)->s_write(&PS(mod_data), PS(id), "", 0 TSRMLS_CC);
612 }
613 }
614
615 if (ret == FAILURE) {
616 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write session data (%s). Please "
617 "verify that the current setting of session.save_path "
618 "is correct (%s)",
619 PS(mod)->s_name,
620 PS(save_path));
621 }
622 }
623
624 if (PS(mod_data)) {
625 PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
626 }
627 }
628 /* }}} */
629
630 /* *************************
631 * INI Settings/Handlers *
632 ************************* */
633
PHP_INI_MH(OnUpdateSaveHandler)634 static PHP_INI_MH(OnUpdateSaveHandler) /* {{{ */
635 {
636 ps_module *tmp;
637 SESSION_CHECK_ACTIVE_STATE;
638
639 tmp = _php_find_ps_module(new_value TSRMLS_CC);
640
641 if (PG(modules_activated) && !tmp) {
642 int err_type;
643
644 if (stage == ZEND_INI_STAGE_RUNTIME) {
645 err_type = E_WARNING;
646 } else {
647 err_type = E_ERROR;
648 }
649
650 /* Do not output error when restoring ini options. */
651 if (stage != ZEND_INI_STAGE_DEACTIVATE) {
652 php_error_docref(NULL TSRMLS_CC, err_type, "Cannot find save handler '%s'", new_value);
653 }
654 return FAILURE;
655 }
656 PS(mod) = tmp;
657
658 return SUCCESS;
659 }
660 /* }}} */
661
PHP_INI_MH(OnUpdateSerializer)662 static PHP_INI_MH(OnUpdateSerializer) /* {{{ */
663 {
664 const ps_serializer *tmp;
665 SESSION_CHECK_ACTIVE_STATE;
666
667 tmp = _php_find_ps_serializer(new_value TSRMLS_CC);
668
669 if (PG(modules_activated) && !tmp) {
670 int err_type;
671
672 if (stage == ZEND_INI_STAGE_RUNTIME) {
673 err_type = E_WARNING;
674 } else {
675 err_type = E_ERROR;
676 }
677
678 /* Do not output error when restoring ini options. */
679 if (stage != ZEND_INI_STAGE_DEACTIVATE) {
680 php_error_docref(NULL TSRMLS_CC, err_type, "Cannot find serialization handler '%s'", new_value);
681 }
682 return FAILURE;
683 }
684 PS(serializer) = tmp;
685
686 return SUCCESS;
687 }
688 /* }}} */
689
PHP_INI_MH(OnUpdateTransSid)690 static PHP_INI_MH(OnUpdateTransSid) /* {{{ */
691 {
692 SESSION_CHECK_ACTIVE_STATE;
693
694 if (!strncasecmp(new_value, "on", sizeof("on"))) {
695 PS(use_trans_sid) = (zend_bool) 1;
696 } else {
697 PS(use_trans_sid) = (zend_bool) atoi(new_value);
698 }
699
700 return SUCCESS;
701 }
702 /* }}} */
703
PHP_INI_MH(OnUpdateSaveDir)704 static PHP_INI_MH(OnUpdateSaveDir) /* {{{ */
705 {
706 /* Only do the safemode/open_basedir check at runtime */
707 if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) {
708 char *p;
709
710 if (memchr(new_value, '\0', new_value_length) != NULL) {
711 return FAILURE;
712 }
713
714 /* we do not use zend_memrchr() since path can contain ; itself */
715 if ((p = strchr(new_value, ';'))) {
716 char *p2;
717 p++;
718 if ((p2 = strchr(p, ';'))) {
719 p = p2 + 1;
720 }
721 } else {
722 p = new_value;
723 }
724
725 if (PG(safe_mode) && *p && (!php_checkuid(p, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
726 return FAILURE;
727 }
728
729 if (PG(open_basedir) && *p && php_check_open_basedir(p TSRMLS_CC)) {
730 return FAILURE;
731 }
732 }
733
734 OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
735 return SUCCESS;
736 }
737 /* }}} */
738
PHP_INI_MH(OnUpdateHashFunc)739 static PHP_INI_MH(OnUpdateHashFunc) /* {{{ */
740 {
741 long val;
742 char *endptr = NULL;
743
744 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
745 PS(hash_ops) = NULL;
746 #endif
747
748 val = strtol(new_value, &endptr, 10);
749 if (endptr && (*endptr == '\0')) {
750 /* Numeric value */
751 PS(hash_func) = val ? 1 : 0;
752
753 return SUCCESS;
754 }
755
756 if (new_value_length == (sizeof("md5") - 1) &&
757 strncasecmp(new_value, "md5", sizeof("md5") - 1) == 0) {
758 PS(hash_func) = PS_HASH_FUNC_MD5;
759
760 return SUCCESS;
761 }
762
763 if (new_value_length == (sizeof("sha1") - 1) &&
764 strncasecmp(new_value, "sha1", sizeof("sha1") - 1) == 0) {
765 PS(hash_func) = PS_HASH_FUNC_SHA1;
766
767 return SUCCESS;
768 }
769
770 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) /* {{{ */
771 {
772 php_hash_ops *ops = (php_hash_ops*)php_hash_fetch_ops(new_value, new_value_length);
773
774 if (ops) {
775 PS(hash_func) = PS_HASH_FUNC_OTHER;
776 PS(hash_ops) = ops;
777
778 return SUCCESS;
779 }
780 }
781 #endif /* HAVE_HASH_EXT }}} */
782
783 return FAILURE;
784 }
785 /* }}} */
786
787 /* {{{ PHP_INI
788 */
789 PHP_INI_BEGIN()
790 STD_PHP_INI_BOOLEAN("session.bug_compat_42", "1", PHP_INI_ALL, OnUpdateBool, bug_compat, php_ps_globals, ps_globals)
791 STD_PHP_INI_BOOLEAN("session.bug_compat_warn", "1", PHP_INI_ALL, OnUpdateBool, bug_compat_warn, php_ps_globals, ps_globals)
792 STD_PHP_INI_ENTRY("session.save_path", "", PHP_INI_ALL, OnUpdateSaveDir,save_path, php_ps_globals, ps_globals)
793 STD_PHP_INI_ENTRY("session.name", "PHPSESSID", PHP_INI_ALL, OnUpdateString, session_name, php_ps_globals, ps_globals)
794 PHP_INI_ENTRY("session.save_handler", "files", PHP_INI_ALL, OnUpdateSaveHandler)
795 STD_PHP_INI_BOOLEAN("session.auto_start", "0", PHP_INI_ALL, OnUpdateBool, auto_start, php_ps_globals, ps_globals)
796 STD_PHP_INI_ENTRY("session.gc_probability", "1", PHP_INI_ALL, OnUpdateLong, gc_probability, php_ps_globals, ps_globals)
797 STD_PHP_INI_ENTRY("session.gc_divisor", "100", PHP_INI_ALL, OnUpdateLong, gc_divisor, php_ps_globals, ps_globals)
798 STD_PHP_INI_ENTRY("session.gc_maxlifetime", "1440", PHP_INI_ALL, OnUpdateLong, gc_maxlifetime, php_ps_globals, ps_globals)
799 PHP_INI_ENTRY("session.serialize_handler", "php", PHP_INI_ALL, OnUpdateSerializer)
800 STD_PHP_INI_ENTRY("session.cookie_lifetime", "0", PHP_INI_ALL, OnUpdateLong, cookie_lifetime, php_ps_globals, ps_globals)
801 STD_PHP_INI_ENTRY("session.cookie_path", "/", PHP_INI_ALL, OnUpdateString, cookie_path, php_ps_globals, ps_globals)
802 STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateString, cookie_domain, php_ps_globals, ps_globals)
803 STD_PHP_INI_BOOLEAN("session.cookie_secure", "", PHP_INI_ALL, OnUpdateBool, cookie_secure, php_ps_globals, ps_globals)
804 STD_PHP_INI_BOOLEAN("session.cookie_httponly", "", PHP_INI_ALL, OnUpdateBool, cookie_httponly, php_ps_globals, ps_globals)
805 STD_PHP_INI_BOOLEAN("session.use_cookies", "1", PHP_INI_ALL, OnUpdateBool, use_cookies, php_ps_globals, ps_globals)
806 STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals)
807 STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals)
808 STD_PHP_INI_ENTRY("session.entropy_file", "", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals)
809 STD_PHP_INI_ENTRY("session.entropy_length", "0", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals)
810 STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateString, cache_limiter, php_ps_globals, ps_globals)
811 STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateLong, cache_expire, php_ps_globals, ps_globals)
812 PHP_INI_ENTRY("session.use_trans_sid", "0", PHP_INI_ALL, OnUpdateTransSid)
813 PHP_INI_ENTRY("session.hash_function", "0", PHP_INI_ALL, OnUpdateHashFunc)
814 STD_PHP_INI_ENTRY("session.hash_bits_per_character", "4", PHP_INI_ALL, OnUpdateLong, hash_bits_per_character, php_ps_globals, ps_globals)
815
816 /* Commented out until future discussion */
817 /* PHP_INI_ENTRY("session.encode_sources", "globals,track", PHP_INI_ALL, NULL) */
PHP_INI_END()818 PHP_INI_END()
819 /* }}} */
820
821 /* ***************
822 * Serializers *
823 *************** */
824
825 #define PS_BIN_NR_OF_BITS 8
826 #define PS_BIN_UNDEF (1<<(PS_BIN_NR_OF_BITS-1))
827 #define PS_BIN_MAX (PS_BIN_UNDEF-1)
828
829 PS_SERIALIZER_ENCODE_FUNC(php_binary) /* {{{ */
830 {
831 smart_str buf = {0};
832 php_serialize_data_t var_hash;
833 PS_ENCODE_VARS;
834
835 PHP_VAR_SERIALIZE_INIT(var_hash);
836
837 PS_ENCODE_LOOP(
838 if (key_length > PS_BIN_MAX) continue;
839 smart_str_appendc(&buf, (unsigned char) key_length);
840 smart_str_appendl(&buf, key, key_length);
841 php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
842 } else {
843 if (key_length > PS_BIN_MAX) continue;
844 smart_str_appendc(&buf, (unsigned char) (key_length & PS_BIN_UNDEF));
845 smart_str_appendl(&buf, key, key_length);
846 );
847
848 if (newlen) {
849 *newlen = buf.len;
850 }
851 smart_str_0(&buf);
852 *newstr = buf.c;
853 PHP_VAR_SERIALIZE_DESTROY(var_hash);
854
855 return SUCCESS;
856 }
857 /* }}} */
858
859 PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
860 {
861 const char *p;
862 char *name;
863 const char *endptr = val + vallen;
864 zval *current;
865 int namelen;
866 int has_value;
867 php_unserialize_data_t var_hash;
868
869 PHP_VAR_UNSERIALIZE_INIT(var_hash);
870
871 for (p = val; p < endptr; ) {
872 zval **tmp;
873 namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);
874
875 if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
876 return FAILURE;
877 }
878
879 has_value = *p & PS_BIN_UNDEF ? 0 : 1;
880
881 name = estrndup(p + 1, namelen);
882
883 p += namelen + 1;
884
885 if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
886 if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {
887 efree(name);
888 continue;
889 }
890 }
891
892 if (has_value) {
893 ALLOC_INIT_ZVAL(current);
894 if (php_var_unserialize(¤t, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
895 php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC);
896 }
897 zval_ptr_dtor(¤t);
898 }
899 PS_ADD_VARL(name, namelen);
900 efree(name);
901 }
902
903 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
904
905 return SUCCESS;
906 }
907 /* }}} */
908
909 #define PS_DELIMITER '|'
910 #define PS_UNDEF_MARKER '!'
911
912 PS_SERIALIZER_ENCODE_FUNC(php) /* {{{ */
913 {
914 smart_str buf = {0};
915 php_serialize_data_t var_hash;
916 PS_ENCODE_VARS;
917
918 PHP_VAR_SERIALIZE_INIT(var_hash);
919
920 PS_ENCODE_LOOP(
921 smart_str_appendl(&buf, key, key_length);
922 if (memchr(key, PS_DELIMITER, key_length) || memchr(key, PS_UNDEF_MARKER, key_length)) {
923 PHP_VAR_SERIALIZE_DESTROY(var_hash);
924 smart_str_free(&buf);
925 return FAILURE;
926 }
927 smart_str_appendc(&buf, PS_DELIMITER);
928
929 php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
930 } else {
931 smart_str_appendc(&buf, PS_UNDEF_MARKER);
932 smart_str_appendl(&buf, key, key_length);
933 smart_str_appendc(&buf, PS_DELIMITER);
934 );
935
936 if (newlen) {
937 *newlen = buf.len;
938 }
939 smart_str_0(&buf);
940 *newstr = buf.c;
941
942 PHP_VAR_SERIALIZE_DESTROY(var_hash);
943 return SUCCESS;
944 }
945 /* }}} */
946
947 PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
948 {
949 const char *p, *q;
950 char *name;
951 const char *endptr = val + vallen;
952 zval *current;
953 int namelen;
954 int has_value;
955 php_unserialize_data_t var_hash;
956
957 PHP_VAR_UNSERIALIZE_INIT(var_hash);
958
959 p = val;
960
961 while (p < endptr) {
962 zval **tmp;
963 q = p;
964 while (*q != PS_DELIMITER) {
965 if (++q >= endptr) goto break_outer_loop;
966 }
967 if (p[0] == PS_UNDEF_MARKER) {
968 p++;
969 has_value = 0;
970 } else {
971 has_value = 1;
972 }
973
974 namelen = q - p;
975 name = estrndup(p, namelen);
976 q++;
977
978 if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
979 if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {
980 goto skip;
981 }
982 }
983
984 if (has_value) {
985 ALLOC_INIT_ZVAL(current);
986 if (php_var_unserialize(¤t, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
987 php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC);
988 }
989 zval_ptr_dtor(¤t);
990 }
991 PS_ADD_VARL(name, namelen);
992 skip:
993 efree(name);
994
995 p = q;
996 }
997 break_outer_loop:
998
999 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1000
1001 return SUCCESS;
1002 }
1003 /* }}} */
1004
1005 #define MAX_SERIALIZERS 10
1006 #define PREDEFINED_SERIALIZERS 2
1007
1008 static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = {
1009 PS_SERIALIZER_ENTRY(php),
1010 PS_SERIALIZER_ENTRY(php_binary)
1011 };
1012
1013 PHPAPI int php_session_register_serializer(const char *name, int (*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */
1014 {
1015 int ret = -1;
1016 int i;
1017
1018 for (i = 0; i < MAX_SERIALIZERS; i++) {
1019 if (ps_serializers[i].name == NULL) {
1020 ps_serializers[i].name = name;
1021 ps_serializers[i].encode = encode;
1022 ps_serializers[i].decode = decode;
1023 ps_serializers[i + 1].name = NULL;
1024 ret = 0;
1025 break;
1026 }
1027 }
1028 return ret;
1029 }
1030 /* }}} */
1031
1032 /* *******************
1033 * Storage Modules *
1034 ******************* */
1035
1036 #define MAX_MODULES 10
1037 #define PREDEFINED_MODULES 2
1038
1039 static ps_module *ps_modules[MAX_MODULES + 1] = {
1040 ps_files_ptr,
1041 ps_user_ptr
1042 };
1043
1044 PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
1045 {
1046 int ret = -1;
1047 int i;
1048
1049 for (i = 0; i < MAX_MODULES; i++) {
1050 if (!ps_modules[i]) {
1051 ps_modules[i] = ptr;
1052 ret = 0;
1053 break;
1054 }
1055 }
1056 return ret;
1057 }
1058 /* }}} */
1059
1060 /* ******************
1061 * Cache Limiters *
1062 ****************** */
1063
1064 typedef struct {
1065 char *name;
1066 void (*func)(TSRMLS_D);
1067 } php_session_cache_limiter_t;
1068
1069 #define CACHE_LIMITER(name) _php_cache_limiter_##name
1070 #define CACHE_LIMITER_FUNC(name) static void CACHE_LIMITER(name)(TSRMLS_D)
1071 #define CACHE_LIMITER_ENTRY(name) { #name, CACHE_LIMITER(name) },
1072 #define ADD_HEADER(a) sapi_add_header(a, strlen(a), 1);
1073 #define MAX_STR 512
1074
1075 static char *month_names[] = {
1076 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1077 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1078 };
1079
1080 static char *week_days[] = {
1081 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
1082 };
1083
1084 static inline void strcpy_gmt(char *ubuf, time_t *when) /* {{{ */
1085 {
1086 char buf[MAX_STR];
1087 struct tm tm, *res;
1088 int n;
1089
1090 res = php_gmtime_r(when, &tm);
1091
1092 if (!res) {
1093 buf[0] = '\0';
1094 return;
1095 }
1096
1097 n = slprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT", /* SAFE */
1098 week_days[tm.tm_wday], tm.tm_mday,
1099 month_names[tm.tm_mon], tm.tm_year + 1900,
1100 tm.tm_hour, tm.tm_min,
1101 tm.tm_sec);
1102 memcpy(ubuf, buf, n);
1103 ubuf[n] = '\0';
1104 }
1105 /* }}} */
1106
1107 static inline void last_modified(TSRMLS_D) /* {{{ */
1108 {
1109 const char *path;
1110 struct stat sb;
1111 char buf[MAX_STR + 1];
1112
1113 path = SG(request_info).path_translated;
1114 if (path) {
1115 if (VCWD_STAT(path, &sb) == -1) {
1116 return;
1117 }
1118
1119 #define LAST_MODIFIED "Last-Modified: "
1120 memcpy(buf, LAST_MODIFIED, sizeof(LAST_MODIFIED) - 1);
1121 strcpy_gmt(buf + sizeof(LAST_MODIFIED) - 1, &sb.st_mtime);
1122 ADD_HEADER(buf);
1123 }
1124 }
1125 /* }}} */
1126
1127 #define EXPIRES "Expires: "
1128 CACHE_LIMITER_FUNC(public) /* {{{ */
1129 {
1130 char buf[MAX_STR + 1];
1131 struct timeval tv;
1132 time_t now;
1133
1134 gettimeofday(&tv, NULL);
1135 now = tv.tv_sec + PS(cache_expire) * 60;
1136 memcpy(buf, EXPIRES, sizeof(EXPIRES) - 1);
1137 strcpy_gmt(buf + sizeof(EXPIRES) - 1, &now);
1138 ADD_HEADER(buf);
1139
1140 snprintf(buf, sizeof(buf) , "Cache-Control: public, max-age=%ld", PS(cache_expire) * 60); /* SAFE */
1141 ADD_HEADER(buf);
1142
1143 last_modified(TSRMLS_C);
1144 }
1145 /* }}} */
1146
1147 CACHE_LIMITER_FUNC(private_no_expire) /* {{{ */
1148 {
1149 char buf[MAX_STR + 1];
1150
1151 snprintf(buf, sizeof(buf), "Cache-Control: private, max-age=%ld, pre-check=%ld", PS(cache_expire) * 60, PS(cache_expire) * 60); /* SAFE */
1152 ADD_HEADER(buf);
1153
1154 last_modified(TSRMLS_C);
1155 }
1156 /* }}} */
1157
1158 CACHE_LIMITER_FUNC(private) /* {{{ */
1159 {
1160 ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
1161 CACHE_LIMITER(private_no_expire)(TSRMLS_C);
1162 }
1163 /* }}} */
1164
1165 CACHE_LIMITER_FUNC(nocache) /* {{{ */
1166 {
1167 ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
1168
1169 /* For HTTP/1.1 conforming clients and the rest (MSIE 5) */
1170 ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
1171
1172 /* For HTTP/1.0 conforming clients */
1173 ADD_HEADER("Pragma: no-cache");
1174 }
1175 /* }}} */
1176
1177 static php_session_cache_limiter_t php_session_cache_limiters[] = {
1178 CACHE_LIMITER_ENTRY(public)
1179 CACHE_LIMITER_ENTRY(private)
1180 CACHE_LIMITER_ENTRY(private_no_expire)
1181 CACHE_LIMITER_ENTRY(nocache)
1182 {0}
1183 };
1184
1185 static int php_session_cache_limiter(TSRMLS_D) /* {{{ */
1186 {
1187 php_session_cache_limiter_t *lim;
1188
1189 if (PS(cache_limiter)[0] == '\0') return 0;
1190
1191 if (SG(headers_sent)) {
1192 char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
1193 int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
1194
1195 if (output_start_filename) {
1196 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);
1197 } else {
1198 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent");
1199 }
1200 return -2;
1201 }
1202
1203 for (lim = php_session_cache_limiters; lim->name; lim++) {
1204 if (!strcasecmp(lim->name, PS(cache_limiter))) {
1205 lim->func(TSRMLS_C);
1206 return 0;
1207 }
1208 }
1209
1210 return -1;
1211 }
1212 /* }}} */
1213
1214 /* *********************
1215 * Cookie Management *
1216 ********************* */
1217
1218 #define COOKIE_SET_COOKIE "Set-Cookie: "
1219 #define COOKIE_EXPIRES "; expires="
1220 #define COOKIE_PATH "; path="
1221 #define COOKIE_DOMAIN "; domain="
1222 #define COOKIE_SECURE "; secure"
1223 #define COOKIE_HTTPONLY "; HttpOnly"
1224
1225 static void php_session_send_cookie(TSRMLS_D) /* {{{ */
1226 {
1227 smart_str ncookie = {0};
1228 char *date_fmt = NULL;
1229 char *e_session_name, *e_id;
1230
1231 if (SG(headers_sent)) {
1232 char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
1233 int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
1234
1235 if (output_start_filename) {
1236 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);
1237 } else {
1238 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cookie - headers already sent");
1239 }
1240 return;
1241 }
1242
1243 /* URL encode session_name and id because they might be user supplied */
1244 e_session_name = php_url_encode(PS(session_name), strlen(PS(session_name)), NULL);
1245 e_id = php_url_encode(PS(id), strlen(PS(id)), NULL);
1246
1247 smart_str_appends(&ncookie, COOKIE_SET_COOKIE);
1248 smart_str_appends(&ncookie, e_session_name);
1249 smart_str_appendc(&ncookie, '=');
1250 smart_str_appends(&ncookie, e_id);
1251
1252 efree(e_session_name);
1253 efree(e_id);
1254
1255 if (PS(cookie_lifetime) > 0) {
1256 struct timeval tv;
1257 time_t t;
1258
1259 gettimeofday(&tv, NULL);
1260 t = tv.tv_sec + PS(cookie_lifetime);
1261
1262 if (t > 0) {
1263 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);
1264 smart_str_appends(&ncookie, COOKIE_EXPIRES);
1265 smart_str_appends(&ncookie, date_fmt);
1266 efree(date_fmt);
1267 }
1268 }
1269
1270 if (PS(cookie_path)[0]) {
1271 smart_str_appends(&ncookie, COOKIE_PATH);
1272 smart_str_appends(&ncookie, PS(cookie_path));
1273 }
1274
1275 if (PS(cookie_domain)[0]) {
1276 smart_str_appends(&ncookie, COOKIE_DOMAIN);
1277 smart_str_appends(&ncookie, PS(cookie_domain));
1278 }
1279
1280 if (PS(cookie_secure)) {
1281 smart_str_appends(&ncookie, COOKIE_SECURE);
1282 }
1283
1284 if (PS(cookie_httponly)) {
1285 smart_str_appends(&ncookie, COOKIE_HTTPONLY);
1286 }
1287
1288 smart_str_0(&ncookie);
1289
1290 /* 'replace' must be 0 here, else a previous Set-Cookie
1291 header, probably sent with setcookie() will be replaced! */
1292 sapi_add_header_ex(ncookie.c, ncookie.len, 0, 0 TSRMLS_CC);
1293 }
1294 /* }}} */
1295
1296 PHPAPI ps_module *_php_find_ps_module(char *name TSRMLS_DC) /* {{{ */
1297 {
1298 ps_module *ret = NULL;
1299 ps_module **mod;
1300 int i;
1301
1302 for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
1303 if (*mod && !strcasecmp(name, (*mod)->s_name)) {
1304 ret = *mod;
1305 break;
1306 }
1307 }
1308 return ret;
1309 }
1310 /* }}} */
1311
1312 PHPAPI const ps_serializer *_php_find_ps_serializer(char *name TSRMLS_DC) /* {{{ */
1313 {
1314 const ps_serializer *ret = NULL;
1315 const ps_serializer *mod;
1316
1317 for (mod = ps_serializers; mod->name; mod++) {
1318 if (!strcasecmp(name, mod->name)) {
1319 ret = mod;
1320 break;
1321 }
1322 }
1323 return ret;
1324 }
1325 /* }}} */
1326
1327 #define PPID2SID \
1328 convert_to_string((*ppid)); \
1329 PS(id) = estrndup(Z_STRVAL_PP(ppid), Z_STRLEN_PP(ppid))
1330
1331 static void php_session_reset_id(TSRMLS_D) /* {{{ */
1332 {
1333 int module_number = PS(module_number);
1334
1335 if (PS(use_cookies) && PS(send_cookie)) {
1336 php_session_send_cookie(TSRMLS_C);
1337 PS(send_cookie) = 0;
1338 }
1339
1340 /* if the SID constant exists, destroy it. */
1341 zend_hash_del(EG(zend_constants), "sid", sizeof("sid"));
1342
1343 if (PS(define_sid)) {
1344 smart_str var = {0};
1345
1346 smart_str_appends(&var, PS(session_name));
1347 smart_str_appendc(&var, '=');
1348 smart_str_appends(&var, PS(id));
1349 smart_str_0(&var);
1350 REGISTER_STRINGL_CONSTANT("SID", var.c, var.len, 0);
1351 } else {
1352 REGISTER_STRINGL_CONSTANT("SID", STR_EMPTY_ALLOC(), 0, 0);
1353 }
1354
1355 if (PS(apply_trans_sid)) {
1356 php_url_scanner_reset_vars(TSRMLS_C);
1357 php_url_scanner_add_var(PS(session_name), strlen(PS(session_name)), PS(id), strlen(PS(id)), 1 TSRMLS_CC);
1358 }
1359 }
1360 /* }}} */
1361
1362 PHPAPI void php_session_start(TSRMLS_D) /* {{{ */
1363 {
1364 zval **ppid;
1365 zval **data;
1366 char *p, *value;
1367 int nrand;
1368 int lensess;
1369
1370 if (PS(use_only_cookies)) {
1371 PS(apply_trans_sid) = 0;
1372 } else {
1373 PS(apply_trans_sid) = PS(use_trans_sid);
1374 }
1375
1376 switch (PS(session_status)) {
1377 case php_session_active:
1378 php_error(E_NOTICE, "A session had already been started - ignoring session_start()");
1379 return;
1380 break;
1381
1382 case php_session_disabled:
1383 value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0);
1384 if (!PS(mod) && value) {
1385 PS(mod) = _php_find_ps_module(value TSRMLS_CC);
1386 if (!PS(mod)) {
1387 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find save handler '%s' - session startup failed", value);
1388 return;
1389 }
1390 }
1391 value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
1392 if (!PS(serializer) && value) {
1393 PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
1394 if (!PS(serializer)) {
1395 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find serialization handler '%s' - session startup failed", value);
1396 return;
1397 }
1398 }
1399 PS(session_status) = php_session_none;
1400 /* fallthrough */
1401
1402 default:
1403 case php_session_none:
1404 PS(define_sid) = 1;
1405 PS(send_cookie) = 1;
1406 }
1407
1408 lensess = strlen(PS(session_name));
1409
1410 /* Cookies are preferred, because initially
1411 * cookie and get variables will be available. */
1412
1413 if (!PS(id)) {
1414 if (PS(use_cookies) && zend_hash_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE"), (void **) &data) == SUCCESS &&
1415 Z_TYPE_PP(data) == IS_ARRAY &&
1416 zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
1417 ) {
1418 PPID2SID;
1419 PS(apply_trans_sid) = 0;
1420 PS(send_cookie) = 0;
1421 PS(define_sid) = 0;
1422 }
1423
1424 if (!PS(use_only_cookies) && !PS(id) &&
1425 zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void **) &data) == SUCCESS &&
1426 Z_TYPE_PP(data) == IS_ARRAY &&
1427 zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
1428 ) {
1429 PPID2SID;
1430 PS(send_cookie) = 0;
1431 }
1432
1433 if (!PS(use_only_cookies) && !PS(id) &&
1434 zend_hash_find(&EG(symbol_table), "_POST", sizeof("_POST"), (void **) &data) == SUCCESS &&
1435 Z_TYPE_PP(data) == IS_ARRAY &&
1436 zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
1437 ) {
1438 PPID2SID;
1439 PS(send_cookie) = 0;
1440 }
1441 }
1442
1443 /* Check the REQUEST_URI symbol for a string of the form
1444 * '<session-name>=<session-id>' to allow URLs of the form
1445 * http://yoursite/<session-name>=<session-id>/script.php */
1446
1447 if (!PS(use_only_cookies) && !PS(id) && PG(http_globals)[TRACK_VARS_SERVER] &&
1448 zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &data) == SUCCESS &&
1449 Z_TYPE_PP(data) == IS_STRING &&
1450 (p = strstr(Z_STRVAL_PP(data), PS(session_name))) &&
1451 p[lensess] == '='
1452 ) {
1453 char *q;
1454
1455 p += lensess + 1;
1456 if ((q = strpbrk(p, "/?\\"))) {
1457 PS(id) = estrndup(p, q - p);
1458 PS(send_cookie) = 0;
1459 }
1460 }
1461
1462 /* Check whether the current request was referred to by
1463 * an external site which invalidates the previously found id. */
1464
1465 if (PS(id) &&
1466 PS(extern_referer_chk)[0] != '\0' &&
1467 PG(http_globals)[TRACK_VARS_SERVER] &&
1468 zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER"), (void **) &data) == SUCCESS &&
1469 Z_TYPE_PP(data) == IS_STRING &&
1470 Z_STRLEN_PP(data) != 0 &&
1471 strstr(Z_STRVAL_PP(data), PS(extern_referer_chk)) == NULL
1472 ) {
1473 efree(PS(id));
1474 PS(id) = NULL;
1475 PS(send_cookie) = 1;
1476 if (PS(use_trans_sid) && !PS(use_only_cookies)) {
1477 PS(apply_trans_sid) = 1;
1478 }
1479 }
1480
1481 php_session_initialize(TSRMLS_C);
1482
1483 if (!PS(use_cookies) && PS(send_cookie)) {
1484 if (PS(use_trans_sid) && !PS(use_only_cookies)) {
1485 PS(apply_trans_sid) = 1;
1486 }
1487 PS(send_cookie) = 0;
1488 }
1489
1490 php_session_reset_id(TSRMLS_C);
1491
1492 PS(session_status) = php_session_active;
1493
1494 php_session_cache_limiter(TSRMLS_C);
1495
1496 if (PS(mod_data) && PS(gc_probability) > 0) {
1497 int nrdels = -1;
1498
1499 nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
1500 if (nrand < PS(gc_probability)) {
1501 PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC);
1502 #ifdef SESSION_DEBUG
1503 if (nrdels != -1) {
1504 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "purged %d expired session objects", nrdels);
1505 }
1506 #endif
1507 }
1508 }
1509 }
1510 /* }}} */
1511
1512 static void php_session_flush(TSRMLS_D) /* {{{ */
1513 {
1514 if (PS(session_status) == php_session_active) {
1515 PS(session_status) = php_session_none;
1516 php_session_save_current_state(TSRMLS_C);
1517 }
1518 }
1519 /* }}} */
1520
1521 PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t *newlen TSRMLS_DC) /* {{{ */
1522 {
1523 if (PS(apply_trans_sid) && (PS(session_status) == php_session_active)) {
1524 *new = php_url_scanner_adapt_single_url(url, urllen, PS(session_name), PS(id), newlen TSRMLS_CC);
1525 }
1526 }
1527 /* }}} */
1528
1529 /* ********************************
1530 * Userspace exported functions *
1531 ******************************** */
1532
1533 /* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])
1534 Set session cookie parameters */
1535 static PHP_FUNCTION(session_set_cookie_params)
1536 {
1537 zval **lifetime = NULL;
1538 char *path = NULL, *domain = NULL;
1539 int path_len, domain_len, argc = ZEND_NUM_ARGS();
1540 zend_bool secure = 0, httponly = 0;
1541
1542 if (!PS(use_cookies) ||
1543 zend_parse_parameters(argc TSRMLS_CC, "Z|ssbb", &lifetime, &path, &path_len, &domain, &domain_len, &secure, &httponly) == FAILURE) {
1544 return;
1545 }
1546
1547 convert_to_string_ex(lifetime);
1548
1549 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);
1550
1551 if (path) {
1552 zend_alter_ini_entry("session.cookie_path", sizeof("session.cookie_path"), path, path_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1553 }
1554 if (domain) {
1555 zend_alter_ini_entry("session.cookie_domain", sizeof("session.cookie_domain"), domain, domain_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1556 }
1557
1558 if (argc > 3) {
1559 zend_alter_ini_entry("session.cookie_secure", sizeof("session.cookie_secure"), secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1560 }
1561 if (argc > 4) {
1562 zend_alter_ini_entry("session.cookie_httponly", sizeof("session.cookie_httponly"), httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1563 }
1564 }
1565 /* }}} */
1566
1567 /* {{{ proto array session_get_cookie_params(void)
1568 Return the session cookie parameters */
1569 static PHP_FUNCTION(session_get_cookie_params)
1570 {
1571 if (zend_parse_parameters_none() == FAILURE) {
1572 return;
1573 }
1574
1575 array_init(return_value);
1576
1577 add_assoc_long(return_value, "lifetime", PS(cookie_lifetime));
1578 add_assoc_string(return_value, "path", PS(cookie_path), 1);
1579 add_assoc_string(return_value, "domain", PS(cookie_domain), 1);
1580 add_assoc_bool(return_value, "secure", PS(cookie_secure));
1581 add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
1582 }
1583 /* }}} */
1584
1585 /* {{{ proto string session_name([string newname])
1586 Return the current session name. If newname is given, the session name is replaced with newname */
1587 static PHP_FUNCTION(session_name)
1588 {
1589 char *name = NULL;
1590 int name_len;
1591
1592 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
1593 return;
1594 }
1595
1596 RETVAL_STRING(PS(session_name), 1);
1597
1598 if (name) {
1599 zend_alter_ini_entry("session.name", sizeof("session.name"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1600 }
1601 }
1602 /* }}} */
1603
1604 /* {{{ proto string session_module_name([string newname])
1605 Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname */
1606 static PHP_FUNCTION(session_module_name)
1607 {
1608 char *name = NULL;
1609 int name_len;
1610
1611 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
1612 return;
1613 }
1614
1615 /* Set return_value to current module name */
1616 if (PS(mod) && PS(mod)->s_name) {
1617 RETVAL_STRING(safe_estrdup(PS(mod)->s_name), 0);
1618 } else {
1619 RETVAL_EMPTY_STRING();
1620 }
1621
1622 if (name) {
1623 if (!_php_find_ps_module(name TSRMLS_CC)) {
1624 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find named PHP session module (%s)", name);
1625
1626 zval_dtor(return_value);
1627 RETURN_FALSE;
1628 }
1629 if (PS(mod_data)) {
1630 PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
1631 }
1632 PS(mod_data) = NULL;
1633
1634 zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1635 }
1636 }
1637 /* }}} */
1638
1639 /* {{{ proto void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc)
1640 Sets user-level functions */
1641 static PHP_FUNCTION(session_set_save_handler)
1642 {
1643 zval ***args = NULL;
1644 int i, num_args, argc = ZEND_NUM_ARGS();
1645 char *name;
1646
1647 if (PS(session_status) != php_session_none) {
1648 RETURN_FALSE;
1649 }
1650
1651 if (argc != 6) {
1652 WRONG_PARAM_COUNT;
1653 }
1654
1655 if (zend_parse_parameters(argc TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1656 return;
1657 }
1658
1659 for (i = 0; i < 6; i++) {
1660 if (!zend_is_callable(*args[i], 0, &name TSRMLS_CC)) {
1661 efree(args);
1662 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument %d is not a valid callback", i+1);
1663 efree(name);
1664 RETURN_FALSE;
1665 }
1666 efree(name);
1667 }
1668
1669 zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1670
1671 for (i = 0; i < 6; i++) {
1672 if (PS(mod_user_names).names[i] != NULL) {
1673 zval_ptr_dtor(&PS(mod_user_names).names[i]);
1674 }
1675 Z_ADDREF_PP(args[i]);
1676 PS(mod_user_names).names[i] = *args[i];
1677 }
1678
1679 efree(args);
1680 RETURN_TRUE;
1681 }
1682 /* }}} */
1683
1684 /* {{{ proto string session_save_path([string newname])
1685 Return the current save path passed to module_name. If newname is given, the save path is replaced with newname */
1686 static PHP_FUNCTION(session_save_path)
1687 {
1688 char *name = NULL;
1689 int name_len;
1690
1691 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
1692 return;
1693 }
1694
1695 RETVAL_STRING(PS(save_path), 1);
1696
1697 if (name) {
1698 if (memchr(name, '\0', name_len) != NULL) {
1699 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The save_path cannot contain NULL characters");
1700 zval_dtor(return_value);
1701 RETURN_FALSE;
1702 }
1703 zend_alter_ini_entry("session.save_path", sizeof("session.save_path"), name, name_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1704 }
1705 }
1706 /* }}} */
1707
1708 /* {{{ proto string session_id([string newid])
1709 Return the current session id. If newid is given, the session id is replaced with newid */
1710 static PHP_FUNCTION(session_id)
1711 {
1712 char *name = NULL;
1713 int name_len;
1714
1715 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) {
1716 return;
1717 }
1718
1719 if (PS(id)) {
1720 RETVAL_STRING(PS(id), 1);
1721 } else {
1722 RETVAL_EMPTY_STRING();
1723 }
1724
1725 if (name) {
1726 if (PS(id)) {
1727 efree(PS(id));
1728 }
1729 PS(id) = estrndup(name, name_len);
1730 }
1731 }
1732 /* }}} */
1733
1734 /* {{{ proto bool session_regenerate_id([bool delete_old_session])
1735 Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session. */
1736 static PHP_FUNCTION(session_regenerate_id)
1737 {
1738 zend_bool del_ses = 0;
1739
1740 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &del_ses) == FAILURE) {
1741 return;
1742 }
1743
1744 if (SG(headers_sent) && PS(use_cookies)) {
1745 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot regenerate session id - headers already sent");
1746 RETURN_FALSE;
1747 }
1748
1749 if (PS(session_status) == php_session_active) {
1750 if (PS(id)) {
1751 if (del_ses && PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) {
1752 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session object destruction failed");
1753 RETURN_FALSE;
1754 }
1755 efree(PS(id));
1756 PS(id) = NULL;
1757 }
1758
1759 PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC);
1760
1761 PS(send_cookie) = 1;
1762 php_session_reset_id(TSRMLS_C);
1763
1764 RETURN_TRUE;
1765 }
1766 RETURN_FALSE;
1767 }
1768 /* }}} */
1769
1770 /* {{{ proto string session_cache_limiter([string new_cache_limiter])
1771 Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter */
1772 static PHP_FUNCTION(session_cache_limiter)
1773 {
1774 char *limiter = NULL;
1775 int limiter_len;
1776
1777 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &limiter, &limiter_len) == FAILURE) {
1778 return;
1779 }
1780
1781 RETVAL_STRING(PS(cache_limiter), 1);
1782
1783 if (limiter) {
1784 zend_alter_ini_entry("session.cache_limiter", sizeof("session.cache_limiter"), limiter, limiter_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
1785 }
1786 }
1787 /* }}} */
1788
1789 /* {{{ proto int session_cache_expire([int new_cache_expire])
1790 Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire */
1791 static PHP_FUNCTION(session_cache_expire)
1792 {
1793 zval **expires = NULL;
1794 int argc = ZEND_NUM_ARGS();
1795
1796 if (zend_parse_parameters(argc TSRMLS_CC, "|Z", &expires) == FAILURE) {
1797 return;
1798 }
1799
1800 RETVAL_LONG(PS(cache_expire));
1801
1802 if (argc == 1) {
1803 convert_to_string_ex(expires);
1804 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);
1805 }
1806 }
1807 /* }}} */
1808
1809 /* {{{ static void php_register_var(zval** entry TSRMLS_DC) */
1810 static void php_register_var(zval** entry TSRMLS_DC)
1811 {
1812 zval **value;
1813
1814 if (Z_TYPE_PP(entry) == IS_ARRAY) {
1815 if (Z_ARRVAL_PP(entry)->nApplyCount > 1) {
1816 return;
1817 }
1818
1819 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(entry));
1820 Z_ARRVAL_PP(entry)->nApplyCount++;
1821
1822 while (zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void**)&value) == SUCCESS) {
1823 php_register_var(value TSRMLS_CC);
1824 zend_hash_move_forward(Z_ARRVAL_PP(entry));
1825 }
1826
1827 Z_ARRVAL_PP(entry)->nApplyCount--;
1828 } else {
1829 convert_to_string_ex(entry);
1830
1831 if ((strcmp(Z_STRVAL_PP(entry), "HTTP_SESSION_VARS") != 0) &&
1832 (strcmp(Z_STRVAL_PP(entry), "_SESSION") != 0)
1833 ) {
1834 PS_ADD_VARL(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry));
1835 }
1836 }
1837 }
1838 /* }}} */
1839
1840 /* {{{ proto string session_encode(void)
1841 Serializes the current setup and returns the serialized representation */
1842 static PHP_FUNCTION(session_encode)
1843 {
1844 int len;
1845 char *enc;
1846
1847 if (zend_parse_parameters_none() == FAILURE) {
1848 return;
1849 }
1850
1851 enc = php_session_encode(&len TSRMLS_CC);
1852 if (enc == NULL) {
1853 RETURN_FALSE;
1854 }
1855
1856 RETVAL_STRINGL(enc, len, 0);
1857 }
1858 /* }}} */
1859
1860 /* {{{ proto bool session_decode(string data)
1861 Deserializes data and reinitializes the variables */
1862 static PHP_FUNCTION(session_decode)
1863 {
1864 char *str;
1865 int str_len;
1866
1867 if (PS(session_status) == php_session_none) {
1868 RETURN_FALSE;
1869 }
1870
1871 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
1872 return;
1873 }
1874
1875 php_session_decode(str, str_len TSRMLS_CC);
1876
1877 RETURN_TRUE;
1878 }
1879 /* }}} */
1880
1881 /* {{{ proto bool session_start(void)
1882 Begin session - reinitializes freezed variables, registers browsers etc */
1883 static PHP_FUNCTION(session_start)
1884 {
1885 /* skipping check for non-zero args for performance reasons here ?*/
1886 php_session_start(TSRMLS_C);
1887
1888 if (PS(session_status) != php_session_active) {
1889 RETURN_FALSE;
1890 }
1891 RETURN_TRUE;
1892 }
1893 /* }}} */
1894
1895 /* {{{ proto bool session_destroy(void)
1896 Destroy the current session and all data associated with it */
1897 static PHP_FUNCTION(session_destroy)
1898 {
1899 if (zend_parse_parameters_none() == FAILURE) {
1900 return;
1901 }
1902
1903 RETURN_BOOL(php_session_destroy(TSRMLS_C) == SUCCESS);
1904 }
1905 /* }}} */
1906
1907 /* {{{ proto void session_unset(void)
1908 Unset all registered variables */
1909 static PHP_FUNCTION(session_unset)
1910 {
1911 if (PS(session_status) == php_session_none) {
1912 RETURN_FALSE;
1913 }
1914
1915 IF_SESSION_VARS() {
1916 HashTable *ht_sess_var;
1917
1918 SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars));
1919 ht_sess_var = Z_ARRVAL_P(PS(http_session_vars));
1920
1921 if (PG(register_globals)) {
1922 uint str_len;
1923 char *str;
1924 ulong num_key;
1925 HashPosition pos;
1926
1927 zend_hash_internal_pointer_reset_ex(ht_sess_var, &pos);
1928
1929 while (zend_hash_get_current_key_ex(ht_sess_var, &str, &str_len, &num_key, 0, &pos) == HASH_KEY_IS_STRING) {
1930 zend_delete_global_variable(str, str_len - 1 TSRMLS_CC);
1931 zend_hash_move_forward_ex(ht_sess_var, &pos);
1932 }
1933 }
1934
1935 /* Clean $_SESSION. */
1936 zend_hash_clean(ht_sess_var);
1937 }
1938 }
1939 /* }}} */
1940
1941 /* {{{ proto void session_write_close(void)
1942 Write session data and end session */
1943 static PHP_FUNCTION(session_write_close)
1944 {
1945 php_session_flush(TSRMLS_C);
1946 }
1947 /* }}} */
1948
1949 /* {{{ proto bool session_register(mixed var_names [, mixed ...])
1950 Adds varname(s) to the list of variables which are freezed at the session end */
1951 static PHP_FUNCTION(session_register)
1952 {
1953 zval ***args = NULL;
1954 int num_args, i;
1955
1956 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1957 return;
1958 }
1959
1960 if (PS(session_status) == php_session_none || PS(session_status) == php_session_disabled) {
1961 php_session_start(TSRMLS_C);
1962 }
1963
1964 if (PS(session_status) == php_session_disabled) {
1965 if (args) {
1966 efree(args);
1967 }
1968 RETURN_FALSE;
1969 }
1970
1971 for (i = 0; i < num_args; i++) {
1972 if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
1973 SEPARATE_ZVAL(args[i]);
1974 }
1975 php_register_var(args[i] TSRMLS_CC);
1976 }
1977
1978 if (args) {
1979 efree(args);
1980 }
1981
1982 RETURN_TRUE;
1983 }
1984 /* }}} */
1985
1986 /* {{{ proto bool session_unregister(string varname)
1987 Removes varname from the list of variables which are freezed at the session end */
1988 static PHP_FUNCTION(session_unregister)
1989 {
1990 char *p_name;
1991 int p_name_len;
1992
1993 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) {
1994 return;
1995 }
1996
1997 IF_SESSION_VARS() {
1998 SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars));
1999 PS_DEL_VARL(p_name, p_name_len);
2000 }
2001
2002 RETURN_TRUE;
2003 }
2004 /* }}} */
2005
2006 /* {{{ proto bool session_is_registered(string varname)
2007 Checks if a variable is registered in session */
2008 static PHP_FUNCTION(session_is_registered)
2009 {
2010 zval *p_var;
2011 char *p_name;
2012 int p_name_len;
2013
2014 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) {
2015 return;
2016 }
2017
2018 if (PS(session_status) == php_session_none) {
2019 RETURN_FALSE;
2020 }
2021
2022 IF_SESSION_VARS() {
2023 if (zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), p_name, p_name_len+1, (void **)&p_var) == SUCCESS) {
2024 RETURN_TRUE;
2025 }
2026 }
2027 RETURN_FALSE;
2028 }
2029 /* }}} */
2030
2031 /* {{{ arginfo */
2032 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_name, 0, 0, 0)
2033 ZEND_ARG_INFO(0, name)
2034 ZEND_END_ARG_INFO()
2035
2036 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_module_name, 0, 0, 0)
2037 ZEND_ARG_INFO(0, module)
2038 ZEND_END_ARG_INFO()
2039
2040 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_save_path, 0, 0, 0)
2041 ZEND_ARG_INFO(0, path)
2042 ZEND_END_ARG_INFO()
2043
2044 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_id, 0, 0, 0)
2045 ZEND_ARG_INFO(0, id)
2046 ZEND_END_ARG_INFO()
2047
2048 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_regenerate_id, 0, 0, 0)
2049 ZEND_ARG_INFO(0, delete_old_session)
2050 ZEND_END_ARG_INFO()
2051
2052 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_decode, 0, 0, 1)
2053 ZEND_ARG_INFO(0, data)
2054 ZEND_END_ARG_INFO()
2055
2056 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_register, 0, 0, 1)
2057 ZEND_ARG_INFO(0, name)
2058 ZEND_ARG_INFO(0, ...)
2059 ZEND_END_ARG_INFO()
2060
2061 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_unregister, 0, 0, 1)
2062 ZEND_ARG_INFO(0, name)
2063 ZEND_END_ARG_INFO()
2064
2065 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_is_registered, 0, 0, 1)
2066 ZEND_ARG_INFO(0, name)
2067 ZEND_END_ARG_INFO()
2068
2069 ZEND_BEGIN_ARG_INFO(arginfo_session_void, 0)
2070 ZEND_END_ARG_INFO()
2071
2072 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_save_handler, 0, 0, 6)
2073 ZEND_ARG_INFO(0, open)
2074 ZEND_ARG_INFO(0, close)
2075 ZEND_ARG_INFO(0, read)
2076 ZEND_ARG_INFO(0, write)
2077 ZEND_ARG_INFO(0, destroy)
2078 ZEND_ARG_INFO(0, gc)
2079 ZEND_END_ARG_INFO()
2080
2081 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_limiter, 0, 0, 0)
2082 ZEND_ARG_INFO(0, cache_limiter)
2083 ZEND_END_ARG_INFO()
2084
2085 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_expire, 0, 0, 0)
2086 ZEND_ARG_INFO(0, new_cache_expire)
2087 ZEND_END_ARG_INFO()
2088
2089 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_cookie_params, 0, 0, 1)
2090 ZEND_ARG_INFO(0, lifetime)
2091 ZEND_ARG_INFO(0, path)
2092 ZEND_ARG_INFO(0, domain)
2093 ZEND_ARG_INFO(0, secure)
2094 ZEND_ARG_INFO(0, httponly)
2095 ZEND_END_ARG_INFO()
2096 /* }}} */
2097
2098 /* {{{ session_functions[]
2099 */
2100 static const zend_function_entry session_functions[] = {
2101 PHP_FE(session_name, arginfo_session_name)
2102 PHP_FE(session_module_name, arginfo_session_module_name)
2103 PHP_FE(session_save_path, arginfo_session_save_path)
2104 PHP_FE(session_id, arginfo_session_id)
2105 PHP_FE(session_regenerate_id, arginfo_session_regenerate_id)
2106 PHP_FE(session_decode, arginfo_session_decode)
2107 PHP_DEP_FE(session_register, arginfo_session_register)
2108 PHP_DEP_FE(session_unregister, arginfo_session_unregister)
2109 PHP_DEP_FE(session_is_registered, arginfo_session_is_registered)
2110 PHP_FE(session_encode, arginfo_session_void)
2111 PHP_FE(session_start, arginfo_session_void)
2112 PHP_FE(session_destroy, arginfo_session_void)
2113 PHP_FE(session_unset, arginfo_session_void)
2114 PHP_FE(session_set_save_handler, arginfo_session_set_save_handler)
2115 PHP_FE(session_cache_limiter, arginfo_session_cache_limiter)
2116 PHP_FE(session_cache_expire, arginfo_session_cache_expire)
2117 PHP_FE(session_set_cookie_params, arginfo_session_set_cookie_params)
2118 PHP_FE(session_get_cookie_params, arginfo_session_void)
2119 PHP_FE(session_write_close, arginfo_session_void)
2120 PHP_FALIAS(session_commit, session_write_close, arginfo_session_void)
2121 PHP_FE_END
2122 };
2123 /* }}} */
2124
2125 /* ********************************
2126 * Module Setup and Destruction *
2127 ******************************** */
2128
2129 static PHP_RINIT_FUNCTION(session) /* {{{ */
2130 {
2131 php_rinit_session_globals(TSRMLS_C);
2132
2133 if (PS(mod) == NULL) {
2134 char *value;
2135
2136 value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0);
2137 if (value) {
2138 PS(mod) = _php_find_ps_module(value TSRMLS_CC);
2139 }
2140 }
2141
2142 if (PS(serializer) == NULL) {
2143 char *value;
2144
2145 value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
2146 if (value) {
2147 PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
2148 }
2149 }
2150
2151 if (PS(mod) == NULL || PS(serializer) == NULL) {
2152 /* current status is unusable */
2153 PS(session_status) = php_session_disabled;
2154 return SUCCESS;
2155 }
2156
2157 if (PS(auto_start)) {
2158 php_session_start(TSRMLS_C);
2159 }
2160
2161 return SUCCESS;
2162 }
2163 /* }}} */
2164
2165 static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */
2166 {
2167 int i;
2168
2169 zend_try {
2170 php_session_flush(TSRMLS_C);
2171 } zend_end_try();
2172 php_rshutdown_session_globals(TSRMLS_C);
2173
2174 /* this should NOT be done in php_rshutdown_session_globals() */
2175 for (i = 0; i < 6; i++) {
2176 if (PS(mod_user_names).names[i] != NULL) {
2177 zval_ptr_dtor(&PS(mod_user_names).names[i]);
2178 PS(mod_user_names).names[i] = NULL;
2179 }
2180 }
2181
2182 return SUCCESS;
2183 }
2184 /* }}} */
2185
2186 static PHP_GINIT_FUNCTION(ps) /* {{{ */
2187 {
2188 int i;
2189
2190 ps_globals->save_path = NULL;
2191 ps_globals->session_name = NULL;
2192 ps_globals->id = NULL;
2193 ps_globals->mod = NULL;
2194 ps_globals->serializer = NULL;
2195 ps_globals->mod_data = NULL;
2196 ps_globals->session_status = php_session_none;
2197 for (i = 0; i < 6; i++) {
2198 ps_globals->mod_user_names.names[i] = NULL;
2199 }
2200 ps_globals->http_session_vars = NULL;
2201 }
2202 /* }}} */
2203
2204 static PHP_MINIT_FUNCTION(session) /* {{{ */
2205 {
2206 zend_register_auto_global("_SESSION", sizeof("_SESSION")-1, NULL TSRMLS_CC);
2207
2208 PS(module_number) = module_number; /* if we really need this var we need to init it in zts mode as well! */
2209
2210 PS(session_status) = php_session_none;
2211 REGISTER_INI_ENTRIES();
2212
2213 #ifdef HAVE_LIBMM
2214 PHP_MINIT(ps_mm) (INIT_FUNC_ARGS_PASSTHRU);
2215 #endif
2216 return SUCCESS;
2217 }
2218 /* }}} */
2219
2220 static PHP_MSHUTDOWN_FUNCTION(session) /* {{{ */
2221 {
2222 UNREGISTER_INI_ENTRIES();
2223
2224 #ifdef HAVE_LIBMM
2225 PHP_MSHUTDOWN(ps_mm) (SHUTDOWN_FUNC_ARGS_PASSTHRU);
2226 #endif
2227
2228 ps_serializers[PREDEFINED_SERIALIZERS].name = NULL;
2229 memset(&ps_modules[PREDEFINED_MODULES], 0, (MAX_MODULES-PREDEFINED_MODULES)*sizeof(ps_module *));
2230
2231 return SUCCESS;
2232 }
2233 /* }}} */
2234
2235 static PHP_MINFO_FUNCTION(session) /* {{{ */
2236 {
2237 ps_module **mod;
2238 ps_serializer *ser;
2239 smart_str save_handlers = {0};
2240 smart_str ser_handlers = {0};
2241 int i;
2242
2243 /* Get save handlers */
2244 for (i = 0, mod = ps_modules; i < MAX_MODULES; i++, mod++) {
2245 if (*mod && (*mod)->s_name) {
2246 smart_str_appends(&save_handlers, (*mod)->s_name);
2247 smart_str_appendc(&save_handlers, ' ');
2248 }
2249 }
2250
2251 /* Get serializer handlers */
2252 for (i = 0, ser = ps_serializers; i < MAX_SERIALIZERS; i++, ser++) {
2253 if (ser && ser->name) {
2254 smart_str_appends(&ser_handlers, ser->name);
2255 smart_str_appendc(&ser_handlers, ' ');
2256 }
2257 }
2258
2259 php_info_print_table_start();
2260 php_info_print_table_row(2, "Session Support", "enabled" );
2261
2262 if (save_handlers.c) {
2263 smart_str_0(&save_handlers);
2264 php_info_print_table_row(2, "Registered save handlers", save_handlers.c);
2265 smart_str_free(&save_handlers);
2266 } else {
2267 php_info_print_table_row(2, "Registered save handlers", "none");
2268 }
2269
2270 if (ser_handlers.c) {
2271 smart_str_0(&ser_handlers);
2272 php_info_print_table_row(2, "Registered serializer handlers", ser_handlers.c);
2273 smart_str_free(&ser_handlers);
2274 } else {
2275 php_info_print_table_row(2, "Registered serializer handlers", "none");
2276 }
2277
2278 php_info_print_table_end();
2279
2280 DISPLAY_INI_ENTRIES();
2281 }
2282 /* }}} */
2283
2284 static const zend_module_dep session_deps[] = { /* {{{ */
2285 ZEND_MOD_OPTIONAL("hash")
2286 ZEND_MOD_REQUIRED("spl")
2287 ZEND_MOD_END
2288 };
2289 /* }}} */
2290
2291 zend_module_entry session_module_entry = {
2292 STANDARD_MODULE_HEADER_EX,
2293 NULL,
2294 session_deps,
2295 "session",
2296 session_functions,
2297 PHP_MINIT(session), PHP_MSHUTDOWN(session),
2298 PHP_RINIT(session), PHP_RSHUTDOWN(session),
2299 PHP_MINFO(session),
2300 NO_VERSION_YET,
2301 PHP_MODULE_GLOBALS(ps),
2302 PHP_GINIT(ps),
2303 NULL,
2304 NULL,
2305 STANDARD_MODULE_PROPERTIES_EX
2306 };
2307
2308 #ifdef COMPILE_DL_SESSION
2309 ZEND_GET_MODULE(session)
2310 #endif
2311
2312 /*
2313 * Local variables:
2314 * tab-width: 4
2315 * c-basic-offset: 4
2316 * End:
2317 * vim600: noet sw=4 ts=4 fdm=marker
2318 * vim<600: sw=4 ts=4
2319 */
2320