set json api functions parameter names
[tine20] / tine20 / Tinebase / Session / Abstract.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  Session
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Guilherme Striquer Bisotto <guilherme.bisotto@serpro.gov.br>
9  * @copyright   Copyright (c) 2014-2014 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  */
12
13 /**
14  * Abstract class for Session and Session Namespaces
15  * 
16  * @package     Tinebase
17  * @subpackage  Session
18  */
19 abstract class Tinebase_Session_Abstract extends Zend_Session_Namespace
20 {
21     /**
22      * Default session directory name
23      */
24     const SESSION_DIR_NAME = 'tine20_sessions';
25     
26     /**
27      * constant for session namespace (tinebase) registry index
28      */
29     const SESSION = 'session';
30     
31     protected static $_sessionEnabled = false;
32     protected static $_isSetupSession = false;
33     
34     /**
35      * get a value from the registry
36      *
37      */
38     protected static function get($index)
39     {
40         return (Zend_Registry::isRegistered($index)) ? Zend_Registry::get($index) : NULL;
41     }
42     
43     /**
44      * set a registry value
45      *
46      * @return mixed value
47      */
48     protected static function set($index, $value)
49     {
50         Zend_Registry::set($index, $value);
51     }
52     
53     /**
54      * Create a session namespace or return an existing one
55      *
56      * @param unknown $_namespace
57      * @throws Exception
58      * @return Zend_Session_Namespace
59      */
60     protected static function _getSessionNamespace($_namespace)
61     {
62         $sessionNamespace = self::get($_namespace);
63         
64         if ($sessionNamespace == null) {
65             try {
66                 $sessionNamespace = new Zend_Session_Namespace($_namespace);
67                 self::set($_namespace, $sessionNamespace);
68             } catch (Exception $e) {
69                 self::expireSessionCookie();
70                 throw $e;
71             }
72         }
73         
74         return $sessionNamespace;
75     }
76     
77     /**
78      * Zend_Session::sessionExists encapsulation
79      *
80      * @return boolean
81      */
82     public static function sessionExists()
83     {
84         return Zend_Session::sessionExists();
85     }
86     
87     /**
88      * Zend_Session::isStarted encapsulation
89      *
90      * @return boolean
91      */
92     public static function isStarted()
93     {
94         return Zend_Session::isStarted();
95     }
96     
97     /**
98      * Destroy session and remove cookie
99      */
100     public static function destroyAndRemoveCookie()
101     {
102         Zend_Session::destroy(true, true);
103     }
104     
105     /**
106      * Destroy session but not remove cookie
107      */
108     public static function destroyAndMantainCookie()
109     {
110         Zend_Session::destroy(false, true);
111     }
112     
113     /**
114      * Zend_Session::writeClose encapsulation
115      *
116      * @param string $readonly
117      */
118     public static function writeClose($readonly = true)
119     {
120         Zend_Session::writeClose($readonly);
121     }
122     
123     /**
124      * Zend_Session::isWritable encapsulation
125      *
126      * @return boolean
127      */
128     public static function isWritable()
129     {
130         return Zend_Session::isWritable();
131     }
132     
133     /**
134      * Zend_Session::getId encapsulation
135      *
136      * @return string
137      */
138     public static function getId()
139     {
140         return Zend_Session::getId();
141     }
142     
143     /**
144      * Zend_Session::expireSessionCookie encapsulation
145      */
146     public static function expireSessionCookie()
147     {
148         Zend_Session::expireSessionCookie();
149     }
150     
151     /**
152      * Zend_Session::regenerateId encapsulation
153      */
154     public static function regenerateId()
155     {
156        Zend_Session::regenerateId();
157     }
158     
159     /**
160      * get session dir string (without PATH_SEP at the end)
161      *
162      * @return string
163      */
164     public static function getSessionDir()
165     {
166         $config = Tinebase_Core::getConfig();
167         $sessionDir = ($config->session && $config->session->path)
168             ? $config->session->path
169             : null;
170         
171         #####################################
172         # LEGACY/COMPATIBILITY: 
173         # (1) had to rename session.save_path key to sessiondir because otherwise the
174         # generic save config method would interpret the "_" as array key/value seperator
175         # (2) moved session config to subgroup 'session'
176         if (empty($sessionDir)) {
177             foreach (array('session.save_path', 'sessiondir') as $deprecatedSessionDir) {
178                 $sessionDir = $config->get($deprecatedSessionDir, null);
179                 if ($sessionDir) {
180                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " config.inc.php key '{$deprecatedSessionDir}' should be renamed to 'path' and moved to 'session' group.");
181                 }
182             }
183         }
184         #####################################
185         
186         if (empty($sessionDir) || !@is_writable($sessionDir)) {
187             $sessionDir = session_save_path();
188             if (empty($sessionDir) || !@is_writable($sessionDir)) {
189                 $sessionDir = Tinebase_Core::guessTempDir();
190             }
191             
192             $sessionDirName = self::SESSION_DIR_NAME;
193             $sessionDir .= DIRECTORY_SEPARATOR . $sessionDirName;
194         }
195         
196         Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " Using session dir: " . $sessionDir);
197         
198         return $sessionDir;
199     }
200     
201     /**
202      * set session backend
203      */
204     public static function setSessionBackend()
205     {
206         $config = Tinebase_Core::getConfig();
207         $defaultSessionSaveHandler = ucfirst(ini_get('session.save_handler'));
208         $defaultSessionSavePath = ini_get('session.save_path');
209
210         $backendType = ($config->session && $config->session->backend) ? ucfirst($config->session->backend) : $defaultSessionSaveHandler;
211         $maxLifeTime = ($config->session && $config->session->lifetime) ? $config->session->lifetime : 86400; // one day is default
212         
213         switch ($backendType) {
214             case 'Files': // this is the default for the ini setting session.save_handler
215             case 'File':
216                 if ($config->gc_maxlifetime) {
217                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " config.inc.php key 'gc_maxlifetime' should be renamed to 'lifetime' and moved to 'session' group.");
218                     $maxLifeTime = $config->get('gc_maxlifetime', 86400);
219                 }
220                 
221                 Zend_Session::setOptions(array(
222                     'gc_maxlifetime'     => $maxLifeTime
223                 ));
224                 
225                 $sessionSavepath = self::getSessionDir();
226                 if (ini_set('session.save_path', $sessionSavepath) !== FALSE) {
227                     if (!is_dir($sessionSavepath)) {
228                         mkdir($sessionSavepath, 0700);
229                     }
230                 }
231                 
232                 $lastSessionCleanup = Tinebase_Config::getInstance()->get(Tinebase_Config::LAST_SESSIONS_CLEANUP_RUN);
233                 if ($lastSessionCleanup instanceof DateTime && $lastSessionCleanup > Tinebase_DateTime::now()->subHour(2)) {
234                     Zend_Session::setOptions(array(
235                         'gc_probability' => 0,
236                         'gc_divisor'     => 100
237                     ));
238                 } else if (@opendir($defaultSessionSavePath) !== FALSE) {
239                     Zend_Session::setOptions(array(
240                         'gc_probability' => 1,
241                         'gc_divisor'     => 100
242                     ));
243                 } else {
244                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__
245                         . " Unable to initialize automatic session cleanup. Check permissions to " . ini_get('session.save_path'));
246                 }
247                 
248                 break;
249                 
250             case 'Redis':
251                 if ($config->session) {
252                     $host = ($config->session->host) ? $config->session->host : 'localhost';
253                     $port = ($config->session->port) ? $config->session->port : 6379;
254                     if ($config->session && $config->session->prefix) {
255                         $prefix = $config->session->prefix;
256                     } else {
257                         $prefix = ($config->database && $config->database->tableprefix) ? $config->database->tableprefix : 'tine20';
258                     }
259                     $prefix = $prefix . '_SESSION_';
260                     $savePath = "tcp://$host:$port?prefix=$prefix";
261                 } else if ($defaultSessionSavePath) {
262                     $savePath = $defaultSessionSavePath;
263                 } else {
264                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__
265                         . " Unable to setup redis session backend - config missing");
266                     return;
267                 }
268
269                 Zend_Session::setOptions(array(
270                     'gc_maxlifetime' => $maxLifeTime,
271                     'save_handler'   => 'redis',
272                     'save_path'      => $savePath
273                 ));
274                 
275                 break;
276                 
277             default:
278                 break;
279         }
280         
281         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Session of backend type '{$backendType}' configured.");
282     }
283
284     /**
285      * activate session and set name in options
286      *
287      * @param $sessionName
288      */
289     public static function setSessionEnabled($sessionName)
290     {
291         self::setSessionOptions(array(
292             'name'   => $sessionName
293         ));
294         
295         self::$_sessionEnabled = true;
296         self::$_isSetupSession = $sessionName === 'TINE20SETUPSESSID';
297     }
298
299     /**
300      * @return bool
301      *
302      * TODO it would be better to look into the session options and check the name
303      * TODO and maybe this can be removed as we already have Setup_Session and Tinebase_Session classes ...
304      */
305     public static function isSetupSession()
306     {
307         return self::$_isSetupSession;
308     }
309     
310     /**
311      * set session options
312      *
313      * @param array $_options
314      */
315     public static function setSessionOptions($options = array())
316     {
317         $options = array_merge(
318             $options,
319              array (
320                 'cookie_httponly' => true,
321                 'hash_function'   => 1
322              )
323         );
324         
325         if (isset($_SERVER['REQUEST_URI'])) {
326             $request = Tinebase_Core::get(Tinebase_Core::REQUEST);
327
328             // fallback to request uri
329             $baseUri = $_SERVER['REQUEST_URI'];
330
331             if ($request) {
332                 if ($request->getHeaders()->has('X-FORWARDED-HOST')) {
333                     /************** Apache 2.4 with mod_proxy ****************
334                      * Apache set's X-FORWARDED-HOST and REFERER
335                      * 
336                      * ProxyPass /tine20 http://192.168.122.158/tine20
337                      * <Location /tine20>
338                      *      ProxyPassReverse http://192.168.122.158/tine20
339                      * </Location>
340                      * 
341                      * ProxyPass /192.168.122.158/tine20 http://192.168.122.158/tine20
342                      * <Location /192.168.122.158/tine20>
343                      *     ProxyPassReverse http://192.168.122.158/tine20
344                      * </Location>
345                      */
346                     if ($request->getHeaders()->has('REFERER')) {
347                         $refererUri = \Zend\Uri\UriFactory::factory($request->getHeaders()->get('REFERER')->getFieldValue());
348                         $baseUri = $refererUri->getPath();
349                     } else {
350                         $exploded = explode("/", $_SERVER['REQUEST_URI']);
351                         if (strtolower($exploded[1]) == strtolower($_SERVER['HTTP_HOST'])) {
352                              $baseUri = '/' . $_SERVER['HTTP_HOST'] . (($baseUri == '/') ? '' : $baseUri);
353                         }
354                     }
355                     
356                 } else {
357                     $baseUri = $request->getBasePath();
358                 }
359             }
360
361             // strip of index.php
362             if (substr($baseUri, -9) === 'index.php') {
363                 $baseUri = dirname($baseUri);
364             }
365             
366             // strip of trailing /
367             $baseUri = rtrim($baseUri, '/');
368             
369             // fix for windows server with backslash directory separator
370             $baseUri = str_replace(DIRECTORY_SEPARATOR, '/', $baseUri);
371             
372             $options['cookie_path'] = $baseUri;
373         }
374         
375         if (!empty($_SERVER['HTTPS']) && strtoupper($_SERVER['HTTPS']) != 'OFF') {
376             $options['cookie_secure'] = true;
377         }
378         
379         Zend_Session::setOptions($options);
380     }
381     
382     public static function getSessionEnabled()
383     {
384         return self::$_sessionEnabled;
385     }
386     
387     /**
388      * Gets Tinebase User session namespace
389      *
390      * @throws Zend_Session_Exception
391      * @return Zend_Session_Namespace
392      */
393     public static function getSessionNamespace()
394     {
395         if (! Tinebase_Session::isStarted()) {
396             throw new Zend_Session_Exception('Session not started');
397         }
398         
399         if (!self::getSessionEnabled()) {
400             throw new Zend_Session_Exception('Session not enabled for request');
401         }
402         
403         try {
404            return self::_getSessionNamespace(static::NAMESPACE_NAME);
405            
406         } catch(Exception $e) {
407             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Session error: ' . $e->getMessage());
408             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $e->getTraceAsString());
409             throw $e;
410         }
411     }
412 }