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