Overview

Packages

  • Auth
  • Core
  • Horde
    • Imsp
  • None
  • Notification

Classes

  • Horde
  • Horde_Config
  • Horde_Config_Form
  • Horde_Core_ActiveSync_Connector
  • Horde_Core_ActiveSync_Driver
  • Horde_Core_Ajax_Application
  • Horde_Core_Ajax_Imple
  • Horde_Core_Ajax_Imple_AutoCompleter
  • Horde_Core_Ajax_Imple_Geocoder_Geonames
  • Horde_Core_Ajax_Imple_SpellChecker
  • Horde_Core_Alarm_Handler_Notify
  • Horde_Core_Auth_Application
  • Horde_Core_Auth_Composite
  • Horde_Core_Auth_Ldap
  • Horde_Core_Auth_Msad
  • Horde_Core_Auth_Shibboleth
  • Horde_Core_Auth_Signup_Base
  • Horde_Core_Auth_Signup_Form
  • Horde_Core_Auth_Signup_Null
  • Horde_Core_Auth_Signup_Sql
  • Horde_Core_Auth_Signup_SqlObject
  • Horde_Core_Autoloader_Callback_Mime
  • Horde_Core_Autoloader_Callback_Nls
  • Horde_Core_Block
  • Horde_Core_Block_Collection
  • Horde_Core_Block_Layout
  • Horde_Core_Block_Layout_Manager
  • Horde_Core_Block_Layout_View
  • Horde_Core_Block_Upgrade
  • Horde_Core_Browser
  • Horde_Core_Bundle
  • Horde_Core_Cli
  • Horde_Core_Controller_NotFound
  • Horde_Core_Controller_RequestConfiguration
  • Horde_Core_Controller_RequestMapper
  • Horde_Core_Controller_SettingsFinder
  • Horde_Core_Db_Migration
  • Horde_Core_Factory_ActiveSyncBackend
  • Horde_Core_Factory_ActiveSyncServer
  • Horde_Core_Factory_ActiveSyncState
  • Horde_Core_Factory_Ajax
  • Horde_Core_Factory_Alarm
  • Horde_Core_Factory_Auth
  • Horde_Core_Factory_AuthSignup
  • Horde_Core_Factory_Base
  • Horde_Core_Factory_BlockCollection
  • Horde_Core_Factory_Browser
  • Horde_Core_Factory_Cache
  • Horde_Core_Factory_Crypt
  • Horde_Core_Factory_Data
  • Horde_Core_Factory_Db
  • Horde_Core_Factory_DbBase
  • Horde_Core_Factory_DbPear
  • Horde_Core_Factory_Dns
  • Horde_Core_Factory_Editor
  • Horde_Core_Factory_Facebook
  • Horde_Core_Factory_Group
  • Horde_Core_Factory_History
  • Horde_Core_Factory_HttpClient
  • Horde_Core_Factory_Identity
  • Horde_Core_Factory_Image
  • Horde_Core_Factory_Imple
  • Horde_Core_Factory_Imsp
  • Horde_Core_Factory_ImspAuth
  • Horde_Core_Factory_Injector
  • Horde_Core_Factory_KolabServer
  • Horde_Core_Factory_KolabSession
  • Horde_Core_Factory_KolabStorage
  • Horde_Core_Factory_Ldap
  • Horde_Core_Factory_Lock
  • Horde_Core_Factory_Logger
  • Horde_Core_Factory_LoginTasks
  • Horde_Core_Factory_Mail
  • Horde_Core_Factory_Mapper
  • Horde_Core_Factory_Matcher
  • Horde_Core_Factory_Memcache
  • Horde_Core_Factory_MimeViewer
  • Horde_Core_Factory_Notification
  • Horde_Core_Factory_Perms
  • Horde_Core_Factory_PermsCore
  • Horde_Core_Factory_Prefs
  • Horde_Core_Factory_Request
  • Horde_Core_Factory_Secret
  • Horde_Core_Factory_SessionHandler
  • Horde_Core_Factory_Share
  • Horde_Core_Factory_ShareBase
  • Horde_Core_Factory_Template
  • Horde_Core_Factory_TextFilter
  • Horde_Core_Factory_ThemesCache
  • Horde_Core_Factory_Token
  • Horde_Core_Factory_Tree
  • Horde_Core_Factory_Twitter
  • Horde_Core_Factory_UrlShortener
  • Horde_Core_Factory_Vfs
  • Horde_Core_Factory_View
  • Horde_Core_Factory_Weather
  • Horde_Core_Group_Ldap
  • Horde_Core_Log_Logger
  • Horde_Core_LoginTasks
  • Horde_Core_LoginTasks_Backend_Horde
  • Horde_Core_LoginTasks_SystemTask_Upgrade
  • Horde_Core_Mime_Viewer_Syntaxhighlighter
  • Horde_Core_Mime_Viewer_Vcard
  • Horde_Core_Notification_Event_Status
  • Horde_Core_Notification_Handler_Decorator_Hordelog
  • Horde_Core_Notification_Storage_Session
  • Horde_Core_Perms
  • Horde_Core_Perms_Ui
  • Horde_Core_Prefs_Cache_Session
  • Horde_Core_Prefs_Identity
  • Horde_Core_Prefs_Storage_Configuration
  • Horde_Core_Prefs_Storage_Hooks
  • Horde_Core_Prefs_Storage_Upgrade
  • Horde_Core_Prefs_Ui
  • Horde_Core_Prefs_Ui_Widgets
  • Horde_Core_Share_Driver
  • Horde_Core_Share_FactoryCallback
  • Horde_Core_Sidebar
  • Horde_Core_Text_Filter_Bbcode
  • Horde_Core_Text_Filter_Emails
  • Horde_Core_Text_Filter_Emoticons
  • Horde_Core_Text_Filter_Highlightquotes
  • Horde_Core_Translation
  • Horde_Core_Tree_Html
  • Horde_Core_Tree_Javascript
  • Horde_Core_Tree_Simplehtml
  • Horde_Core_Ui_FlagImage
  • Horde_Core_Ui_JsCalendar
  • Horde_Core_Ui_Language
  • Horde_Core_Ui_Layout
  • Horde_Core_Ui_ModalFormRenderer
  • Horde_Core_Ui_Pager
  • Horde_Core_Ui_Tabs
  • Horde_Core_Ui_TagCloud
  • Horde_Core_Ui_VarRenderer
  • Horde_Core_Ui_VarRenderer_Html
  • Horde_Core_Ui_VarRenderer_TablesetHtml
  • Horde_Core_Ui_Widget
  • Horde_ErrorHandler
  • Horde_Help
  • Horde_Menu
  • Horde_Registry
  • Horde_Registry_Api
  • Horde_Registry_Application
  • Horde_Registry_Caller
  • Horde_Registry_Nlsconfig
  • Horde_Script_Files
  • Horde_Session
  • Horde_Session_Null
  • Horde_Themes
  • Horde_Themes_Cache
  • Horde_Themes_Css
  • Horde_Themes_Element
  • Horde_Themes_Image
  • Horde_Themes_Sound

