12e3d5194187b14cc3c0f430eaa9ec6ace6956d8
[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             }
1056         }
1057         
1058         self::getLogger()->debug(__METHOD__ . ' (' . __LINE__ . ') value: ' . print_r($data, true));
1059     }
1060
1061     /**
1062      * sets the user locale
1063      *
1064      * @param  string $localeString
1065      */
1066     public static function setupUserLocale($localeString = 'auto')
1067     {
1068         try {
1069             $session = Tinebase_Session::getSessionNamespace();
1070         } catch (Zend_Session_Exception $zse) {
1071             $session = null;
1072         }
1073         
1074         if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " given localeString '$localeString'");
1075         
1076         // get locale object from session or ...
1077         if (   $session !== NULL
1078             && isset($session->userLocale)
1079             && is_object($session->userLocale)
1080             && ($session->userLocale->toString() === $localeString || $localeString === 'auto')
1081         ) {
1082             $locale = $session->userLocale;
1083
1084             if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__
1085                 . " Got locale from session : " . (string)$locale);
1086             
1087         // ... create new locale object
1088         } else {
1089             if ($localeString === 'auto') {
1090                 // check if cookie or pref with language is available
1091                 if (isset($_COOKIE['TINE20LOCALE'])) {
1092                     $localeString = $_COOKIE['TINE20LOCALE'];
1093                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__
1094                         . " Got locale from cookie: '$localeString'");
1095                     
1096                 } elseif (isset($session->currentAccount)) {
1097                     $localeString = self::getPreference()->getValue(Tinebase_Preference::LOCALE, 'auto');
1098                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
1099                         . " Got locale from preference: '$localeString'");
1100                 } else {
1101                     if (self::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
1102                         . " Try to detect the locale of the user (browser, environment, default)");
1103                 }
1104             }
1105             
1106             $locale = Tinebase_Translation::getLocale($localeString);
1107             
1108             // save in session
1109             if ($session !== NULL) {
1110                 $session->userLocale = $locale;
1111             }
1112             
1113             // check if the detected locale should be saved in preferences
1114             if ($localeString === 'auto' && is_object(Tinebase_Core::getUser()) && (string)$locale !== 'en') {
1115                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) self::getLogger()->debug(__METHOD__ . '::' . __LINE__
1116                     . " Saving locale: " . (string)$locale);
1117                 self::getPreference()->{Tinebase_Preference::LOCALE} = (string)$locale;
1118             }
1119         }
1120         
1121         // save in registry
1122         self::set('locale', $locale);
1123         
1124         $localeString = (string)$locale;
1125         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) self::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Setting user locale: " . $localeString);
1126         
1127         // set correct ctype locale, to make sure that the filesystem functions like basename() are working correctly with utf8 chars
1128         $ctypeLocale = setlocale(LC_CTYPE, 0);
1129         if (! preg_match('/utf-?8/i', $ctypeLocale)) {
1130             // use en_US as fallback locale if region string is missing
1131             $newCTypeLocale = ((strpos($localeString, '_') !== FALSE) ? $localeString : 'en_US') . '.UTF8';
1132             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
1133                 . ' Setting CTYPE locale from "' . $ctypeLocale . '" to "' . $newCTypeLocale . '".');
1134             setlocale(LC_CTYPE, $newCTypeLocale);
1135         }
1136     }
1137
1138     /**
1139      * intializes the timezone handling
1140      *
1141      * @param  string $_timezone
1142      * @param  bool   $_saveaspreference
1143      * @return string
1144      */
1145     public static function setupUserTimezone($_timezone = null, $_saveaspreference = FALSE)
1146     {
1147         try {
1148             $session = Tinebase_Session::getSessionNamespace();
1149         } catch (Zend_Session_Exception $zse) {
1150             $session = null;
1151         }
1152         
1153         // get timezone from session, parameter or preference
1154         if ($_timezone === null && $session instanceof Zend_Session_Namespace && isset($session->timezone)) {
1155             $timezone = $session->timezone;
1156         } else {
1157             $timezone = $_timezone;
1158         }
1159         if ($timezone === null) {
1160             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
1161                 . ' get timezone from preferences');
1162             $timezone = self::getPreference()->getValue(Tinebase_Preference::TIMEZONE);
1163         }
1164         
1165         // set timezone in registry, session and preference
1166         self::set(self::USERTIMEZONE, $timezone);
1167         if ($session instanceof Zend_Session_Namespace && Tinebase_Session::isWritable()) {
1168             $session->timezone = $timezone;
1169         }
1170         if ($_saveaspreference) {
1171             self::getPreference()->setValue(Tinebase_Preference::TIMEZONE, $timezone);
1172         }
1173         
1174         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
1175                 . ' User timezone: ' . $timezone);
1176         
1177         return $timezone;
1178     }
1179
1180     /**
1181      * set php execution life (max) time
1182      *
1183      * @param int $_seconds
1184      * @return int old max exexcution time in seconds
1185      */
1186     public static function setExecutionLifeTime($_seconds)
1187     {
1188         $oldMaxExcecutionTime = ini_get('max_execution_time');
1189         
1190         if ($oldMaxExcecutionTime > 0) {
1191             $safeModeSetting = ini_get('safe_mode');
1192             if ($safeModeSetting !== 'off' && (bool) $safeModeSetting === true) {
1193                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
1194                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
1195                         . ' max_execution_time(' . $oldMaxExcecutionTime . ') is too low. Can\'t set limit to ' 
1196                         . $_seconds . ' because of safe mode restrictions. safe_mode = ' . $safeModeSetting);
1197                 }
1198             } else {
1199                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
1200                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' setting execution life time to: ' . $_seconds);
1201                 }
1202                 set_time_limit($_seconds);
1203             }
1204         }
1205         
1206         return $oldMaxExcecutionTime;
1207     }
1208     
1209     /**
1210      * set php memory (max) limit
1211      *
1212      * @param string $_limit
1213      * @return string old max memory limit
1214      */
1215     public static function setMemoryLimit($_limit)
1216     {
1217         $oldMaxMemoryLimit = ini_get('memory_limit');
1218         
1219         if (! empty($oldMaxMemoryLimit)) {
1220             if ((bool)ini_get('safe_mode') === true) {
1221                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
1222                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
1223                         . ' memory_limit(' . $oldMaxMemoryLimit . ') is too low. Can\'t set limit to ' 
1224                         . $_limit . ' because of safe mode restrictions.');
1225                 }
1226             } else {
1227                 if (Tinebase_Core::isRegistered(self::LOGGER) && Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
1228                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' setting memory limit to: ' . $_limit);
1229                 }
1230                 ini_set('memory_limit', $_limit);
1231             }
1232         }
1233         
1234         return $oldMaxMemoryLimit;
1235     }
1236     
1237     /**
1238      * log memory usage
1239      *
1240      */
1241     public static function logMemoryUsage()
1242     {
1243         if (function_exists('memory_get_peak_usage')) {
1244             $memory = memory_get_peak_usage(true);
1245         } else {
1246             $memory = memory_get_usage(true);
1247         }
1248         
1249         return  ' Memory usage: ' . ($memory / 1024 / 1024) . ' MB';
1250     }
1251     
1252     public static function logCacheSize()
1253     {
1254         if(function_exists('realpath_cache_size')) {
1255             $realPathCacheSize = realpath_cache_size();
1256         } else {
1257             $realPathCacheSize = 'unknown';
1258         }
1259         
1260         return ' Real patch cache size: ' . $realPathCacheSize;
1261     }
1262     
1263     /******************************* REGISTRY ************************************/
1264     
1265     /**
1266      * get a value from the registry
1267      *
1268      */
1269     public static function get($index)
1270     {
1271         try {
1272             return Zend_Registry::get($index);
1273         } catch (Zend_Exception $ze) {
1274             return null;
1275         }
1276     }
1277     
1278     /**
1279      * set a registry value
1280      * 
1281      * @throws Tinebase_Exception_InvalidArgument
1282      * @return mixed value
1283      */
1284     public static function set($index, $value)
1285     {
1286         if ($index === self::USER) {
1287             if ($value === null) {
1288                 throw new Tinebase_Exception_InvalidArgument('Invalid user object!');
1289             }
1290             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
1291                 if ($value instanceof Tinebase_Model_FullUser) {
1292                     $userString =  $value->accountLoginName;
1293                 } else if ($value instanceof Tinebase_Model_User) {
1294                     $userString = $value->accountDisplayName;
1295                 } else {
1296                     $userString = var_export($value, true);
1297                 }
1298                 
1299                 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Setting user ' . $userString);
1300             }
1301         }
1302         
1303         Zend_Registry::set($index, $value);
1304     }
1305     
1306     /**
1307      * checks a registry value
1308      *
1309      * @return boolean
1310      */
1311     public static function isRegistered($index)
1312     {
1313         return Zend_Registry::isRegistered($index);
1314     }
1315
1316     /**
1317      * Returns the auth typ from config or default value
1318      *
1319      * @return String
1320      */
1321     public static function getAuthType()
1322     {
1323         if (isset(Tinebase_Core::getConfig()->authentication)) {
1324             $authType = Tinebase_Core::getConfig()->authentication->get('backend', Tinebase_Auth::SQL);
1325         } else {
1326             $authType = Tinebase_Auth::SQL;
1327         }
1328
1329         return ucfirst($authType);
1330     }
1331     
1332     /**
1333      * get config from the registry
1334      *
1335      * @return Zend_Config|Zend_Config_Ini|Tinebase_Config
1336      */
1337     public static function getConfig()
1338     {
1339         if (! self::get(self::CONFIG)) {
1340             self::setupConfig();
1341         }
1342         return self::get(self::CONFIG);
1343     }
1344     
1345     /**
1346      * get max configured loglevel
1347      * 
1348      * @return integer
1349      */
1350     public static function getLogLevel()
1351     {
1352         if (! ($logLevel = self::get(self::LOGLEVEL))) {
1353             $config = self::getConfig();
1354             $logLevel = Tinebase_Log::getMaxLogLevel(isset($config->logger) ? $config->logger : NULL);
1355             self::set(self::LOGLEVEL, $logLevel);
1356         }
1357         
1358         return $logLevel;
1359     }
1360     
1361     /**
1362      * check if given loglevel should be logged
1363      * 
1364      * @param  integer $_prio
1365      * @return boolean
1366      */
1367     public static function isLogLevel($_prio)
1368     {
1369         if (! isset(self::$logLevel) || self::$logLevel === 0 ) {
1370             self::$logLevel = self::getLogLevel();
1371         }
1372
1373         return self::$logLevel >= $_prio;
1374     }
1375     
1376     /**
1377      * get config from the registry
1378      *
1379      * @return Tinebase_Log the logger
1380      */
1381     public static function getLogger()
1382     {
1383         if (! self::get(self::LOGGER) instanceof Tinebase_Log) {
1384             Tinebase_Core::setupLogger();
1385         }
1386         
1387         return self::get(self::LOGGER);
1388     }
1389     
1390     /**
1391      * get cache from the registry
1392      *
1393      * @return Zend_Cache_Core the cache
1394      */
1395     public static function getCache()
1396     {
1397         return self::get(self::CACHE);
1398     }
1399     
1400     /**
1401      * get credentials cache from the registry or initialize it
1402      *
1403      * @return  Tinebase_Model_CredentialCache
1404      */
1405     public static function getUserCredentialCache()
1406     {
1407         if (! self::get(self::USERCREDENTIALCACHE) instanceof Tinebase_Model_CredentialCache && self::getUser()) {
1408             try {
1409                 $cache = Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter()->getCache();
1410             } catch (Zend_Db_Statement_Exception $zdse) {
1411                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
1412                     . " Could not get credential cache adapter, perhaps Tine 2.0 is not installed yet");
1413                 $cache = NULL;
1414             }
1415         
1416             if ($cache !== NULL) {
1417                 self::set(self::USERCREDENTIALCACHE, $cache);
1418             }
1419         }
1420         
1421         return self::get(self::USERCREDENTIALCACHE);
1422     }
1423     
1424     /**
1425      * get locale from the registry
1426      *
1427      * @return Zend_Locale
1428      */
1429     public static function getLocale()
1430     {
1431         return self::get(self::LOCALE);
1432     }
1433         
1434     /**
1435      * get current user account
1436      *
1437      * @return Tinebase_Model_FullUser the user account record
1438      */
1439     public static function getUser()
1440     {
1441         return self::get(self::USER);
1442     }
1443
1444     /**
1445      * get current users timezone
1446      *
1447      * @return string the current users timezone string
1448      */
1449     public static function getUserTimezone()
1450     {
1451         if (!self::isRegistered(self::USERTIMEZONE) || self::get(self::USERTIMEZONE) == NULL) {
1452             return Tinebase_Core::setupUserTimezone();
1453         }
1454         
1455         return self::get(self::USERTIMEZONE);
1456     }
1457
1458     /**
1459      * get preferences instance by application name (create+save it to registry if it doesn't exist)
1460      *
1461      * @param string $_application
1462      * @param boolean $_throwException throws exception if class does not exist
1463      * @return Tinebase_Preference_Abstract
1464      */
1465     public static function getPreference($_application = 'Tinebase', $_throwException = FALSE)
1466     {
1467         $result = NULL;
1468
1469         if (self::isRegistered(self::PREFERENCES)) {
1470             $prefs = self::get(self::PREFERENCES);
1471             if (isset($prefs[$_application])) {
1472                 $result = $prefs[$_application];
1473             }
1474         } else {
1475             $prefs = array();
1476         }
1477
1478         if ($result === NULL) {
1479             $prefClassName = $_application . '_Preference';
1480             if (@class_exists($prefClassName)) {
1481                 $result = new $prefClassName();
1482                 $prefs[$_application] = $result;
1483                 self::set(self::PREFERENCES, $prefs);
1484             } else if ($_throwException) {
1485                 throw new Tinebase_Exception_NotFound('No preference class found for app ' . $_application);
1486             }
1487         }
1488
1489         return $result;
1490     }
1491     
1492     /**
1493      * get db adapter
1494      *
1495      * @return Zend_Db_Adapter_Abstract
1496      */
1497     public static function getDb()
1498     {
1499         if (! self::get(self::DB) instanceof Zend_Db_Adapter_Abstract) {
1500             Tinebase_Core::setupDatabaseConnection();
1501         }
1502         
1503         return self::get(self::DB);
1504     }
1505     
1506     /**
1507      * get db name
1508      * 
1509      * @return string
1510      */
1511     public static function getDbName()
1512     {
1513         if (! self::get(self::DBNAME)) {
1514             $db = self::getDb();
1515             $adapterName = get_class($db);
1516     
1517             if (empty($adapterName) || strpos($adapterName, '_') === FALSE) {
1518                 throw new Tinebase_Exception('Could not get DB adapter name.');
1519             }
1520     
1521             $adapterNameParts = explode('_', $adapterName);
1522             $type = array_pop($adapterNameParts);
1523     
1524             // special handling for Oracle
1525             $type = str_replace('Oci', self::ORACLE, $type);
1526             self::set(self::DBNAME, $type);
1527         }
1528         
1529         return self::get(self::DBNAME);
1530     }
1531     
1532     /**
1533      * get temp dir string (without PATH_SEP at the end)
1534      *
1535      * @return string
1536      */
1537     public static function getTempDir()
1538     {
1539         return self::get(self::TMPDIR);
1540     }
1541     
1542     /**
1543      * returns protocol + hostname
1544      * 
1545      * @return string
1546      */
1547     public static function getHostname()
1548     {
1549         $hostname = self::get('HOSTNAME');
1550         if (! $hostname) {
1551             $request = new Sabre\HTTP\Request();
1552             $hostname = strlen($request->getUri()) > 1 ?
1553                 str_replace($request->getUri(), '', $request->getAbsoluteUri()) :
1554                 $request->getAbsoluteUri();
1555
1556             self::set('HOSTNAME', $hostname);
1557         }
1558         
1559         return $hostname;
1560     }
1561     
1562     /**
1563      * Singleton instance
1564      *
1565      * @return Zend_Scheduler
1566      */
1567     public static function getScheduler()
1568     {
1569         if (! self::get(self::SCHEDULER) instanceof Zend_Scheduler) {
1570             $scheduler =  new Zend_Scheduler();
1571             $scheduler->setBackend(new Zend_Scheduler_Backend_Db(array(
1572                 'DbAdapter' => self::getDb(),
1573                 'tableName' => SQL_TABLE_PREFIX . 'scheduler',
1574                 'taskClass' => 'Tinebase_Scheduler_Task'
1575             )));
1576             
1577             self::set(self::SCHEDULER, $scheduler);
1578         }
1579         return self::get(self::SCHEDULER);
1580     }
1581     
1582     /**
1583      * filter input string for database as some databases (looking at you, MySQL) can't cope with some chars
1584      * 
1585      * @param string $string
1586      * @return string
1587      *
1588      * @see 0008644: error when sending mail with note (wrong charset)
1589      * @see http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string/8215387#8215387
1590      * @see http://stackoverflow.com/questions/8491431/remove-4-byte-characters-from-a-utf-8-string
1591      */
1592     public static function filterInputForDatabase($string)
1593     {
1594         if (self::getDb() instanceof Zend_Db_Adapter_Pdo_Mysql) {
1595             $string = Tinebase_Helper::mbConvertTo($string);
1596             
1597             // remove 4 byte utf8
1598             $result = preg_replace('/[\xF0-\xF7].../s', '?', $string);
1599         } else {
1600             $result = $string;
1601         }
1602         
1603         return $result;
1604     }
1605     
1606     /**
1607      * checks if a system command exists. Works on POSIX systems.
1608      * 
1609      * @param string $name
1610      * @return bool
1611      */
1612     public static function systemCommandExists($name)
1613     {
1614         $ret = shell_exec('which ' . $name);
1615         return ! empty($ret);
1616     }
1617     
1618     /**
1619      * calls a system command with escapeshellcmd
1620      * 
1621      * @param string $cmd
1622      * @return bool
1623      */
1624     public static function callSystemCommand($cmd)
1625     {
1626         return shell_exec(escapeshellcmd($cmd));
1627     }
1628 }