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