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