Exceptions

  • Horde_Exception_HookNotSet
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Session:: class provides a set of methods for handling the
  4:  * administration and contents of the Horde session variable.
  5:  *
  6:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (LGPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 10:  *
 11:  * @author   Michael Slusarz <slusarz@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 14:  * @package  Core
 15:  */
 16: class Horde_Session
 17: {
 18:     /* Class constants. */
 19:     const DATA = '_d';
 20:     const MODIFIED = '_m';
 21:     const PRUNE = '_p';
 22: 
 23:     const TYPE_ARRAY = 1;
 24:     const TYPE_OBJECT = 2;
 25: 
 26:     const NOT_SERIALIZED = 0;
 27:     const IS_SERIALIZED = 1;
 28: 
 29:     /**
 30:      * Maximum size of the pruneable data store.
 31:      *
 32:      * @var integer
 33:      */
 34:     public $maxStore = 20;
 35: 
 36:     /**
 37:      * The session handler object.
 38:      *
 39:      * @var Horde_SessionHandler
 40:      */
 41:     public $sessionHandler = null;
 42: 
 43:     /**
 44:      * Indicates that the session is active (read/write).
 45:      *
 46:      * @var boolean
 47:      */
 48:     private $_active = false;
 49: 
 50:     /**
 51:      * Indicate that a new session ID has been generated for this page load.
 52:      *
 53:      * @var boolean
 54:      */
 55:     private $_cleansession = false;
 56: 
 57:     /**
 58:      * Use LZF compression?
 59:      * We use LZF compression on arrays and objects. Compressing numbers and
 60:      * most strings is not enought of an benefit for the overhead.
 61:      *
 62:      * @var boolean
 63:      */
 64:     private $_lzf = false;
 65: 
 66:     /**
 67:      * Indicates that session data is read-only.
 68:      *
 69:      * @var boolean
 70:      */
 71:     private $_readonly = false;
 72: 
 73:     /**
 74:      * On re-login, indicate whether we were previously authenticated.
 75:      *
 76:      * @var integer
 77:      */
 78:     private $_relogin = null;
 79: 
 80:     /**
 81:      * Constructor.
 82:      */
 83:     public function __construct()
 84:     {
 85:         $this->_lzf = Horde_Util::extensionExists('lzf');
 86: 
 87:         /* Make sure global session variable is always initialized. */
 88:         $_SESSION = array();
 89:     }
 90: 
 91:     /**
 92:      * Sets a custom session handler up, if there is one.
 93:      *
 94:      * @param boolean $start         Initiate the session?
 95:      * @param string $cache_limiter  Override for the session cache limiter
 96:      *                               value.
 97:      * @param string $session_id     The session ID to use.
 98:      *
 99:      * @throws Horde_Exception
100:      */
101:     public function setup($start = true, $cache_limiter = null,
102:                           $session_id = null)
103:     {
104:         global $conf;
105: 
106:         ini_set('url_rewriter.tags', 0);
107:         if (empty($conf['session']['use_only_cookies'])) {
108:             ini_set('session.use_only_cookies', 0);
109:         } else {
110:             ini_set('session.use_only_cookies', 1);
111:             if (!empty($conf['cookie']['domain']) &&
112:                 (strpos($conf['server']['name'], '.') === false)) {
113:                 throw new Horde_Exception('Session cookies will not work without a FQDN and with a non-empty cookie domain. Either use a fully qualified domain name like "http://www.example.com" instead of "http://example" only, or set the cookie domain in the Horde configuration to an empty value, or enable non-cookie (url-based) sessions in the Horde configuration.');
114:             }
115:         }
116: 
117:         if (!empty($conf['session']['timeout'])) {
118:             ini_set('session.gc_maxlifetime', $conf['session']['timeout']);
119:         }
120: 
121:         session_set_cookie_params(
122:             0,
123:             $conf['cookie']['path'],
124:             $conf['cookie']['domain'],
125:             $conf['use_ssl'] == 1 ? 1 : 0,
126:             true
127:         );
128:         session_cache_limiter(is_null($cache_limiter) ? $conf['session']['cache_limiter'] : $cache_limiter);
129:         session_name(urlencode($conf['session']['name']));
130:         if ($session_id) {
131:             session_id($session_id);
132:         }
133: 
134:         /* We want to create an instance here, not get, since we may be
135:          * destroying the previous instances in the page. */
136:         $this->sessionHandler = $GLOBALS['injector']->createInstance('Horde_SessionHandler');
137: 
138:         if ($start) {
139:             $this->start();
140:             $this->_start();
141:         }
142:     }
143: 
144:     /**
145:      * Starts the session.
146:      *
147:      * @since 1.4.0
148:      */
149:     public function start()
150:     {
151:         session_start();
152:         $this->_active = true;
153: 
154:         /* We have reopened a session. Check to make sure that authentication
155:          * status has not changed in the meantime. */
156:         if (!$this->_readonly &&
157:             !is_null($this->_relogin) &&
158:             (($GLOBALS['registry']->getAuth() !== false) !== $this->_relogin)) {
159:             Horde::logMessage('Previous session attempted to be reopened after authentication status change. All session modifications will be ignored.', 'DEBUG');
160:             $this->_readonly = true;
161:         }
162:     }
163: 
164:     /**
165:      * Tasks to perform when starting a session.
166:      */
167:     private function _start()
168:     {
169:         /* Create internal data arrays. */
170:         if (!isset($_SESSION[self::MODIFIED])) {
171:             /* Last modification time of session.
172:              * This will cause the check below to always return true
173:              * (time() >= 0) and will set the initial value. */
174:             $_SESSION[self::MODIFIED] = 0;
175:         }
176: 
177:         /* Determine if we need to force write the session to avoid a
178:          * session timeout, even though the session is unchanged.
179:          * Theory: On initial login, set the current time plus half of the
180:          * max lifetime in the session.  Then check this timestamp before
181:          * saving. If we exceed, force a write of the session and set a
182:          * new timestamp. Why half the maxlifetime?  It guarantees that if
183:          * we are accessing the server via a periodic mechanism (think
184:          * folder refreshing in IMP) that we will catch this refresh. */
185:         $curr_time = time();
186:         if ($curr_time >= $_SESSION[self::MODIFIED]) {
187:             $_SESSION[self::MODIFIED] = intval($curr_time + (ini_get('session.gc_maxlifetime') / 2));
188:             $this->sessionHandler->changed = true;
189:         }
190:     }
191: 
192:     /**
193:      * Destroys any existing session on login and make sure to use a new
194:      * session ID, to avoid session fixation issues. Should be called before
195:      * checking a login.
196:      *
197:      * @return boolean  True if the session was cleaned.
198:      */
199:     public function clean()
200:     {
201:         if ($this->_cleansession) {
202:             return false;
203:         }
204: 
205:         // Make sure to force a completely new session ID and clear all
206:         // session data.
207:         session_regenerate_id(true);
208:         session_unset();
209:         $_SESSION = array();
210:         $this->_start();
211: 
212:         $this->_cleansession = true;
213: 
214:         return true;
215:     }
216: 
217:     /**
218:      * Close the current session.
219:      */
220:     public function close()
221:     {
222:         $this->_active = false;
223:         $this->_relogin = ($GLOBALS['registry']->getAuth() !== false);
224:         session_write_close();
225:     }
226: 
227:     /**
228:      * Destroy session data.
229:      */
230:     public function destroy()
231:     {
232:         session_destroy();
233:         $this->_cleansession = true;
234:     }
235: 
236:     /**
237:      * Is the current session active (read/write)?
238:      *
239:      * @since 1.4.0
240:      *
241:      * @return boolean  True if the current session is active.
242:      */
243:     public function isActive()
244:     {
245:         return $this->_active;
246:     }
247: 
248:     /* Session variable access. */
249: 
250:     /**
251:      * Does the session variable exist?
252:      *
253:      * @param string $app   Application name.
254:      * @param string $name  Session variable name.
255:      *
256:      * @return boolean  True if session variable exists.
257:      */
258:     public function exists($app, $name)
259:     {
260:         return isset($_SESSION[$app][self::NOT_SERIALIZED . $name]) ||
261:                isset($_SESSION[$app][self::IS_SERIALIZED . $name]);
262:     }
263: 
264:     /**
265:      * Get the value of a session variable.
266:      *
267:      * @param string $app    Application name.
268:      * @param string $name   Session variable name.
269:      * @param integer $mask  One of:
270:      *   - self::TYPE_ARRAY - Return an array value.
271:      *   - self::TYPE_OBJECT - Return an object value.
272:      *
273:      * @return mixed  The value or null if the value doesn't exist.
274:      */
275:     public function get($app, $name, $mask = 0)
276:     {
277:         if (isset($_SESSION[$app][self::NOT_SERIALIZED . $name])) {
278:             return $_SESSION[$app][self::NOT_SERIALIZED . $name];
279:         } elseif (isset($_SESSION[$app][self::IS_SERIALIZED . $name])) {
280:             $data = $_SESSION[$app][self::IS_SERIALIZED . $name];
281: 
282:             if ($this->_lzf &&
283:                 (($data = @lzf_decompress($data)) === false)) {
284:                 $this->remove($app, $name);
285:                 return $this->get($app, $name);
286:             }
287: 
288:             return @unserialize($data);
289:         }
290: 
291:         if ($subkeys = $this->_subkeys($app, $name)) {
292:             $ret = array();
293:             foreach ($subkeys as $k => $v) {
294:                 $ret[$k] = $this->get($app, $v, $mask);
295:             }
296:             return $ret;
297:         }
298: 
299:         if (strpos($name, self::DATA) === 0) {
300:             return $this->retrieve($name);
301:         }
302: 
303:         switch ($mask) {
304:         case self::TYPE_ARRAY:
305:             return array();
306: 
307:         case self::TYPE_OBJECT:
308:             return new stdClass;
309:         }
310: 
311:         return null;
312:     }
313: 
314:     /**
315:      * Sets the value of a session variable.
316:      *
317:      * @param string $app    Application name.
318:      * @param string $name   Session variable name.
319:      * @param mixed $value   Session variable value.
320:      * @param integer $mask  One of:
321:      *   - self::TYPE_ARRAY - Force save as an array value.
322:      *   - self::TYPE_OBJECT - Force save as an object value.
323:      */
324:     public function set($app, $name, $value, $mask = 0)
325:     {
326:         if ($this->_readonly) {
327:             return;
328:         }
329: 
330:         /* Each particular piece of session data is generally not used on any
331:          * given page load.  Thus, for arrays and objects, it is beneficial to
332:          * always convert to string representations so that the object/array
333:          * does not need to be rebuilt every time the session is reloaded. */
334:         if (is_object($value) || ($mask & self::TYPE_OBJECT) ||
335:             is_array($value) || ($mask & self::TYPE_ARRAY)) {
336:             $value = serialize($value);
337:             if ($this->_lzf) {
338:                 $value = lzf_compress($value);
339:             }
340:             $_SESSION[$app][self::IS_SERIALIZED . $name] = $value;
341:             unset($_SESSION[$app][self::NOT_SERIALIZED . $name]);
342:         } else {
343:             $_SESSION[$app][self::NOT_SERIALIZED . $name] = $value;
344:             unset($_SESSION[$app][self::IS_SERIALIZED . $name]);
345:         }
346: 
347:         $this->sessionHandler->changed = true;
348:     }
349: 
350:     /**
351:      * Remove session key(s).
352:      *
353:      * @param string $app    Application name.
354:      * @param string $name   Session variable name.
355:      */
356:     public function remove($app, $name = null)
357:     {
358:         if ($this->_readonly) {
359:             return;
360:         }
361: 
362:         if (!isset($_SESSION[$app])) {
363:             return;
364:         }
365: 
366:         if (is_null($name)) {
367:             unset($_SESSION[$app]);
368:         } elseif ($this->exists($app, $name)) {
369:             unset(
370:                 $_SESSION[$app][self::NOT_SERIALIZED . $name],
371:                 $_SESSION[$app][self::IS_SERIALIZED . $name],
372:                 $_SESSION[self::PRUNE][$this->_getKey($app, $name)]
373:             );
374:         } else {
375:             foreach ($this->_subkeys($app, $name) as $val) {
376:                 $this->remove($app, $val);
377:             }
378:         }
379:     }
380: 
381:     /**
382:      * Generates the unique storage key.
383:      *
384:      * @param string $app   Application name.
385:      * @param string $name  Session variable name.
386:      *
387:      * @return string  The unique storage key.
388:      */
389:     private function _getKey($app, $name)
390:     {
391:         return $app . ':' . $name;
392:     }
393: 
394:     /**
395:      * Return the list of subkeys for a master key.
396:      *
397:      * @param string $app   Application name.
398:      * @param string $name  Session variable name.
399:      *
400:      * @return array  Subkeyname (keys) and session variable name (values).
401:      */
402:     private function _subkeys($app, $name)
403:     {
404:         $ret = array();
405: 
406:         if ($name &&
407:             isset($_SESSION[$app]) &&
408:             ($name[strlen($name) - 1] == '/')) {
409:             foreach (array_keys($_SESSION[$app]) as $k) {
410:                 if (strpos($k, $name) === 1) {
411:                     $ret[substr($k, strlen($name) + 1)] = substr($k, 1);
412:                 }
413:             }
414:         }
415: 
416:         return $ret;
417:     }
418: 
419:     /* Session object storage. */
420: 
421:     /**
422:      * Store an arbitrary piece of data in the session.
423:      *
424:      * @param mixed $data     Data to save.
425:      * @param boolean $prune  Is data pruneable?
426:      * @param string $id      ID to use (otherwise, is autogenerated).
427:      *
428:      * @return string  The session storage id (used to retrieve session data).
429:      */
430:     public function store($data, $prune = true, $id = null)
431:     {
432:         $id = is_null($id)
433:             ? strval(new Horde_Support_Randomid())
434:             : $this->_getStoreId($id);
435: 
436:         $this->set(self::DATA, $id, $data);
437: 
438:         if ($prune) {
439:             $ptr = &$_SESSION[self::PRUNE];
440:             unset($ptr[$id]);
441:             $ptr[$id] = 1;
442:             if (count($ptr) > $this->maxStore) {
443:                 array_shift($ptr);
444:             }
445:         }
446: 
447:         return $this->_getKey(self::DATA, $id);
448:     }
449: 
450:     /**
451:      * Retrieve data from the session data store (created via store()).
452:      *
453:      * @param string $id  The session data ID.
454:      *
455:      * @return mixed  The session data value.
456:      */
457:     public function retrieve($id)
458:     {
459:         return $this->get(self::DATA, $this->_getStoreId($id));
460:     }
461: 
462:     /**
463:      * Purge data from the session data store (created via store()).
464:      *
465:      * @param string $id  The session data ID.
466:      */
467:     public function purge($id)
468:     {
469:         $this->remove(self::DATA, $this->_getStoreId($id));
470:     }
471: 
472:     /**
473:      * Returns the base storage ID.
474:      *
475:      * @param string $id  The session data ID.
476:      *
477:      * @return string  The base storage ID (without prefix).
478:      */
479:     private function _getStoreId($id)
480:     {
481:         $id = trim($id);
482: 
483:         if (strpos($id, self::DATA) === 0) {
484:             $id = substr($id, strlen(self::DATA) + 1);
485:         }
486: 
487:         return $id;
488:     }
489: 
490: }
491: 
API documentation generated by ApiGen