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