adds generic method for getting the hostname
[tine20] / tine20 / Tinebase / Core.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Server
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Philipp Schüle <p.schuele@metaways.de>
10  *
11  */
12
13 /**
14  * php helpers
15  */
16 require_once 'Helper.php';
17
18 /**
19  * dispatcher and initialisation class (functions are static)
20  * - dispatchRequest() function
21  * - initXYZ() functions
22  * - has registry and config
23  *
24  * @package     Tinebase
25  * @subpackage  Server
26  */
27 class Tinebase_Core
28 {
29     /**************** registry indexes *************************/
30
31     /**
32      * constant for config registry index
33      */
34     const CONFIG = 'configFile';
35
36     /**
37      * constant for locale registry index
38      */
39     const LOCALE = 'locale';
40
41     /**
42      * constant for logger registry index
43      */
44     const LOGGER = 'logger';
45     
46     /**
47      * constant for loglevel registry index
48      *
49      */
50     const LOGLEVEL = 'loglevel';
51
52     /**
53      * constant for cache registry index
54      */
55     const CACHE = 'cache';
56     
57      /**
58      * constant for shared cache registry index
59      */
60     const SHAREDCACHE = 'sharedCache';
61
62     /**
63      * constant for session namespace (tinebase) registry index
64      */
65     const SESSION = 'session';
66     
67     /**
68      */
69     const SESSIONID = 'sessionId';
70
71     /**
72      * constant for application start time in ms registry index
73      */
74     const STARTTIME = 'starttime';
75
76     /**
77      * constant for current account/user
78      */
79     const USER = 'currentAccount';
80
81     /**
82      * const for current users credentialcache
83      */
84     const USERCREDENTIALCACHE = 'usercredentialcache';
85
86     /**
87      * constant for database adapter
88      */
89     const DB = 'dbAdapter';
90     
91     /**
92      * constant for database adapter name
93      * 
94      */
95     const DBNAME = 'dbAdapterName';
96
97     /**
98      * constant for database adapter
99      */
100     const USERTIMEZONE = 'userTimeZone';
101
102     /**
103      * constant for preferences registry
104      */
105     const PREFERENCES = 'preferences';
106     
107     /**
108      * constant for preferences registry
109      */
110     const SCHEDULER = 'scheduler';
111     
112     /**
113      * constant temp dir registry
114      */
115     const TMPDIR = 'tmpdir';
116     
117     /**
118      * constant temp dir registry
119      */
120     const FILESDIR = 'filesdir';
121     
122     /**
123      * constant for request method registry
124      */
125     const METHOD = 'method';
126     
127     /**
128      * const PDO_MYSQL
129      *
130      */
131     const PDO_MYSQL = 'Pdo_Mysql';
132     
133     /**
134      * minimal version of MySQL supported
135      */
136     const MYSQL_MINIMAL_VERSION = '5.0.0';
137     
138     /**
139      * const PDO_PGSQL
140      *
141      */
142     const PDO_PGSQL = 'Pdo_Pgsql';
143     
144     /**
145      * minimal version of PostgreSQL supported
146      */
147     const PGSQL_MINIMAL_VERSION = '8.4.8';
148
149     /**
150      * const PDO_OCI
151      *
152      */
153     const PDO_OCI = 'Pdo_Oci';
154     
155     /**
156      * const ORACLE
157      * Zend_Db adapter name for the oci8 driver.
158      *
159      */
160     const ORACLE = 'Oracle';
161     
162     /**
163      * minimal version of Oracle supported
164      */
165     const ORACLE_MINIMAL_VERSION = '9.0.0';
166     
167     /**
168      * Application Instance Cache
169      * @var array
170      */
171     protected static $appInstanceCache = array();
172     
173     /******************************* DISPATCH *********************************/
174     
175     /**
176      * dispatch request
177      */
178     public static function dispatchRequest()
179     {
180         // check transaction header
181         if (isset($_SERVER['HTTP_X_TINE20_TRANSACTIONID'])) {
182             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Client transaction {$_SERVER['HTTP_X_TINE20_TRANSACTIONID']}");
183             Tinebase_Log_Formatter::setPrefix(substr($_SERVER['HTTP_X_TINE20_TRANSACTIONID'], 0, 5));
184         }
185         
186         $server = NULL;
187         
188         /**************************** JSON API *****************************/
189         if ((isset($_SERVER['HTTP_X_TINE20_REQUEST_TYPE']) && $_SERVER['HTTP_X_TINE20_REQUEST_TYPE'] == 'JSON')  ||
190             (isset($_SERVER['CONTENT_TYPE']) && substr($_SERVER['CONTENT_TYPE'],0,16) == 'application/json')  ||
191             (isset($_POST['requestType']) && $_POST['requestType'] == 'JSON') ||
192             (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
193         ) {
194             $server = new Tinebase_Server_Json();
195             
196         /**************************** SNOM API *****************************/
197         } elseif(
198             isset($_SERVER['HTTP_USER_AGENT']) &&
199             preg_match('/^Mozilla\/4\.0 \(compatible; (snom...)\-SIP (\d+\.\d+\.\d+)/i', $_SERVER['HTTP_USER_AGENT'])
200         ) {
201             $server = new Voipmanager_Server_Snom();
202             
203         /**************************** ASTERISK API *****************************/
204         } elseif(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == 'asterisk-libcurl-agent/1.0') {
205             $server = new Voipmanager_Server_Asterisk();
206             
207         /**************************** ActiveSync API ****************************
208          * RewriteRule ^Microsoft-Server-ActiveSync index.php?frontend=activesync [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
209          */
210         } elseif((isset($_SERVER['REDIRECT_ACTIVESYNC']) && $_SERVER['REDIRECT_ACTIVESYNC'] == 'true') ||
211                  (isset($_GET['frontend']) && $_GET['frontend'] == 'activesync')) {
212             $server = new ActiveSync_Server_Http();
213             self::set('serverclassname', get_class($server));
214
215         /**************************** WebDAV / CardDAV / CalDAV API **********************************
216          * RewriteCond %{REQUEST_METHOD} !^(GET|POST)$
217          * RewriteRule ^/$            /index.php?frontend=webdav [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
218          *
219          * RewriteRule ^/addressbooks /index.php?frontend=webdav [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
220          * RewriteRule ^/calendars    /index.php?frontend=webdav [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
221          * RewriteRule ^/principals   /index.php?frontend=webdav [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
222          * RewriteRule ^/webdav       /index.php?frontend=webdav [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
223          */
224         } elseif(isset($_GET['frontend']) && $_GET['frontend'] == 'webdav') {
225             $server = new Tinebase_Server_WebDAV();
226             
227         /**************************** CLI API *****************************/
228         } elseif (php_sapi_name() == 'cli') {
229             $server = new Tinebase_Server_Cli();
230             
231         /**************************** HTTP API ****************************/
232         } else {
233             
234             /**************************** OpenID ****************************
235              * RewriteRule ^/users/(.*)                      /index.php?frontend=openid&username=$1 [L,QSA]
236              */
237             if (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], 'application/xrds+xml') !== FALSE) {
238                 $_REQUEST['method'] = 'Tinebase.getXRDS';
239             } elseif ((isset($_SERVER['REDIRECT_USERINFOPAGE']) && $_SERVER['REDIRECT_USERINFOPAGE'] == 'true') ||
240                       (isset($_REQUEST['frontend']) && $_REQUEST['frontend'] == 'openid')) {
241                 $_REQUEST['method'] = 'Tinebase.userInfoPage';
242             }
243             
244             if (!isset($_REQUEST['method']) && (isset($_REQUEST['openid_action']) || isset($_REQUEST['openid_assoc_handle'])) ) {
245                 $_REQUEST['method'] = 'Tinebase.openId';
246             }
247             
248             $server = new Tinebase_Server_Http();
249         }
250         
251         $server->handle();
252         $method = get_class($server) . '::' . $server->getRequestMethod();
253         self::set(self::METHOD, $method);
254
255         self::finishProfiling();
256         self::getDbProfiling();
257     }
258     
259     /**
260      * returns TRUE if request is HTTPS
261      * 
262      * @return boolean
263      */
264     public static function isHttpsRequest()
265     {
266         return (! empty($_SERVER['HTTPS']) && strtoupper($_SERVER['HTTPS']) != 'OFF');
267     }
268     
269     /**
270      * enable profiling
271      * - supports xhprof
272      */
273     public static function enableProfiling()
274     {
275         if (! self::getConfig() || ! self::getConfig()->profiler) {
276             return;
277         }
278
279         if (self::getConfig()->profiler->xhprof) {
280             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Enabling xhprof');
281             
282             xhprof_enable(XHPROF_FLAGS_MEMORY);
283         } 
284     }
285
286     /**
287      * finish profiling / save profiling data to a file
288      * - supports xhprof
289      */
290     public static function finishProfiling()
291     {
292     if (! self::getConfig() || ! self::getConfig()->profiler) {
293             return;
294         }
295         
296         $config = self::getConfig()->profiler;
297         $method = self::get(self::METHOD);
298     
299         if ($config->xhprof) {
300             $xhprof_data = xhprof_disable();
301             
302             if ($config->method) {
303                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Filtering xhprof profiling method: ' . $config->method);
304                 if (! preg_match($config->method, $method)) {
305                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Method mismatch, do not save profiling info.');
306                     return;
307                 }
308             }
309             
310             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Saving xhprof profiling run for method ' . $method);
311             
312             $XHPROF_ROOT = '/usr/share/php5-xhprof';
313             if (file_exists($XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php")) {
314                 include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
315                 include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";
316                 $xhprof_runs = new XHProfRuns_Default();
317                 $run_id = $xhprof_runs->save_run($xhprof_data, "tine");
318             } else {
319                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . '  ' . print_r($xhprof_data, TRUE));
320             }
321         }
322     }
323     
324     /******************************* APPLICATION ************************************/
325
326     /**
327      * returns an instance of the controller of an application
328      *
329      * @param   string $_applicationName appname / modelname
330      * @param   string $_modelName
331      * @return  Tinebase_Controller_Abstract|Tinebase_Controller_Record_Abstract the controller of the application
332      * @throws  Tinebase_Exception_NotFound
333      */
334     public static function getApplicationInstance($_applicationName, $_modelName = '', $_ignoreACL = FALSE)
335     {
336         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
337             . ' Params: application: ' . $_applicationName . ' / model: ' . $_modelName);
338         
339         $cacheKey = $_applicationName . '_' . $_modelName . '_' . ($_ignoreACL?1:0);
340         if (isset(self::$appInstanceCache[$cacheKey])) {
341             return self::$appInstanceCache[$cacheKey];
342         }
343         
344         // modified (some model names can have both . and _ in their names and we should treat them as JS model name
345         if (strpos($_applicationName, '_') && ! strpos($_applicationName, '.')) {
346             // got (complete) model name name as first param
347             list($appName, $i, $modelName) = explode('_', $_applicationName, 3);
348         } else if (strpos($_applicationName, '.')) {
349             // got (complete) model name name as first param (JS style)
350             list($j, $appName, $i, $modelName) = explode('.', $_applicationName, 4);
351         } else {
352             $appName = $_applicationName;
353             $modelName = $_modelName;
354         }
355         
356         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
357             . ' Extracted appName: ' . $appName . ' modelName: ' . $modelName);
358         
359         $controllerName = ucfirst((string) $appName);
360         if ($appName !== 'Tinebase' || ($appName === 'Tinebase' && ! $modelName)) {
361             // only app controllers are called "App_Controller_Model"
362             $controllerName .= '_Controller';
363         }
364         
365         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
366             . ' controllerName: ' . $controllerName);
367
368         if (! empty($modelName)) {
369             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
370                 . ' Checking for model controller ...');
371             
372             $modelName = preg_replace('/^' . $appName . '_' . 'Model_/', '', $modelName);
373             $controllerNameModel = $controllerName . '_' . $modelName;
374             if (! class_exists($controllerNameModel)) {
375                 throw new Tinebase_Exception_NotFound('No Application Controller found (checked class ' . $controllerNameModel . ')!');
376                 // check for generic app controller
377                 if (! class_exists($controllerName)) {
378                     throw new Tinebase_Exception_NotFound('No Controller found (checked classes '. $controllerName . ' and ' . $controllerNameModel . ')!');
379                 }
380             } else {
381                 $controllerName = $controllerNameModel;
382             }
383         } else if (! class_exists($controllerName)) {
384             throw new Tinebase_Exception_NotFound('No Application Controller found (checked class ' . $controllerName . ')!');
385         }
386         
387         if (! $_ignoreACL && is_object(Tinebase_Core::getUser()) && ! Tinebase_Core::getUser()->hasRight($appName, Tinebase_Acl_Rights_Abstract::RUN)) {
388             throw new Tinebase_Exception_AccessDenied('No right to access application ' . $appName);
389         }
390         
391         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
392             . ' Getting instance of ' . $controllerName);
393         
394         $controller = call_user_func(array($controllerName, 'getInstance'));
395         self::$appInstanceCache[$cacheKey] = $controller;
396         
397         return $controller;
398     }
399     
400     /******************************* SETUP ************************************/
401
402     /**
403      * init tine framework
404      */
405     public static function initFramework()
406     {
407         Tinebase_Core::setupTempDir();
408         
409         Tinebase_Core::setupStreamWrapper();
410         
411         //Cache must be setup before User Locale because otherwise Zend_Locale tries to setup 
412         //its own cache handler which might result in a open_basedir restriction depending on the php.ini settings
413         Tinebase_Core::setupCache();
414         
415         Tinebase_Core::setupBuildConstants();
416         
417         Tinebase_Core::setupSession();
418         
419         if (Zend_Session::sessionExists()) {
420             Tinebase_Core::startSession();
421         }
422         
423         // setup a temporary user locale/timezone. This will be overwritten later but we 
424         // need to handle exceptions during initialisation process such as session timeout
425         // @todo add fallback locale to config file
426         Tinebase_Core::set('locale', new Zend_Locale('en_US'));
427         Tinebase_Core::set('userTimeZone', 'UTC');
428         
429         Tinebase_Core::setupUserCredentialCache();
430         
431         Tinebase_Core::setupUserTimezone();
432         
433         Tinebase_Core::setupUserLocale();
434         
435         Tinebase_Core::enableProfiling();
436         
437         if (PHP_SAPI !== 'cli') {
438             header('X-API: http://www.tine20.org/apidocs/tine20/');
439             if (isset($_SERVER['HTTP_X_TRANSACTIONID'])) {
440                 header('X-TransactionID: ' . substr($_SERVER['HTTP_X_TRANSACTIONID'], 1, -1) . ';' . $_SERVER['SERVER_NAME'] . ';16.4.5009.816;' . date('Y-m-d H:i:s') . ' UTC;265.1558 ms');
441             }
442         }
443     }
444     
445     /**
446      * initializes the build constants like buildtype, package information, ...
447      */
448     public static function setupBuildConstants()
449     {
450         $config = self::getConfig();
451         define('TINE20_BUILDTYPE',     strtoupper($config->get('buildtype', 'DEVELOPMENT')));
452         define('TINE20_CODENAME',      getDevelopmentRevision());
453         define('TINE20_PACKAGESTRING', 'none');
454         define('TINE20_RELEASETIME',   'none');
455     }
456     
457     /**
458      * tines error exception handler for catchable fatal errors
459      *
460      * NOTE: PHP < 5.3 don't throws exceptions for Catchable fatal errors per default,
461      * so we convert them into exceptions manually
462      *
463      * @param integer $severity
464      * @param string $errstr
465      * @param string $errfile
466      * @param integer $errline
467      * @throws ErrorException
468      */
469     public static function errorHandler($severity, $errstr, $errfile, $errline)
470     {
471         if (error_reporting() == 0) {
472             return;
473         }
474
475         $logLine = " $errstr in {$errfile}::{$errline} ($severity)";
476         $e = new Exception('just to get trace');
477         $trace = $e->getTraceAsString();
478         
479         switch ($severity) {
480             case E_COMPILE_ERROR:
481             case E_CORE_ERROR:
482             case E_ERROR:
483             case E_PARSE:
484             case E_RECOVERABLE_ERROR:
485             case E_USER_ERROR:
486                 throw new ErrorException($errstr, 0, $severity, $errfile, $errline);
487                 break;
488
489             case E_COMPILE_WARNING:
490             case E_CORE_WARNING:
491             case E_USER_WARNING:
492             case E_WARNING:
493                 if (Tinebase_Core::isRegistered(Tinebase_Core::LOGGER)) {
494                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . $logLine);
495                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' ' . $trace);
496                 } else {
497                     error_log(__METHOD__ . '::' . __LINE__ . $logLine);
498                     error_log(__METHOD__ . '::' . __LINE__ . ' ' . $trace);
499                 }
500                 break;
501
502             case E_NOTICE:
503             case E_STRICT:
504             case E_USER_NOTICE:
505             default:
506                 if (Tinebase_Core::isRegistered(Tinebase_Core::LOGGER)) {
507                     Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . $logLine);
508                     Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . $trace);
509                 } else {
510                     error_log(__METHOD__ . '::' . __LINE__ . $logLine);
511                     error_log(__METHOD__ . '::' . __LINE__ . ' ' . $trace);
512                 }
513                 break;
514         }
515     }
516
517     /**
518      * initializes the config
519      */
520     public static function setupConfig()
521     {
522         self::set(self::CONFIG, Tinebase_Config::getInstance());
523     }
524
525     /**
526      * setup temp dir registry setting retrieved by {@see _getTempDir()}
527      *
528      * @return void
529      */
530     public static function setupTempDir()
531     {
532         self::set(self::TMPDIR, self::guessTempDir());
533     }
534
535     /**
536      * figure out temp directory:
537      * config.inc.php > sys_get_temp_dir > session_save_path > /tmp
538      *
539      * @return String
540      */
541     public static function guessTempDir()
542     {
543         $config = self::getConfig();
544
545         $tmpdir = $config->tmpdir;
546         if ($tmpdir == Tinebase_Model_Config::NOTSET || !@is_writable($tmpdir)) {
547             $tmpdir = sys_get_temp_dir();
548             if (empty($tmpdir) || !@is_writable($tmpdir)) {
549                 $tmpdir = session_save_path();
550                 if (empty($tmpdir) || !@is_writable($tmpdir)) {
551                     $tmpdir = '/tmp';
552                 }
553             }
554         }
555
556         return $tmpdir;
557     }
558
559     /**
560      * initializes the logger
561      *
562      * @param $_defaultWriter Zend_Log_Writer_Abstract default log writer
563      */
564     public static function setupLogger(Zend_Log_Writer_Abstract $_defaultWriter = NULL)
565     {
566         $config = self::getConfig();
567         $logger = new Tinebase_Log();
568
569         if (isset($config->logger) && $config->logger->active) {
570             try {
571                 $logger->addWriterByConfig($config->logger);
572                 if ($config->logger->additionalWriters) {
573                     foreach ($config->logger->additionalWriters as $writerConfig) {
574                         $logger->addWriterByConfig($writerConfig);
575                     }
576                 }
577             } catch (Exception $e) {
578                 error_log("Tine 2.0 can't setup the configured logger! The Server responded: $e");
579                 $writer = ($_defaultWriter === NULL) ? new Zend_Log_Writer_Null() : $_defaultWriter;
580                 $logger->addWriter($writer);
581             }
582         } else {
583             $writer = new Zend_Log_Writer_Syslog(array(
584                 'application'   => 'Tine 2.0'
585             ));
586             
587             $filter = new Zend_Log_Filter_Priority(Zend_Log::WARN);
588             $writer->addFilter($filter);
589             $logger->addWriter($writer);
590         }
591         
592         self::set(self::LOGGER, $logger);
593
594         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) $logger->info(__METHOD__ . '::' . __LINE__ .' Logger initialized');
595         if (isset($loggerConfig) && Tinebase_Core::isLogLevel(Zend_Log::TRACE)) $logger->trace(__METHOD__ . '::' . __LINE__ 
596             .' Logger settings: ' . print_r($loggerConfig->toArray(), TRUE));
597     }
598
599     /**
600      * setup the cache and add it to zend registry
601      *
602      * @param bool $_enabled disabled cache regardless what's configured in config.inc.php
603      * 
604      * @todo use the same config keys as Zend_Cache (backend + frontend) to simplify this
605      */
606     public static function setupCache($_enabled = true)
607     {
608         $config = self::getConfig();
609         if ($config->caching && $config->caching->active) {
610             if (isset($config->caching->shared) && ($config->caching->shared === true)) {
611                 self::set(self::SHAREDCACHE, true);
612             } else {
613                 self::set(self::SHAREDCACHE, false);
614             }
615         }
616
617         // create zend cache
618         if ($_enabled === true && $config->caching && $config->caching->active) {
619             $frontendOptions = array(
620                 'lifetime'                  => ($config->caching->lifetime) ? $config->caching->lifetime : 7200,
621                 'automatic_serialization'   => true, // turn that off for more speed
622                 'caching'                   => true,
623                 'automatic_cleaning_factor' => 0,    // no garbage collection as this is done by a scheduler task
624                 'write_control'             => false, // don't read cache entry after it got written
625                 'logging'                   => (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)),
626                 'logger'                    => self::getLogger(),
627             );
628
629             $backendType = ($config->caching->backend) ? ucfirst($config->caching->backend) : 'File';
630             $backendOptions = ($config->caching->backendOptions) ? $config->caching->backendOptions->toArray() : false;
631
632             if (! $backendOptions) {
633                 switch ($backendType) {
634                     case 'File':
635                         $backendOptions = array(
636                             'cache_dir'              => ($config->caching->path)     ? $config->caching->path     : Tinebase_Core::getTempDir(),
637                             'hashed_directory_level' => ($config->caching->dirlevel) ? $config->caching->dirlevel : 4, 
638                             'logging'                => (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)),
639                             'logger'                 => self::getLogger(),
640                         );
641                         break;
642                         
643                     case 'Memcached':
644                         $host = $config->caching->host ? $config->caching->host : ($config->caching->memcached->host ? $config->caching->memcached->host : 'localhost');
645                         $port = $config->caching->port ? $config->caching->port : ($config->caching->memcached->port ? $config->caching->memcached->port : 11211);
646                         $backendOptions = array(
647                             'servers' => array(
648                                 'host' => $host,
649                                 'port' => $port,
650                                 'persistent' => TRUE
651                         ));
652                         break;
653                         
654                     case 'Redis':
655                         $host = $config->caching->host ? $config->caching->host : ($config->caching->redis->host ? $config->caching->redis->host : 'localhost');
656                         $port = $config->caching->port ? $config->caching->port : ($config->caching->redis->port ? $config->caching->redis->port : 6379);
657                         $prefix = (Setup_Controller::getInstance()->isInstalled('Tinebase')) ? Tinebase_Application::getInstance()->getApplicationByName('Tinebase')->getId() : 'TINESETUP';
658                         $prefix .= '_CACHE_';
659                         $backendOptions = array(
660                             'servers' => array(
661                                 'host'   => $host,
662                                 'port'   => $port,
663                                 'prefix' => $prefix
664                         ));
665                         break;
666                         
667                     default:
668                         $backendOptions = array();
669                         break;
670                 }
671             }
672
673             Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " cache of backend type '{$backendType}' enabled");
674             
675             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
676                 // logger is an object, that makes ugly traces :)
677                 $backendOptionsWithoutLogger = $backendOptions;
678                 if (isset($backendOptionsWithoutLogger['logger'])) {
679                     unset($backendOptionsWithoutLogger['logger']);
680                 }
681                 Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " backend options: " . print_r($backendOptionsWithoutLogger, TRUE));
682             }
683
684         } else {
685             Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . ' cache disabled');
686             $backendType = 'Test';
687             $frontendOptions = array(
688                 'caching' => false
689             );
690             $backendOptions = array(
691             );
692         }
693
694         // getting a Zend_Cache_Core object
695         try {
696             $cache = Zend_Cache::factory('Core', $backendType, $frontendOptions, $backendOptions);
697             
698         } catch (Zend_Cache_Exception $e) {
699             $enabled = FALSE;
700             if ('File' === $backendType && !is_dir($backendOptions['cache_dir'])) {
701                 // create cache directory and re-try
702                 if (mkdir($backendOptions['cache_dir'], 0770, true)) {
703                     $enabled = $_enabled;
704                 }
705             }
706             
707             Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . ' Cache error: ' . $e->getMessage());
708
709             self::setupCache($enabled);
710             return;
711         }
712
713
714         // some important caches
715         Zend_Locale::setCache($cache);
716         Zend_Translate::setCache($cache);
717         
718         Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
719         self::set(self::CACHE, $cache);
720     }
721     
722     /**
723      * places user credential cache id from cache adapter (if present) into registry
724      */
725     public static function setupUserCredentialCache()
726     {
727         try {
728             $cache = Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->getCache();
729         } catch (Zend_Db_Statement_Exception $zdse) {
730             // could not get credential cache adapter, perhaps Tine 2.0 is not installed yet
731             $cache = NULL;
732         }
733         if ($cache !== NULL) {
734             self::set(self::USERCREDENTIALCACHE, $cache);
735         }
736     }
737
738     /**
739      * initializes the session
740      */
741     public static function setupSession()
742     {
743         self::setSessionOptions(array(
744             'name' => 'TINE20SESSID'
745         ));
746         self::setSessionBackend();
747     }
748     
749     /**
750      * setup stream wrapper for tine20:// prefix
751      * 
752      */
753     public static function setupStreamWrapper()
754     {
755         if (empty(Tinebase_Core::getConfig()->filesdir)) {
756             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
757                 . " Filesdir config value not set. tine20:// streamwrapper not registered, virtual filesystem not available.");
758             return;
759         }
760         
761         stream_wrapper_register('tine20', 'Tinebase_FileSystem_StreamWrapper');
762     }
763     
764     /**
765      * start session helper function
766      * 
767      * @param array $_options
768      * @throws Exception
769      */
770     public static function startSession()
771     {
772         try {
773             $session = new Zend_Session_Namespace('TinebaseCore');
774         } catch (Exception $e) {
775             self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Session error: ' . $e->getMessage());
776             self::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $e->getTraceAsString());
777             
778             Zend_Session::expireSessionCookie();
779             
780             throw $e;
781         }
782
783         if (isset($session->currentAccount)) {
784             self::set(self::USER, $session->currentAccount);
785         }
786         if (isset($session->setupuser)) {
787             self::set(self::USER, $session->setupuser);
788         }
789         if (!isset($session->jsonKey)) {
790             $session->jsonKey = Tinebase_Record_Abstract::generateUID();
791         }
792         self::set('jsonKey', $session->jsonKey);
793         
794         self::set(self::SESSION, $session);
795         self::set(self::SESSIONID, session_id());
796         self::setDbCapabilitiesInSession($session);
797     }
798     
799     /**
800      * set database capabilities in session
801      * 
802      * @param Zend_Session_Namespace $session
803      */
804     public static function setDbCapabilitiesInSession($session)
805     {
806         if (! isset($session->dbcapabilities)) {
807             $db = Tinebase_Core::getDb();
808             $capabilities = array();
809             if ($db instanceof Zend_Db_Adapter_Pdo_Pgsql) {
810                 $capabilities['unaccent'] = Tinebase_Core::checkUnaccentExtension($db);
811             }
812             $session->dbcapabilities = $capabilities;
813         }
814     }
815     
816     /**
817      * set session options
818      * 
819      * @param array $_options
820      */
821     public static function setSessionOptions($_options = array())
822     {
823         Zend_Session::setOptions(array_merge($_options, array(
824             'cookie_httponly'   => true,
825             'hash_function'     => 1
826         )));
827         
828         if (isset($_SERVER['REQUEST_URI'])) {
829             // cut of path behind caldav/webdav (removeme when dispatching is refactored)
830             if (isset($_SERVER['REDIRECT_WEBDAV']) && $_SERVER['REDIRECT_WEBDAV'] == 'true') {
831                 $decodedUri = Sabre\DAV\URLUtil::decodePath($_SERVER['REQUEST_URI']);
832                 $baseUri = '/' . substr($decodedUri, 0, strpos($decodedUri, 'webdav/') + strlen('webdav/'));
833             } else if (isset($_SERVER['REDIRECT_CALDAV']) && $_SERVER['REDIRECT_CALDAV'] == 'true') {
834                 $decodedUri = Sabre\DAV\URLUtil::decodePath($_SERVER['REQUEST_URI']);
835                 $baseUri = '/' . substr($decodedUri, 0, strpos($decodedUri, 'caldav/') + strlen('caldav/'));
836             } else {
837                 $baseUri = dirname($_SERVER['REQUEST_URI']);
838             }
839             
840             if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
841                 $exploded = explode("/", $_SERVER['REQUEST_URI']);
842                 if (strtolower($exploded[1]) == strtolower($_SERVER['HTTP_HOST'])) {
843                      $baseUri = '/' . $_SERVER['HTTP_HOST'] . (($baseUri == '/') ? '' : $baseUri);
844                 }
845             }
846             
847             // fix for windows server with backslash directory separator
848             $baseUri = str_replace(DIRECTORY_SEPARATOR, '/', $baseUri);
849             
850             Zend_Session::setOptions(array(
851                 'cookie_path'     => $baseUri
852             ));
853         }
854         
855         if (self::isHttpsRequest()) {
856             Zend_Session::setOptions(array(
857                 'cookie_secure'     => true
858             ));
859         }
860     }
861         
862     /**
863      * set session backend
864      */
865     public static function setSessionBackend()
866     {
867         $config = self::getConfig();
868         $backendType = ($config->session && $config->session->backend) ? ucfirst($config->session->backend) : 'File';
869         $maxLifeTime = ($config->session && $config->session->lifetime) ? $config->session->lifetime : 86400; // one day is default
870         
871         switch ($backendType) {
872             case 'File':
873                 if ($config->gc_maxlifetime) {
874                     self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " config.inc.php key 'gc_maxlifetime' should be renamed to 'lifetime' and moved to 'session' group.");
875                     $maxLifeTime = $config->get('gc_maxlifetime', 86400);
876                 }
877                 Zend_Session::setOptions(array(
878                     'gc_maxlifetime'     => $maxLifeTime
879                 ));
880                 
881                 $sessionSavepath = self::getSessionDir();
882                 if (ini_set('session.save_path', $sessionSavepath) !== FALSE) {
883                     if (!is_dir($sessionSavepath)) {
884                         mkdir($sessionSavepath, 0700);
885                     }
886                 }
887                 
888                 $lastSessionCleanup = Tinebase_Config::getInstance()->get(Tinebase_Config::LAST_SESSIONS_CLEANUP_RUN);
889                 if ($lastSessionCleanup instanceof DateTime && $lastSessionCleanup > Tinebase_DateTime::now()->subHour(2)) {
890                     Zend_Session::setOptions(array(
891                         'gc_probability' => 0,
892                         'gc_divisor'     => 100
893                     ));
894                 } else if (@opendir(ini_get('session.save_path')) !== FALSE) {
895                     Zend_Session::setOptions(array(
896                         'gc_probability' => 1,
897                         'gc_divisor'     => 100
898                     ));
899                 } else {
900                     self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " Unable to initialize automatic session cleanup. Check permissions to " . ini_get('session.save_path'));
901                 }
902                 
903                 break;
904                 
905             case 'Redis':
906                 $host   = ($config->session->host) ? $config->session->host : 'localhost';
907                 $port   = ($config->session->port) ? $config->session->port : 6379;
908                 $prefix = Tinebase_Application::getInstance()->getApplicationByName('Tinebase')->getId() . '_SESSION_';
909                 
910                 Zend_Session::setOptions(array(
911                     'gc_maxlifetime' => $maxLifeTime,
912                     'save_handler'   => 'redis',
913                     'save_path'      => "tcp://$host:$port?prefix=$prefix"
914                 ));
915                 break;
916                 
917             default:
918                 break;
919         }
920         
921         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Session of backend type '{$backendType}' configured.");
922     }
923     
924     /**
925      * initializes the database connection
926      */
927     public static function setupDatabaseConnection()
928     {
929         $config = self::getConfig();
930         
931         if (isset($config->database)) {
932             $dbConfig = $config->database;
933             
934             if (! defined('SQL_TABLE_PREFIX')) {
935                 define('SQL_TABLE_PREFIX', $dbConfig->get('tableprefix') ? $dbConfig->get('tableprefix') : 'tine20_');
936             }
937             
938             $db = self::createAndConfigureDbAdapter($dbConfig->toArray());
939             Zend_Db_Table_Abstract::setDefaultAdapter($db);
940             
941             // place table prefix into the concrete adapter
942             $db->table_prefix = SQL_TABLE_PREFIX;
943             
944             self::set(self::DB, $db);
945             
946         } else {
947             die ('database section not found in central configuration file');
948         }
949     }
950     
951     /**
952      * create db adapter and configure it for Tine 2.0
953      * 
954      * @param array $dbConfigArray
955      * @param string $dbBackend
956      * @return Zend_Db_Adapter_Abstract
957      * @throws Tinebase_Exception_Backend_Database
958      * @throws Tinebase_Exception_UnexpectedValue
959      */
960     public static function createAndConfigureDbAdapter($dbConfigArray, $dbBackend = NULL)
961     {
962         if ($dbBackend === NULL) {
963             $constName = 'self::' . strtoupper($dbConfigArray['adapter']);
964             if (empty($dbConfigArray['adapter']) || ! defined($constName)) {
965                 self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Wrong/no db adapter configured (' . $dbConfigArray['adapter'] . '). Using default: ' . self::PDO_MYSQL);
966                 $dbBackend = self::PDO_MYSQL;
967                 $dbConfigArray['adapter'] = self::PDO_MYSQL;
968             } else {
969                 $dbBackend = constant($constName);
970             }
971         }
972         
973         self::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating ' . $dbBackend . ' DB adapter');
974         
975         switch ($dbBackend) {
976             case self::PDO_MYSQL:
977                 foreach (array('PDO::MYSQL_ATTR_USE_BUFFERED_QUERY', 'PDO::MYSQL_ATTR_INIT_COMMAND') as $pdoConstant) {
978                     if (! defined($pdoConstant)) {
979                         throw new Tinebase_Exception_Backend_Database($pdoConstant . ' is not defined. Please check PDO extension.');
980                     }
981                 }
982                 
983                 // force some driver options
984                 $dbConfigArray['driver_options'] = array(
985                     PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => FALSE,
986                     // set utf8 charset
987                     // @todo set to utf8mb4 / @see 0008708: switch to mysql utf8mb4
988                     PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES UTF8;",
989                 );
990                 $db = Zend_Db::factory('Pdo_Mysql', $dbConfigArray);
991                 try {
992                     // set mysql timezone to utc and activate strict mode
993                     $db->query("SET time_zone ='+0:00';");
994                     $db->query("SET SQL_MODE = 'STRICT_ALL_TABLES'");
995                     $db->query("SET SESSION group_concat_max_len = 81920");
996                 } catch (Exception $e) {
997                     self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Failed to set "SET SQL_MODE to STRICT_ALL_TABLES or timezone: ' . $e->getMessage());
998                 }
999                 break;
1000                 
1001             case self::PDO_OCI:
1002                 $db = Zend_Db::factory('Pdo_Oci', $dbConfigArray);
1003                 break;
1004                 
1005             case self::ORACLE:
1006                 $db = Zend_Db::factory(self::ORACLE, $dbConfigArray);
1007                 $db->supportPositionalParameters(true);
1008                 $db->setLobAsString(true);
1009                 break;
1010                 
1011             case self::PDO_PGSQL:
1012                 unset($dbConfigArray['adapter']);
1013                 unset($dbConfigArray['tableprefix']);
1014                 if (empty($dbConfigArray['port'])) {
1015                     $dbConfigArray['port'] = 5432;
1016                 }
1017                 $db = Zend_Db::factory('Pdo_Pgsql', $dbConfigArray);
1018                 try {
1019                     // set mysql timezone to utc and activate strict mode
1020                     $db->query("SET timezone ='+0:00';");
1021                     // PostgreSQL has always been strict about making sure data is valid before allowing it into the database
1022                 } catch (Exception $e) {
1023                     self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Failed to set "SET timezone: ' . $e->getMessage());
1024                 }
1025                 break;
1026                 
1027             default:
1028                 throw new Tinebase_Exception_UnexpectedValue('Invalid database adapter defined. Please set adapter to ' . self::PDO_MYSQL . ' or ' . self::PDO_OCI . ' in config.inc.php.');
1029                 break;
1030         }
1031         
1032         return $db;
1033     }
1034     
1035     /**
1036      * get value of session variable "unaccent"
1037      * 
1038      * @param Zend_Db_Adapter_Abstract $db
1039      * @return boolean $valueUnaccent
1040      * 
1041      * @todo should be moved to pgsql adapter / helper functions
1042      */
1043     public static function checkUnaccentExtension($db)
1044     {
1045         $tableName = 'pg_extension';
1046         $cols = 'COUNT(*)';
1047
1048         $select = $db->select()
1049             ->from($tableName, $cols)
1050             ->where("extname = 'unaccent'");
1051
1052         // if there is no table pg_extension, returns 0 (false)
1053         try {
1054             // checks if unaccent extension is installed or not
1055             // (1 - yes; unaccent found)
1056             $result = (bool) $db->fetchOne($select);
1057         } catch (Zend_Db_Statement_Exception $zdse) {
1058             // (0 - no; unaccent not found)
1059             self::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Unaccent extension disabled (' . $zdse->getMessage() . ')');
1060             $result = FALSE;
1061         }
1062         
1063         return $result;
1064     }
1065
1066     /**
1067      * get db profiling
1068      * 
1069      * Enable db profiling like this (in config.inc.php):
1070      * 
1071      *   'database' => 
1072      *      array(
1073      *         [...] // db connection params  
1074      *         'profiler' => TRUE
1075      *      ),
1076      *   'profiler' =>
1077      *      array(
1078      *         'queryProfiles' => TRUE,
1079      *         'queryProfilesDetails' => TRUE,
1080      *         'user' => 'loginname',             // only profile this user 
1081      *         'profilerFilterElapsedSecs' => 1,  // only show queries whose elapsed time is equal or greater than this
1082      *      )
1083      *    ),
1084      * 
1085      */
1086     public static function getDbProfiling()
1087     {
1088         if (! self::getConfig() || ! self::getConfig()->database || ! (bool) self::getConfig()->database->profiler) {
1089             return;
1090         }
1091         
1092         $config = self::getConfig()->profiler;
1093         
1094         if ($config->user && is_object(self::getUser()) && $config->user !== self::getUser()->accountLoginName) {
1095             return;
1096         }
1097         
1098         $profiler = Zend_Db_Table::getDefaultAdapter()->getProfiler();
1099         
1100         if (! empty($config->profilerFilterElapsedSecs)) {
1101             $profiler->setFilterElapsedSecs($config->profilerFilterElapsedSecs);
1102         }
1103         
1104         $data = array(
1105             'totalNumQueries' => $profiler->getTotalNumQueries(),
1106             'totalElapsedSec' => $profiler->getTotalElapsedSecs(),
1107             'longestTime'        => 0,
1108             'longestQuery'       => ''
1109         );
1110         
1111         if ($config && (bool) $config->queryProfiles) {
1112             $queryProfiles = $profiler->getQueryProfiles();
1113             if (is_array($queryProfiles)) {
1114                 $data['queryProfiles'] = array();
1115                 foreach ($queryProfiles as $profile) {
1116                     if ((bool) $config->queryProfilesDetails) {
1117                         $data['queryProfiles'][] = array(
1118                             'query'       => $profile->getQuery(),
1119                             'elapsedSecs' => $profile->getElapsedSecs(),
1120                         );
1121                     }
1122                     
1123                     if ($profile->getElapsedSecs() > $data['longestTime']) {
1124                         $data['longestTime']  = $profile->getElapsedSecs();
1125                         $data['longestQuery'] = $profile->getQuery();
1126                     }
1127                 }
1128             }
1129         }
1130         
1131         self::getLogger()->debug(__METHOD__ . ' (' . __LINE__ . ') value: ' . print_r($data, true));
1132     }
1133
1134     /**
1135      * sets the user locale
1136      *
1137      * @param  string $localeString
1138      * @param  bool   $saveaspreference
1139      */
1140     public static function setupUserLocale($localeString = 'auto', $saveaspreference = FALSE)
1141     {
1142         $session = self::get(self::SESSION);
1143
1144         if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " given localeString '$localeString'");
1145
1146         // get locale object from session or ...
1147         if (   $session !== NULL 
1148             && isset($session->userLocale) 
1149             && is_object($session->userLocale) 
1150             && ($session->userLocale->toString() === $localeString || $localeString === 'auto')
1151         ) {
1152             $locale = $session->userLocale;
1153
1154             if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__
1155                 . " Got locale from session : " . (string)$locale);
1156             
1157         // ... create new locale object
1158         } else {
1159             if ($localeString === 'auto') {
1160                 // check if cookie or pref with language is available
1161                 if (isset($_COOKIE['TINE20LOCALE'])) {
1162                     $localeString = $_COOKIE['TINE20LOCALE'];
1163                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__
1164                         . " Got locale from cookie: '$localeString'");
1165                     
1166                 } elseif (isset($session->currentAccount)) {
1167                     $localeString = self::getPreference()->getValue(Tinebase_Preference::LOCALE, 'auto');
1168                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
1169                         . " Got locale from preference: '$localeString'");
1170                 } else {
1171                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
1172                         . " Try to detect the locale of the user (browser, environment, default)");
1173                 }
1174             }
1175             
1176             $locale = Tinebase_Translation::getLocale($localeString);
1177     
1178             // save in session
1179             if ($session !== NULL) {
1180                 $session->userLocale = $locale;
1181             }
1182         }
1183         
1184         // save in registry
1185         self::set('locale', $locale);
1186         
1187         $localeString = (string)$locale;
1188         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) self::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Setting user locale: " . $localeString);
1189         
1190         // save locale as preference
1191         if (is_object(Tinebase_Core::getUser()) && ($saveaspreference || self::getPreference()->{Tinebase_Preference::LOCALE} === 'auto')) {
1192             self::getPreference()->{Tinebase_Preference::LOCALE} = $localeString;
1193         }
1194         
1195         // set correct ctype locale, to make sure that the filesystem functions like basename() are working correctly with utf8 chars
1196         $ctypeLocale = setlocale(LC_CTYPE, 0);
1197         if (! preg_match('/utf-?8/i', $ctypeLocale)) {
1198             // use en_US as fallback locale if region string is missing
1199             $newCTypeLocale = ((strpos($localeString, '_') !== FALSE) ? $localeString : 'en_US') . '.UTF8';
1200             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
1201                 . ' Setting CTYPE locale from "' . $ctypeLocale . '" to "' . $newCTypeLocale . '".');
1202             setlocale(LC_CTYPE, $newCTypeLocale);
1203         }
1204     }
1205
1206     /**
1207      * intializes the timezone handling
1208      *
1209      * @param  string $_timezone
1210      * @param  bool   $_saveaspreference
1211      * @return string
1212      */
1213     public static function setupUserTimezone($_timezone = NULL, $_saveaspreference = FALSE)
1214     {
1215         $session = self::get(self::SESSION);
1216
1217         if ($_timezone === NULL) {
1218             
1219             if ($session instanceof Zend_Session_Namespace && isset($session->timezone)) {
1220                 $timezone = $session->timezone;
1221             } else {
1222                 // get timezone from preferences
1223                 $timezone = self::getPreference()->getValue(Tinebase_Preference::TIMEZONE);
1224                 if ($session instanceof Zend_Session_Namespace) {
1225                     $session->timezone = $timezone;
1226                 }
1227             }
1228
1229         } else {
1230             $timezone = $_timezone;
1231             if ($session instanceof Zend_Session_Namespace) {
1232                 $session->timezone = $timezone;
1233             }
1234             
1235             if ($_saveaspreference) {
1236                 // save as user preference
1237                 self::getPreference()->setValue(Tinebase_Preference::TIMEZONE, $timezone);
1238             }
1239         }
1240
1241         self::set(self::USERTIMEZONE, $timezone);
1242
1243         return $timezone;
1244     }
1245
1246     /**
1247      * set php execution life (max) time
1248      *
1249      * @param int $_seconds
1250      * @return int old max exexcution time in seconds
1251      */
1252     public static function setExecutionLifeTime($_seconds)
1253     {
1254         $oldMaxExcecutionTime = ini_get('max_execution_time');
1255         
1256         if ($oldMaxExcecutionTime > 0) {
1257             if ((bool)ini_get('safe_mode') === true) {
1258                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
1259                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
1260                         . ' max_execution_time(' . $oldMaxExcecutionTime . ') is too low. Can\'t set limit to ' 
1261                         . $_seconds . ' because of safe mode restrictions.');
1262                 }
1263             } else {
1264                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
1265                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' setting execution life time to: ' . $_seconds);
1266                 }
1267                 set_time_limit($_seconds);
1268             }
1269         }
1270         
1271         return $oldMaxExcecutionTime;
1272     }
1273     
1274     /**
1275      * set php memory (max) limit
1276      *
1277      * @param string $_limit
1278      * @return string old max memory limit
1279      */
1280     public static function setMemoryLimit($_limit)
1281     {
1282         $oldMaxMemoryLimit = ini_get('memory_limit');
1283         
1284         if (! empty($oldMaxMemoryLimit)) {
1285             if ((bool)ini_get('safe_mode') === true) {
1286                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
1287                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
1288                         . ' memory_limit(' . $oldMaxMemoryLimit . ') is too low. Can\'t set limit to ' 
1289                         . $_limit . ' because of safe mode restrictions.');
1290                 }
1291             } else {
1292                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
1293                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' setting memory limit to: ' . $_limit);
1294                 }
1295                 ini_set('memory_limit', $_limit);
1296             }
1297         }
1298         
1299         return $oldMaxMemoryLimit;
1300     }
1301     
1302     /**
1303      * log memory usage
1304      *
1305      */
1306     public static function logMemoryUsage()
1307     {
1308         if (function_exists('memory_get_peak_usage')) {
1309             $memory = memory_get_peak_usage(true);
1310         } else {
1311             $memory = memory_get_usage(true);
1312         }
1313         
1314         return  ' Memory usage: ' . ($memory / 1024 / 1024) . ' MB';
1315     }
1316     
1317     public static function logCacheSize()
1318     {
1319         if(function_exists('realpath_cache_size')) {
1320             $realPathCacheSize = realpath_cache_size();
1321         } else {
1322             $realPathCacheSize = 'unknown';
1323         }
1324         
1325         return ' Real patch cache size: ' . $realPathCacheSize;
1326     }
1327
1328     /******************************* REGISTRY ************************************/
1329
1330     /**
1331      * get a value from the registry
1332      *
1333      */
1334     public static function get($index)
1335     {
1336         return (Zend_Registry::isRegistered($index)) ? Zend_Registry::get($index) : NULL;
1337     }
1338
1339     /**
1340      * set a registry value
1341      *
1342      * @return mixed value
1343      */
1344     public static function set($index, $value)
1345     {
1346         Zend_Registry::set($index, $value);
1347     }
1348
1349     /**
1350      * checks a registry value
1351      *
1352      * @return boolean
1353      */
1354     public static function isRegistered($index)
1355     {
1356         return Zend_Registry::isRegistered($index);
1357     }
1358
1359     /**
1360      * Returns the auth typ from config or default value
1361      *
1362      * @return String
1363      */
1364     public static function getAuthType()
1365     {
1366         if (isset(Tinebase_Core::getConfig()->authentication)) {
1367             $authType = Tinebase_Core::getConfig()->authentication->get('backend', Tinebase_Auth::SQL);
1368         } else {
1369             $authType = Tinebase_Auth::SQL;
1370         }
1371
1372         return ucfirst($authType);
1373     }
1374
1375     /**
1376      * get config from the registry
1377      *
1378      * @return Zend_Config|Zend_Config_Ini|Tinebase_Config
1379      */
1380     public static function getConfig()
1381     {
1382         if (! self::get(self::CONFIG)) {
1383             self::setupConfig();
1384         }
1385         return self::get(self::CONFIG);
1386     }
1387
1388     /**
1389      * get max configured loglevel
1390      * 
1391      * @return integer
1392      */
1393     public static function getLogLevel()
1394     {
1395         if (! ($logLevel = self::get(self::LOGLEVEL))) {
1396             $config = self::getConfig();
1397             $logLevel = Tinebase_Log::getMaxLogLevel(isset($config->logger) ? $config->logger : NULL);
1398             self::set(self::LOGLEVEL, $logLevel);
1399         }
1400         return $logLevel;
1401     }
1402     
1403     /**
1404      * check if given loglevel should be logged
1405      * 
1406      * @param  integer $_prio
1407      * @return boolean
1408      */
1409     public static function isLogLevel($_prio)
1410     {
1411         return self::getLogLevel() >= $_prio;
1412     }
1413     
1414     /**
1415      * get config from the registry
1416      *
1417      * @return Tinebase_Log the logger
1418      */
1419     public static function getLogger()
1420     {
1421         if (! self::get(self::LOGGER) instanceof Tinebase_Log) {
1422             Tinebase_Core::setupLogger();
1423         }
1424         
1425         return self::get(self::LOGGER);
1426     }
1427
1428     /**
1429      * get cache from the registry
1430      *
1431      * @return Zend_Cache_Core the cache
1432      */
1433     public static function getCache()
1434     {
1435         return self::get(self::CACHE);
1436     }
1437         
1438     /**
1439      * get session namespace from the registry
1440      *
1441      * @return Zend_Session_Namespace tinebase session namespace
1442      */
1443     public static function getSession()
1444     {
1445         return self::get(self::SESSION);
1446     }
1447
1448     /**
1449      * get locale from the registry
1450      *
1451      * @return Zend_Locale
1452      */
1453     public static function getLocale()
1454     {
1455         return self::get(self::LOCALE);
1456     }
1457         
1458     /**
1459      * get current user account
1460      *
1461      * @return Tinebase_Model_FullUser the user account record
1462      */
1463     public static function getUser()
1464     {
1465         $result = (self::isRegistered(self::USER)) ? self::get(self::USER) : NULL;
1466         return $result;
1467     }
1468
1469     /**
1470      * get preferences instance by application name (create+save it to registry if it doesn't exist)
1471      *
1472      * @param string $_application
1473      * @param boolean $_throwException throws exception if class does not exist
1474      * @return Tinebase_Preference_Abstract
1475      */
1476     public static function getPreference($_application = 'Tinebase', $_throwException = FALSE)
1477     {
1478         $result = NULL;
1479
1480         if (self::isRegistered(self::PREFERENCES)) {
1481             $prefs = self::get(self::PREFERENCES);
1482             if (isset($prefs[$_application])) {
1483                 $result = $prefs[$_application];
1484             }
1485         } else {
1486             $prefs = array();
1487         }
1488
1489         if ($result === NULL) {
1490             $prefClassName = $_application . '_Preference';
1491             if (@class_exists($prefClassName)) {
1492                 $result = new $prefClassName();
1493                 $prefs[$_application] = $result;
1494                 self::set(self::PREFERENCES, $prefs);
1495             } else if ($_throwException) {
1496                 throw new Tinebase_Exception_NotFound('No preference class found for app ' . $_application);
1497             }
1498         }
1499
1500         return $result;
1501     }
1502
1503     /**
1504      * get db adapter
1505      *
1506      * @return Zend_Db_Adapter_Abstract
1507      */
1508     public static function getDb()
1509     {
1510         if (! self::get(self::DB) instanceof Zend_Db_Adapter_Abstract) {
1511             Tinebase_Core::setupDatabaseConnection();
1512         }
1513         
1514         return self::get(self::DB);
1515     }
1516     
1517     /**
1518      * get db name
1519      * 
1520      * @return string
1521      */
1522     public static function getDbName()
1523     {
1524         if (! self::get(self::DBNAME)) {
1525             $db = self::getDb();
1526             $adapterName = get_class($db);
1527     
1528             if (empty($adapterName) || strpos($adapterName, '_') === FALSE) {
1529                 throw new Tinebase_Exception('Could not get DB adapter name.');
1530             }
1531     
1532             $adapterNameParts = explode('_', $adapterName);
1533             $type = array_pop($adapterNameParts);
1534     
1535             // special handling for Oracle
1536             $type = str_replace('Oci', self::ORACLE, $type);
1537             self::set(self::DBNAME, $type);
1538         }
1539         
1540         return self::get(self::DBNAME);
1541     }
1542
1543     /**
1544      * get temp dir string (without PATH_SEP at the end)
1545      *
1546      * @return string
1547      */
1548     public static function getTempDir()
1549     {
1550         return self::get(self::TMPDIR);
1551     }
1552
1553     /**
1554      * get session dir string (without PATH_SEP at the end)
1555      *
1556      * @return string
1557      * 
1558      * @todo remove obsolete session config paths in 2011-05
1559      */
1560     public static function getSessionDir()
1561     {
1562         $sessionDirName ='tine20_sessions';
1563         $config = self::getConfig();
1564         
1565         $sessionDir = ($config->session && $config->session->path) ? $config->session->path : null;
1566         
1567         #####################################
1568         # LEGACY/COMPATIBILITY: 
1569         # (1) had to rename session.save_path key to sessiondir because otherwise the
1570         # generic save config method would interpret the "_" as array key/value seperator
1571         # (2) moved session config to subgroup 'session'
1572         if (empty($sessionDir)) {
1573             foreach (array('session.save_path', 'sessiondir') as $deprecatedSessionDir) {
1574                 $sessionDir = $config->get($deprecatedSessionDir, null);
1575                 if ($sessionDir) {
1576                     self::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " config.inc.php key '{$deprecatedSessionDir}' should be renamed to 'path' and moved to 'session' group.");
1577                 }
1578             }
1579         }
1580         #####################################
1581
1582         if (empty($sessionDir) || !@is_writable($sessionDir)) {
1583
1584             $sessionDir = session_save_path();
1585             if (empty($sessionDir) || !@is_writable($sessionDir)) {
1586                 $sessionDir = self::guessTempDir();
1587             }
1588
1589             $sessionDir .= DIRECTORY_SEPARATOR . $sessionDirName;
1590         }
1591         return $sessionDir;
1592     }
1593     
1594     /**
1595      * returns protocol + hostname
1596      * 
1597      * @return string
1598      */
1599     public static function getHostname()
1600     {
1601         $hostname = self::get('HOSTNAME');
1602         if (! $hostname) {
1603             $request = new Sabre\HTTP\Request();
1604             $hostname = str_replace($request->getUri(), '', $request->getAbsoluteUri());
1605             self::set('HOSTNAME', $hostname);
1606         }
1607         
1608         return $hostname;
1609     }
1610     
1611     /**
1612      * Singleton instance
1613      *
1614      * @return Zend_Scheduler
1615      */
1616     public static function getScheduler()
1617     {
1618         if (! self::get(self::SCHEDULER) instanceof Zend_Scheduler) {
1619             $scheduler =  new Zend_Scheduler();
1620             $scheduler->setBackend(new Zend_Scheduler_Backend_Db(array(
1621                 'DbAdapter' => self::getDb(),
1622                 'tableName' => SQL_TABLE_PREFIX . 'scheduler',
1623                 'taskClass' => 'Tinebase_Scheduler_Task'
1624             )));
1625             
1626             self::set(self::SCHEDULER, $scheduler);
1627         }
1628         return self::get(self::SCHEDULER);
1629     }
1630
1631     /**
1632      * filter input string for database as some databases (looking at you, MySQL) can't cope with some chars
1633      * 
1634      * @param string $string
1635      * @return string
1636      *
1637      * @see 0008644: error when sending mail with note (wrong charset)
1638      * @see http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string/8215387#8215387
1639      * @see http://stackoverflow.com/questions/8491431/remove-4-byte-characters-from-a-utf-8-string
1640      */
1641     public static function filterInputForDatabase($string)
1642     {
1643         $db = self::getDb();
1644         if ($db && $db instanceof Zend_Db_Adapter_Pdo_Mysql) {
1645             $string = mbConvertTo($string);
1646             
1647             // remove 4 byte utf8
1648             $result = preg_replace('/[\xF0-\xF7].../s', '?', $string);
1649         } else {
1650             $result = $string;
1651         }
1652         
1653         return $result;
1654     }
1655 }