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