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