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