1: <?php
2: /**
3: * This file contains the the backend and frontend session class.
4: *
5: * @package Core
6: * @subpackage Session
7: * @author Frederic Schneider
8: * @copyright four for business AG <www.4fb.de>
9: * @license http://www.contenido.org/license/LIZENZ.txt
10: * @link http://www.4fb.de
11: * @link http://www.contenido.org
12: */
13:
14: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
15:
16: /**
17: * Backend session class.
18: *
19: * @package Core
20: * @subpackage Session
21: */
22: class cSession {
23:
24: /**
25: * Saves the registered variables
26: *
27: * @var array
28: */
29: protected $_pt;
30:
31: /**
32: * The prefix for the session variables
33: *
34: * @var string
35: */
36: protected $_prefix;
37:
38: /**
39: * Placeholder.
40: * This variable isn't needed to make sessions work any longer
41: * but some CONTENIDO functions/classes rely on it
42: *
43: * @var string
44: */
45: public $id;
46:
47: /**
48: * Placeholder.
49: * This variable isn't needed to make sessions work any longer
50: * but some CONTENIDO functions/classes rely on it
51: *
52: * @var string
53: */
54: public $name;
55:
56: /**
57: * cSession constructor. Starts a session if it does not yet exist.
58: *
59: * Session cookies will be created with these parameters:
60: *
61: * The session cookie will have a lifetime of 0 which means "until the browser is closed".
62: *
63: * It will be valid for the host name of the server which generated the cookie
64: * and the path as in either the configured backend or frontend URL.
65: *
66: * @since CON-2785 the cookie path can be configured as $cfg['cookie']['path'].
67: * Configure in <CLIENT>/data/config/<ENV>/config.local.php
68: *
69: * @since CON-2423 Via $cfg['secure'] you can define if the cookie should only be sent over secure connections.
70: * Configure in data/config/<ENV>/config.misc.php
71: *
72: * The session cookie is accessible only through the HTTP protocol.
73: *
74: * @param string $prefix [optional] The prefix for the session variables
75: */
76: public function __construct($prefix = 'backend')
77: {
78: $this->_pt = [];
79: $this->_prefix = $prefix;
80: $this->name = 'contenido';
81:
82: if (isset($_SESSION)) {
83: return;
84: }
85:
86: // determine cookie lifetime
87: $lifetime = 0;
88:
89: // determine cookie path (entire domain if path could not be determined)
90: $url = 'backend' === $prefix ? cRegistry::getBackendUrl() : cRegistry::getFrontendUrl();
91: $path = parse_url($url, PHP_URL_PATH);
92: $path = cRegistry::getConfigValue('cookie', 'path', $path);
93: if (empty($path)) {
94: $path = '/';
95: }
96:
97: // determine cookie domain
98: $domain = null;
99:
100: // determine cookie security flag
101: $secure = cRegistry::getConfigValue('secure');
102:
103: // determine cookie httponly flag
104: $httponly = true;
105:
106: session_set_cookie_params($lifetime, $path, $domain, $secure, $httponly);
107: session_name($this->_prefix);
108: session_start();
109:
110: $this->id = session_id();
111: }
112:
113: /**
114: * Registers a global variable which will become persistent
115: *
116: * @param string $things
117: * The name of the variable (e.g. "idclient")
118: */
119: public function register($things) {
120: $things = explode(',', $things);
121:
122: foreach ($things as $thing) {
123: $thing = trim($thing);
124: if ($thing) {
125: $this->_pt[$thing] = true;
126: }
127: }
128: }
129:
130: /**
131: * Unregisters a variable
132: *
133: * @param string $name
134: * The name of the variable (e.g. "idclient")
135: */
136: public function unregister($name) {
137: $this->_pt[$name] = false;
138: }
139:
140: /**
141: * Checks if a variable is registered
142: *
143: * @param string $name
144: * The name of the variable (e.g. "idclient")
145: * @return bool
146: */
147: public function isRegistered($name) {
148: if (isset($this->_pt[$name]) && $this->_pt[$name] == true) {
149: return true;
150: }
151: return false;
152: }
153:
154: /**
155: * Attaches "&contenido=sessionid" at the end of the URL.
156: * This is no longer needed to make sessions work but some CONTENIDO
157: * functions/classes rely on it
158: *
159: * @param string $url
160: * a URL
161: * @return mixed
162: */
163: public function url($url) {
164: // Remove existing session info from url
165: $url = preg_replace('/([&?])' . quotemeta(urlencode($this->name)) . '=1(&|$)/', "\\1", $url);
166:
167: // Remove trailing ?/& if needed
168: $url = preg_replace('/[&?]+$/', '', $url);
169:
170: if (!preg_match('~\b' . quotemeta(urlencode($this->name)) . '=[a-zA-Z0-9]*\b~', $url)) {
171: $url .= (cString::findFirstPos($url, '?') != false? '&' : '?') . urlencode($this->name) . '=' . $this->id;
172: }
173:
174: // Encode naughty characters in the URL
175: $url = str_replace(['<', '>', ' ', '"', '\''], ['%3C', '%3E', '+', '%22', '%27',], $url);
176:
177: return $url;
178: }
179:
180: /**
181: * Attaches "&contenido=1" at the end of the current URL.
182: * This is no longer needed to make sessions work but some CONTENIDO
183: * functions/classes rely on it
184: *
185: * @return mixed
186: */
187: public function selfURL() {
188: return $this->url($_SERVER['REQUEST_URI'] . ((isset($_SERVER['QUERY_STRING']) && ('' != $_SERVER['QUERY_STRING'])) ? '?' . $_SERVER['QUERY_STRING'] : ''));
189: }
190:
191: /**
192: * Returns PHP code which can be used to rebuild the variable by evaluating
193: * it.
194: * This will work recursevly on arrays
195: *
196: * @param mixed $var
197: * A variable which should get serialized.
198: * @return string
199: * the PHP code which can be evaluated.
200: */
201: public function serialize($var) {
202: $str = "";
203: $this->_rSerialize($var, $str);
204: return $str;
205: }
206:
207: /**
208: * This function will go recursevly through arrays and objects to serialize
209: * them.
210: *
211: * @param mixed $var
212: * The variable
213: * @param string $str
214: * The PHP code will be attached to this string
215: */
216: protected function _rSerialize($var, &$str) {
217: static $t, $l, $k;
218:
219: // Determine the type of $$var
220: eval("\$t = gettype(\$$var);");
221: switch ($t) {
222: case 'array':
223: // $$var is an array. Enumerate the elements and serialize them.
224: $str .= "\$$var = array(); ";
225: eval("\$l = array(); foreach(\$$var as \$k => \$v) {\$l[] = array(\$k,gettype(\$k),\$v);}");
226: foreach ($l as $item) {
227: // Structural recursion
228: $this->_rSerialize($var . "['" . preg_replace("/([\\'])/", "\\\\1", $item[0]) . "']", $str);
229: }
230: break;
231: case 'object':
232: // $$var is an object. Enumerate the slots and serialize them.
233: eval("\$k = \$${var}->classname; \$l = reset(\$${var}->persistent_slots);");
234: $str .= "\$$var = new $k; ";
235: while ($l) {
236: // Structural recursion.
237: $this->_rSerialize($var . "->" . $l, $str);
238: eval("\$l = next(\$${var}->persistent_slots);");
239: }
240: break;
241: default:
242: // $$var is an atom. Extract it to $l, then generate code.
243: eval("\$l = \$$var;");
244: $str .= "\$$var = '" . preg_replace("/([\\'])/", "\\\\1", $l) . "'; ";
245: break;
246: }
247: }
248:
249: /**
250: * Stores the session using PHP's own session implementation
251: */
252: public function freeze() {
253: $str = $this->serialize("this->_pt");
254:
255: foreach ($this->_pt as $thing => $value) {
256: $thing = trim($thing);
257: if ($value) {
258: $str .= $this->serialize("GLOBALS['" . $thing . "']");
259: }
260: }
261:
262: $_SESSION[$this->_prefix . 'csession'] = $str;
263: }
264:
265: /**
266: * Rebuilds every registered variable from the session.
267: */
268: public function thaw() {
269: if (isset($_SESSION[$this->_prefix . 'csession']) && $_SESSION[$this->_prefix . 'csession'] != '') {
270: eval(sprintf(';%s', $_SESSION[$this->_prefix . 'csession']));
271: }
272: }
273:
274: /**
275: * Deletes the session by calling session_destroy()
276: */
277: public function delete() {
278: $params = session_get_cookie_params();
279: setcookie(session_name(), '', time() - 600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
280:
281: session_destroy();
282: }
283:
284: /**
285: * Starts the session and rebuilds the variables
286: */
287: public function start() {
288: $this->thaw();
289: }
290: }
291:
292: /**
293: * Session class for the frontend.
294: * It uses a different prefix. The rest is the
295: * same
296: *
297: * @package Core
298: * @subpackage Session
299: */
300: class cFrontendSession extends cSession {
301: /**
302: * cFrontendSession constructor. Starts a session if it does not yet exist.
303: *
304: * Session cookies will be created with these parameters:
305: *
306: * The session cookie will have a lifetime of 0 which means "until the browser is closed".
307: *
308: * It will be valid for the host name of the server which generated the cookie
309: * and the path as in the configured frontend URL.
310: *
311: * @since CON-2785 the cookie path can be configured as $cfg['cookie']['path'].
312: * Configure in <CLIENT>/data/config/<ENV>/config.local.php
313: *
314: * @since CON-2423 Via $cfg['secure'] you can define if the cookie should only be sent over secure connections.
315: * Configure in data/config/<ENV>/config.misc.php
316: *
317: * The session cookie is accessible only through the HTTP protocol.
318: *
319: * @param string $prefix [optional] The prefix for the session variables
320: */
321: public function __construct() {
322: $client = cRegistry::getClientId();
323:
324: parent::__construct($client . "frontend");
325: }
326:
327: /**
328: * This function overrides cSession::url() so that the contenido=1 isn't
329: * attached to the URL for the frontend
330: *
331: * @see cSession::url()
332: * @param string $url
333: * a URL
334: * @return mixed
335: */
336: public function url($url) {
337: // Remove existing session info from url
338: $url = preg_replace('/([&?])' . quotemeta(urlencode($this->name)) . '=' . $this->id . '(&|$)/', "\\1", $url);
339:
340: // Remove trailing ?/& if needed
341: $url = preg_replace('/[&?]+$/', '', $url);
342:
343: // Encode naughty characters in the URL
344: $url = str_replace(['<', '>', ' ', '"', '\''], ['%3C', '%3E', '+', '%22', '%27'], $url);
345:
346: return $url;
347: }
348: }
349: