0011996: add fallback app icon
[tine20] / tine20 / Tinebase / Controller.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-2014 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Lars Kneschke <l.kneschke@metaways.de>
10  * 
11  */
12
13 /**
14  * the class provides functions to handle applications
15  * 
16  * @package     Tinebase
17  * @subpackage  Server
18  */
19 class Tinebase_Controller extends Tinebase_Controller_Event
20 {
21     /**
22      * holds the instance of the singleton
23      *
24      * @var Tinebase_Controller
25      */
26     private static $_instance = NULL;
27     
28     /**
29      * application name
30      *
31      * @var string
32      */
33     protected $_applicationName = 'Tinebase';
34     
35     protected $_writeAccessLog;
36     
37     /**
38      * the constructor
39      *
40      */
41     private function __construct()
42     {
43         $this->_writeAccessLog = Tinebase_Application::getInstance()->isInstalled('Tinebase')
44             && (Tinebase_Core::get('serverclassname') !== 'ActiveSync_Server_Http' 
45                 || (Tinebase_Application::getInstance()->isInstalled('ActiveSync')
46                         && !(ActiveSync_Config::getInstance()->get(ActiveSync_Config::DISABLE_ACCESS_LOG))));
47     }
48
49     /**
50      * don't clone. Use the singleton.
51      *
52      */
53     private function __clone() {}
54
55     /**
56      * the singleton pattern
57      *
58      * @return Tinebase_Controller
59      */
60     public static function getInstance()
61     {
62         if (self::$_instance === NULL) {
63             self::$_instance = new Tinebase_Controller;
64         }
65         
66         return self::$_instance;
67     }
68     
69     /**
70      * create new user session
71      *
72      * @param   string                           $loginName
73      * @param   string                           $password
74      * @param   Zend_Controller_Request_Abstract $request
75      * @param   string                           $clientIdString
76      *
77      * @return  bool
78      * @throws  Tinebase_Exception_MaintenanceMode
79      *
80      * TODO what happened to the $securitycode parameter?
81      *  ->  @param   string                           $securitycode   the security code(captcha)
82      */
83     public function login($loginName, $password, \Zend\Http\Request $request, $clientIdString = NULL)
84     {
85         $authResult = Tinebase_Auth::getInstance()->authenticate($loginName, $password);
86         
87         $accessLog = Tinebase_AccessLog::getInstance()->getAccessLogEntry($loginName, $authResult, $request, $clientIdString);
88         
89         $user = $this->_validateAuthResult($authResult, $accessLog);
90         
91         if (!($user instanceof Tinebase_Model_FullUser)) {
92             return false;
93         }
94         
95         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
96             __METHOD__ . '::' . __LINE__ . " Login with username {$accessLog->login_name} from {$accessLog->ip} succeeded.");
97
98         if (Tinebase_Core::inMaintenanceMode()) {
99             if (! $user->hasRight('Tinebase', Tinebase_Acl_Rights::MAINTENANCE)) {
100                 throw new Tinebase_Exception_MaintenanceMode();
101             }
102         }
103
104         Tinebase_AccessLog::getInstance()->setSessionId($accessLog);
105         
106         $this->initUser($user);
107         
108         $this->_updateCredentialCache($user->accountLoginName, $password);
109         
110         $this->_updateAccessLog($user, $accessLog);
111         
112         return true;
113     }
114     
115     /**
116      * get login user
117      * 
118      * @param string $_username
119      * @param Tinebase_Model_AccessLog $_accessLog
120      * @return Tinebase_Model_FullUser|NULL
121      */
122     protected function _getLoginUser($_username, Tinebase_Model_AccessLog $_accessLog)
123     {
124         $accountsController = Tinebase_User::getInstance();
125         $user = NULL;
126         
127         try {
128             // does the user exist in the user database?
129             if ($accountsController instanceof Tinebase_User_Interface_SyncAble) {
130                 /**
131                  * catch all exceptions during user data sync
132                  * either it's the first sync and no user data get synchronized or
133                  * we can work with the data synced during previous login
134                  */
135                 try {
136                     Tinebase_User::syncUser($_username,array('syncContactData' => TRUE));
137                 } catch (Exception $e) {
138                     Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Failed to sync user data for: ' . $_username . ' reason: ' . $e->getMessage());
139                     Tinebase_Exception::log($e);
140                 }
141             }
142             
143             $user = $accountsController->getFullUserByLoginName($_username);
144             
145             $_accessLog->account_id = $user->getId();
146             $_accessLog->login_name = $user->accountLoginName;
147             
148         } catch (Tinebase_Exception_NotFound $e) {
149             if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Account ' . $_username . ' not found in account storage.');
150             $_accessLog->result = Tinebase_Auth::FAILURE_IDENTITY_NOT_FOUND;
151         } catch (Zend_Db_Adapter_Exception $zdae) {
152             if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Some database connection failed: ' . $zdae->getMessage());
153             $_accessLog->result = Tinebase_Auth::FAILURE_DATABASE_CONNECTION;
154         }
155         
156         return $user;
157     }
158     
159     /**
160      * check user status
161      * 
162      * @param Tinebase_Model_FullUser $_user
163      * @param Tinebase_Model_AccessLog $_accessLog
164      */
165     protected function _checkUserStatus(Tinebase_Model_FullUser $_user, Tinebase_Model_AccessLog $_accessLog)
166     {
167         // is the user enabled?
168         if ($_accessLog->result == Tinebase_Auth::SUCCESS && $_user->accountStatus !== Tinebase_User::STATUS_ENABLED) {
169             // is the account enabled?
170             if ($_user->accountStatus == Tinebase_User::STATUS_DISABLED) {
171                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::'
172                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' is disabled');
173                 $_accessLog->result = Tinebase_Auth::FAILURE_DISABLED;
174             }
175             
176             // is the account expired?
177             else if ($_user->accountStatus == Tinebase_User::STATUS_EXPIRED) {
178                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
179                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' password is expired');
180                 $_accessLog->result = Tinebase_Auth::FAILURE_PASSWORD_EXPIRED;
181             }
182             
183             // too many login failures?
184             else if ($_user->accountStatus == Tinebase_User::STATUS_BLOCKED) {
185
186                 // first check if the current user agent should be blocked
187                 if (! Tinebase_AccessLog::getInstance()->isUserAgentBlocked($_user, $_accessLog)) {
188                     return;
189                 }
190
191                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
192                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' is blocked');
193                 $_accessLog->result = Tinebase_Auth::FAILURE_BLOCKED;
194             }
195
196             // Tinebase run permission
197             else if (! $_user->hasRight('Tinebase', Tinebase_Acl_Rights_Abstract::RUN)) {
198                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
199                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' has not permissions for Tinebase');
200                 $_accessLog->result = Tinebase_Auth::FAILURE_DISABLED;
201             }
202         }
203     }
204     
205     /**
206      * initialize user (session, locale, tz)
207      * 
208      * @param Tinebase_Model_FullUser $_user
209      * @param boolean $fixCookieHeader
210      */
211     public function initUser(Tinebase_Model_FullUser $_user, $fixCookieHeader = true)
212     {
213         Tinebase_Core::set(Tinebase_Core::USER, $_user);
214         
215         if (Tinebase_Session_Abstract::getSessionEnabled()) {
216             $this->_initUserSession($fixCookieHeader);
217         }
218         
219         // need to set locale again and because locale might not be set correctly during loginFromPost
220         // use 'auto' setting because it is fetched from cookie or preference then
221         Tinebase_Core::setupUserLocale('auto');
222         
223         // need to set userTimeZone again
224         $userTimezone = Tinebase_Core::getPreference()->getValue(Tinebase_Preference::TIMEZONE);
225         Tinebase_Core::setupUserTimezone($userTimezone);
226     }
227     
228     /**
229      * init session after successful login
230      * 
231      * @param Tinebase_Model_FullUser $user
232      * @param boolean $fixCookieHeader
233      */
234     protected function _initUserSession($fixCookieHeader = true)
235     {
236         // FIXME 0010508: Session_Validator_AccountStatus causes problems
237         //Tinebase_Session::registerValidatorAccountStatus();
238
239         Tinebase_Session::registerValidatorMaintenanceMode();
240         
241         if (Tinebase_Config::getInstance()->get(Tinebase_Config::SESSIONUSERAGENTVALIDATION, TRUE)) {
242             Tinebase_Session::registerValidatorHttpUserAgent();
243         } else {
244             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' User agent validation disabled.');
245         }
246         
247         // we only need to activate ip session validation for non-encrypted connections
248         $ipSessionValidationDefault = Tinebase_Core::isHttpsRequest() ? FALSE : TRUE;
249         if (Tinebase_Config::getInstance()->get(Tinebase_Config::SESSIONIPVALIDATION, $ipSessionValidationDefault)) {
250             Tinebase_Session::registerValidatorIpAddress();
251         } else {
252             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Session ip validation disabled.');
253         }
254         
255         if ($fixCookieHeader && Zend_Session::getOptions('use_cookies')) {
256             /** 
257              * fix php session header handling http://forge.tine20.org/mantisbt/view.php?id=4918 
258              * -> search all Set-Cookie: headers and replace them with the last one!
259              **/
260             $cookieHeaders = array();
261             foreach (headers_list() as $headerString) {
262                 if (strpos($headerString, 'Set-Cookie: TINE20SESSID=') === 0) {
263                     array_push($cookieHeaders, $headerString);
264                 }
265             }
266             header(array_pop($cookieHeaders), true);
267             /** end of fix **/
268         }
269         
270         Tinebase_Session::getSessionNamespace()->currentAccount = Tinebase_Core::getUser();
271     }
272     
273     /**
274      * login failed
275      * 
276      * @param  string                    $loginName
277      * @param  Tinebase_Model_AccessLog  $accessLog
278      */
279     protected function _loginFailed($authResult, Tinebase_Model_AccessLog $accessLog)
280     {
281         // @todo update sql schema to allow empty sessionid column
282         $accessLog->sessionid = Tinebase_Record_Abstract::generateUID();
283         $accessLog->lo = $accessLog->li;
284         $user = null;
285
286         if (Tinebase_Auth::FAILURE_CREDENTIAL_INVALID == $accessLog->result) {
287             $user = Tinebase_User::getInstance()->setLastLoginFailure($accessLog->login_name);
288         }
289
290         $loglevel = Zend_Log::INFO;
291         if (null !== $user) {
292             $accessLog->account_id = $user->getId();
293             $warnLoginFailures = Tinebase_Config::getInstance()->get(Tinebase_Config::WARN_LOGIN_FAILURES, 4);
294             if ($user->loginFailures >= $warnLoginFailures) {
295                 $loglevel = Zend_Log::WARN;
296             }
297         }
298
299         if (Tinebase_Core::isLogLevel($loglevel)) Tinebase_Core::getLogger()->log(
300             __METHOD__ . '::' . __LINE__
301                 . " Login with username {$accessLog->login_name} from {$accessLog->ip} failed ({$accessLog->result})!"
302                 . ($user ? ' Auth failure count: ' . $user->loginFailures : ''),
303             $loglevel);
304         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
305             __METHOD__ . '::' . __LINE__ . ' Auth result messages: ' . print_r($authResult->getMessages(), TRUE));
306
307         Tinebase_AccessLog::getInstance()->create($accessLog);
308     }
309     
310      /**
311      * renders and send to browser one captcha image
312      *
313      * @return array
314      */
315     public function makeCaptcha()
316     {
317         return $this->_makeImage();
318     }
319
320     /**
321      * renders and send to browser one captcha image
322      *
323      * @return array
324      */
325     protected function _makeImage()
326     {
327         $result = array();
328         $width='170';
329         $height='40';
330         $characters= mt_rand(5,7);
331         $possible = '123456789aAbBcCdDeEfFgGhHIijJKLmMnNpPqQrRstTuUvVwWxXyYZz';
332         $code = '';
333         $i = 0;
334         while ($i < $characters) {
335             $code .= substr($possible, mt_rand(0, strlen($possible)-1), 1);
336             $i++;
337         }
338         $font = './fonts/Milonga-Regular.ttf';
339         /* font size will be 70% of the image height */
340         $font_size = $height * 0.67;
341         try {
342             $image = @imagecreate($width, $height);
343             /* set the colours */
344             $text_color = imagecolorallocate($image, 20, 40, 100);
345             $noise_color = imagecolorallocate($image, 100, 120, 180);
346             /* generate random dots in background */
347             for( $i=0; $i<($width*$height)/3; $i++ ) {
348                 imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
349             }
350             /* generate random lines in background */
351             for( $i=0; $i<($width*$height)/150; $i++ ) {
352                 imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);
353             }
354             /* create textbox and add text */
355             $textbox = imagettfbbox($font_size, 0, $font, $code);
356             $x = ($width - $textbox[4])/2;
357             $y = ($height - $textbox[5])/2;
358             imagettftext($image, $font_size, 0, $x, $y, $text_color, $font , $code);
359             ob_start();
360             imagejpeg($image);
361             $image_code = ob_get_contents ();
362             ob_end_clean();
363             imagedestroy($image);
364             $result = array();
365             $result['1'] = base64_encode($image_code);
366             Tinebase_Session::getSessionNamespace()->captcha['code'] = $code;
367         } catch (Exception $e) {
368             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . $e->getMessage());
369         }
370         return $result;
371     }
372
373     /**
374      * authenticate user but don't log in
375      *
376      * @param   string $loginName
377      * @param   string $password
378      * @param   array  $remoteInfo
379      * @param   string $clientIdString
380      * @return  bool
381      */
382     public function authenticate($loginName, $password, $remoteInfo, $clientIdString = NULL)
383     {
384         $result = $this->login($loginName, $password, Tinebase_Core::get(Tinebase_Core::REQUEST), $clientIdString);
385         
386         /**
387          * we unset the Zend_Auth session variable. This way we keep the session,
388          * but the user is not logged into Tine 2.0
389          * we use this to validate passwords for OpenId for example
390          */
391         $coreSession = Tinebase_Session::getSessionNamespace();
392         unset($coreSession->Zend_Auth);
393         unset($coreSession->currentAccount);
394         
395         return $result;
396     }
397     
398     /**
399      * change user password
400      *
401      * @param string $_oldPassword
402      * @param string $_newPassword
403      * @throws  Tinebase_Exception_AccessDenied
404      * @throws  Tinebase_Exception_InvalidArgument
405      */
406     public function changePassword($_oldPassword, $_newPassword)
407     {
408         if (! Tinebase_Config::getInstance()->get(Tinebase_Config::PASSWORD_CHANGE, TRUE)) {
409             throw new Tinebase_Exception_AccessDenied('Password change not allowed.');
410         }
411         
412         $loginName = Tinebase_Core::getUser()->accountLoginName;
413         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " change password for $loginName");
414         
415         if (!Tinebase_Auth::getInstance()->isValidPassword($loginName, $_oldPassword)) {
416             throw new Tinebase_Exception_InvalidArgument('Old password is wrong.');
417         }
418         
419         Tinebase_User::getInstance()->setPassword(Tinebase_Core::getUser(), $_newPassword, true, false);
420     }
421     
422     /**
423      * switch to another user's account
424      *
425      * @param string $loginName
426      * @return boolean
427      * @throws Tinebase_Exception_AccessDenied
428      */
429     public function changeUserAccount($loginName)
430     {
431         $allowedRoleChanges = Tinebase_Config::getInstance()->get(Tinebase_Config::ROLE_CHANGE_ALLOWED);
432         
433         if (!$allowedRoleChanges) {
434             throw new Tinebase_Exception_AccessDenied('It is not allowed to switch to this account');
435         }
436         
437         $currentAccountName = Tinebase_Core::getUser()->accountLoginName;
438         
439         $allowedRoleChangesArray = $allowedRoleChanges->toArray();
440         
441         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
442             __METHOD__ . '::' . __LINE__ . ' ROLE_CHANGE_ALLOWED: ' . print_r($allowedRoleChangesArray, true));
443         
444         $user = null;
445         
446         if (isset($allowedRoleChangesArray[$currentAccountName])
447             && in_array($loginName, $allowedRoleChangesArray[$currentAccountName])
448         ) {
449             $user = Tinebase_User::getInstance()->getFullUserByLoginName($loginName);
450             Tinebase_Session::getSessionNamespace()->userAccountChanged = true;
451             Tinebase_Session::getSessionNamespace()->originalAccountName = $currentAccountName;
452             
453         } else if (Tinebase_Session::getSessionNamespace()->userAccountChanged 
454             && isset($allowedRoleChangesArray[Tinebase_Session::getSessionNamespace()->originalAccountName])
455         ) {
456             $user = Tinebase_User::getInstance()->getFullUserByLoginName(Tinebase_Session::getSessionNamespace()->originalAccountName);
457             Tinebase_Session::getSessionNamespace()->userAccountChanged = false;
458             Tinebase_Session::getSessionNamespace()->originalAccountName = null;
459         }
460         
461         if ($user) {
462             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
463                 __METHOD__ . '::' . __LINE__ . ' Switching to user account ' . $user->accountLoginName);
464             
465             $this->initUser($user, /* $fixCookieHeader = */ false);
466             return true;
467         }
468
469         return false;
470     }
471     
472     /**
473      * logout user
474      *
475      * @return void
476      */
477     public function logout()
478     {
479         if ($this->_writeAccessLog) {
480             if (Tinebase_Core::isRegistered(Tinebase_Core::USER) && is_object(Tinebase_Core::getUser())) {
481                 Tinebase_AccessLog::getInstance()->setLogout();
482             }
483         }
484     }
485     
486     /**
487      * gets image info and data
488      * 
489      * @param   string $application application which manages the image
490      * @param   string $identifier identifier of image/record
491      * @param   string $location optional additional identifier
492      * @return  Tinebase_Model_Image
493      * @throws  Tinebase_Exception_NotFound
494      * @throws  Tinebase_Exception_UnexpectedValue
495      */
496     public function getImage($application, $identifier, $location = '')
497     {
498         if ($location === 'vfs') {
499             $node = Tinebase_FileSystem::getInstance()->get($identifier);
500             $path = Tinebase_Model_Tree_Node_Path::STREAMWRAPPERPREFIX . Tinebase_FileSystem::getInstance()->getPathOfNode($node, /* $getPathAsString */ true);
501             $image = Tinebase_ImageHelper::getImageInfoFromBlob(file_get_contents($path));
502
503         } else if ($application == 'Tinebase' && $location == 'tempFile') {
504             $tempFile = Tinebase_TempFile::getInstance()->getTempFile($identifier);
505             $image = Tinebase_ImageHelper::getImageInfoFromBlob(file_get_contents($tempFile->path));
506
507         } else {
508             $appController = Tinebase_Core::getApplicationInstance($application);
509             if (!method_exists($appController, 'getImage')) {
510                 throw new Tinebase_Exception_NotFound("$application has no getImage function.");
511             }
512             $image = $appController->getImage($identifier, $location);
513         }
514
515         if (! $image instanceof Tinebase_Model_Image) {
516             if (is_array($image)) {
517                 $image = new Tinebase_Model_Image($image + array(
518                     'application' => $application,
519                     'id' => $identifier,
520                     'location' => $location
521                 ));
522             } else {
523                 throw new Tinebase_Exception_UnexpectedValue('broken image');
524             }
525         }
526
527
528         return $image;
529     }
530     
531     /**
532      * remove obsolete/outdated stuff from cache
533      * notes: CLEANING_MODE_OLD -> removes obsolete cache entries (files for file cache)
534      *        CLEANING_MODE_ALL -> removes complete cache structure (directories for file cache) + cache entries
535      * 
536      * @param string $_mode
537      */
538     public function cleanupCache($_mode = Zend_Cache::CLEANING_MODE_OLD)
539     {
540         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
541             __METHOD__ . '::' . __LINE__ . ' Cleaning up the cache (mode: ' . $_mode . ')');
542         
543         Tinebase_Core::getCache()->clean($_mode);
544     }
545     
546     /**
547      * cleanup old sessions files => needed only for filesystems based sessions
548      */
549     public function cleanupSessions()
550     {
551         $config = Tinebase_Core::getConfig();
552         
553         $backendType = ($config->session && $config->session->backend) ? ucfirst($config->session->backend) : 'File';
554         
555         if (strtolower($backendType) == 'file') {
556             $maxLifeTime = ($config->session && $config->session->lifetime) ? $config->session->lifetime : 86400;
557             $path = Tinebase_Session_Abstract::getSessionDir();
558             
559             $unlinked = 0;
560             try {
561                 $dir = new DirectoryIterator($path);
562             } catch (Exception $e) {
563                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(
564                     __METHOD__ . '::' . __LINE__ . " Could not cleanup sessions");
565                 Tinebase_Exception::log($e);
566                 return;
567             }
568             
569             foreach ($dir as $fileinfo) {
570                 if (!$fileinfo->isDot() && !$fileinfo->isLink() && $fileinfo->isFile()) {
571                     if ($fileinfo->getMTime() < Tinebase_DateTime::now()->getTimestamp() - $maxLifeTime) {
572                         unlink($fileinfo->getPathname());
573                         $unlinked++;
574                     }
575                 }
576             }
577             
578             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
579                 __METHOD__ . '::' . __LINE__ . " Deleted $unlinked expired session files");
580             
581             Tinebase_Config::getInstance()->set(Tinebase_Config::LAST_SESSIONS_CLEANUP_RUN, Tinebase_DateTime::now()->toString());
582         }
583     }
584     
585     /**
586      * spy function for unittesting of queue workers
587      * 
588      * this function writes the number of executions of itself in the given 
589      * file and optionally sleeps a given time
590      * 
591      * @param string  $filename
592      * @param int     $sleep
593      * @param int     $fail
594      */
595     public function testSpy($filename=NULL, $sleep=0, $fail=NULL)
596     {
597         $filename = $filename ? $filename : ('/tmp/'.__METHOD__);
598         $counter = file_exists($filename) ? (int) file_get_contents($filename) : 0;
599         
600         file_put_contents($filename, ++$counter);
601         
602         if ($sleep) {
603             sleep($sleep);
604         }
605         
606         if ($fail && (int) $counter <= $fail) {
607             throw new Exception('spy failed on request');
608         }
609         
610         return;
611     }
612     
613     /**
614      * handle events for Tinebase
615      * 
616      * @param Tinebase_Event_Abstract $_eventObject
617      */
618     protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
619     {
620         switch (get_class($_eventObject)) {
621             case 'Admin_Event_DeleteGroup':
622                 foreach ($_eventObject->groupIds as $groupId) {
623                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
624                         . ' Removing role memberships of group ' .$groupId );
625                     
626                     $roleIds = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($groupId, Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP);
627                     foreach ($roleIds as $roleId) {
628                         Tinebase_Acl_Roles::getInstance()->removeRoleMember($roleId, array(
629                             'id'   => $groupId,
630                             'type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP,
631                         ));
632                     }
633                 }
634                 break;
635         }
636     }
637     
638     /**
639      * update access log entry if needed
640      * 
641      * @param Tinebase_Model_FullUser $user
642      * @param Tinebase_Model_AccessLog $accessLog
643      */
644     protected function _updateAccessLog(Tinebase_Model_FullUser $user, Tinebase_Model_AccessLog $accessLog)
645     {
646         if (! $accessLog->getId()) {
647             $user->setLoginTime($accessLog->ip);
648             if ($this->_writeAccessLog) {
649                 $accessLog->setId(Tinebase_Record_Abstract::generateUID());
650                 $accessLog = Tinebase_AccessLog::getInstance()->create($accessLog);
651             }
652         }
653         
654         Tinebase_Core::set(Tinebase_Core::USERACCESSLOG, $accessLog);
655     }
656     
657     /**
658      * update credential cache
659      * 
660      * @param string $loginName
661      * @param string $password
662      */
663     protected function _updateCredentialCache($loginName, $password)
664     {
665         $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials($loginName, $password);
666         Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
667     }
668     
669     /**
670      * validate is authentication was successful, user object is available and user is not expired
671      * 
672      * @param Zend_Auth_Result $authResult
673      * @param Tinebase_Model_AccessLog $accessLog
674      * @return boolean|Tinebase_Model_FullUser
675      */
676     protected function _validateAuthResult(Zend_Auth_Result $authResult, Tinebase_Model_AccessLog $accessLog)
677     {
678         // authentication failed
679         if ($accessLog->result !== Tinebase_Auth::SUCCESS) {
680             $this->_loginFailed($authResult, $accessLog);
681             
682             return false;
683         }
684         
685         // try to retrieve user from accounts backend
686         $user = $this->_getLoginUser($authResult->getIdentity(), $accessLog);
687         
688         if ($accessLog->result !== Tinebase_Auth::SUCCESS || !$user) {
689
690             if ($user) {
691                 $accessLog->account_id = $user->getId();
692             }
693             $this->_loginFailed($authResult, $accessLog);
694             
695             return false;
696         }
697         
698         // check if user is expired or blocked
699         $this->_checkUserStatus($user, $accessLog);
700         
701         if ($accessLog->result !== Tinebase_Auth::SUCCESS) {
702             $this->_loginFailed($authResult, $accessLog);
703             
704             return false;
705         }
706         
707         return $user;
708     }
709     
710     /**
711      * returns true if user account has been changed
712      * 
713      * @return boolean
714      */
715     public function userAccountChanged()
716     {
717         try {
718             $session = Tinebase_Session::getSessionNamespace();
719         } catch (Zend_Session_Exception $zse) {
720             $session = null;
721         }
722         
723         return ($session instanceof Zend_Session_Namespace && isset($session->userAccountChanged)) 
724                 ? $session->userAccountChanged
725                 : false;
726     }
727 }