8 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
9 * @author Paul Mehrer <p.mehrer@metaways.de>
10 * @copyright Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
14 * Tinebase_Import_CalDav
20 class Tinebase_Import_CalDav_Client extends \Sabre\DAV\Client
23 * used to overwrite default retry behavior (if != null)
27 protected $_requestTries = null;
29 protected $currentUserPrincipal = '';
30 protected $calendarHomeSet = '';
31 protected $principals = array();
32 protected $principalGroups = array();
34 protected $requestLogFH;
36 const findCurrentUserPrincipalRequest =
37 '<?xml version="1.0"?>
38 <d:propfind xmlns:d="DAV:">
40 <d:current-user-principal />
44 const findCalendarHomeSetRequest =
45 '<?xml version="1.0"?>
46 <d:propfind xmlns:d="DAV:">
48 <x:calendar-home-set xmlns:x="urn:ietf:params:xml:ns:caldav"/>
52 const resolvePrincipalRequest =
53 '<?xml version="1.0"?>
54 <d:propfind xmlns:d="DAV:">
56 <d:group-member-set />
61 public function __construct(array $a)
63 parent::__construct($a);
65 //$this->requestLogFH = fopen('/var/log/tine20/requestLog', 'w');
67 $this->propertyMap['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] = 'Sabre\CalDAV\Property\SupportedCalendarComponentSet';
68 $this->propertyMap['{DAV:}acl'] = 'Sabre\DAVACL\Property\Acl';
69 $this->propertyMap['{DAV:}group-member-set'] = 'Tinebase_Import_CalDav_GroupMemberSet';
73 * findCurrentUserPrincipal
74 * - result ($this->currentUserPrincipal) is cached for 1 week
76 * @param number $tries
79 public function findCurrentUserPrincipal($tries = 1)
81 $cacheId = Tinebase_Helper::convertCacheId('findCurrentUserPrincipal' . $this->userName);
82 if (Tinebase_Core::getCache()->test($cacheId)) {
83 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
84 . ' Loading user principal from cache');
86 $this->currentUserPrincipal = Tinebase_Core::getCache()->load($cacheId);
87 $user = $this->_setUser();
95 $result = $this->calDavRequest('PROPFIND', '/principals/', self::findCurrentUserPrincipalRequest, 0, $tries);
96 if (isset($result['{DAV:}current-user-principal']))
98 $this->currentUserPrincipal = $result['{DAV:}current-user-principal'];
99 $user = $this->_setUser();
104 Tinebase_Core::getCache()->save($this->currentUserPrincipal, $cacheId, array(), /* 1 week */ 24*3600*7);
108 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' couldn\'t find current users principal');
112 protected function _setUser()
115 $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountLoginName', $this->userName, 'Tinebase_Model_FullUser');
116 Tinebase_Core::set(Tinebase_Core::USER, $user);
117 $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials($this->userName, $this->password);
118 Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
119 } catch (Tinebase_Exception_NotFound $e) {
120 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' Can\'t find tine20 user: ' . $this->userName);
124 $this->principals[$this->currentUserPrincipal] = $user;
129 public function findCurrentUserPrincipalForUsers(array &$users)
131 foreach ($users as $username => $pwd) {
132 $this->userName = $username;
133 $this->password = $pwd;
135 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
136 . ' Find principal for user ' . $this->userName);
138 if (! $this->findCurrentUserPrincipal()) {
139 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__
140 . ' Skipping ' . $username);
141 unset($users[$username]);
143 } catch (Tinebase_Exception $te) {
144 // TODO should use better exception (Not_Authenticated, ...)
145 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__
146 . ' Skipping ' . $username);
147 unset($users[$username]);
150 return count($users) > 0;
154 * findCalendarHomeSet
155 * - result ($this->calendarHomeSet) is cached for 1 week
159 public function findCalendarHomeSet()
161 if ('' == $this->currentUserPrincipal && ! $this->findCurrentUserPrincipal(/* tries = */ 3)) {
162 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
163 . ' No principal found for user ' . $this->userName);
166 $cacheId = Tinebase_Helper::convertCacheId('findCalendarHomeSet' . $this->userName);
167 if (Tinebase_Core::getCache()->test($cacheId)) {
168 $this->calendarHomeSet = Tinebase_Core::getCache()->load($cacheId);
169 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
170 . ' Loading user home set from cache');
174 $result = $this->calDavRequest('PROPFIND', $this->currentUserPrincipal, self::findCalendarHomeSetRequest);
176 if (isset($result['{urn:ietf:params:xml:ns:caldav}calendar-home-set'])) {
177 $this->calendarHomeSet = $result['{urn:ietf:params:xml:ns:caldav}calendar-home-set'];
178 Tinebase_Core::getCache()->save($this->calendarHomeSet, $cacheId, array(), /* 1 week */ 24*3600*7);
182 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' couldn\'t find calendar homeset');
189 * @param array $privileges
191 public function resolvePrincipals(array $privileges)
193 foreach ($privileges as $ace)
195 if ( $ace['principal'] == '{DAV:}authenticated' || $ace['principal'] == $this->currentUserPrincipal ||
196 isset($this->principals[$ace['principal']]) || isset($this->principalGroups[$ace['principal']])) {
200 $result = $this->calDavRequest('PROPFIND', $ace['principal'], self::resolvePrincipalRequest);
201 if (isset($result['{DAV:}group-member-set'])) {
202 $principals = $result['{DAV:}group-member-set']->getPrincipals();
203 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__
204 . ' ' . print_r($principals, true));
205 $this->principalGroups[$ace['principal']] = $result['{DAV:}group-member-set']->getPrincipals();
210 public function clearCurrentUserData()
212 $this->currentUserPrincipal = '';
213 $this->calendarHomeSet = '';
217 * perform calDavRequest
219 * @param string $method
221 * @param strubg $body
222 * @param number $depth
223 * @param number $tries
224 * @param number $sleep
225 * @throws Tinebase_Exception
227 public function calDavRequest($method, $uri, $body, $depth = 0, $tries = 10, $sleep = 30)
230 if ($this->_requestTries !== null) {
231 // overwrite default retry behavior
232 $tries = $this->_requestTries;
237 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
238 . ' Sending ' . $method . ' request for uri ' . $uri . ' ...');
239 $response = $this->request($method, $uri, $body, array(
241 'Content-Type' => 'text/xml',
243 } catch (Exception $e) {
244 if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
245 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__
246 . ' Caldav request failed: '
247 . '(' . $this->userName . ')' . $method . ' ' . $uri . "\n" . $body
248 . "\n" . $e->getMessage());
250 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
251 . ' Sleeping ' . $sleep . ' seconds and retrying ... ');
260 throw new Tinebase_Exception("no response");
263 $result = $this->parseMultiStatus($response['body']);
265 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
266 . ' Uri: ' . $uri . ' | request: ' . $body . ' | response: ' . print_r($response, true));
268 // If depth was 0, we only return the top item
271 $result = current($result);
272 $result = isset($result[200])?$result[200]:array();
274 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
275 . ' Result (depth 0): ' . var_export($result, true));
280 $newResult = array();
281 foreach($result as $href => $statusList)
283 $newResult[$href] = isset($statusList[200])?$statusList[200]:array();
286 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
287 . ' Result: ' . var_export($newResult, true));
293 * Parses a WebDAV multistatus response body
295 * @param string $body xml body
298 public function parseMultiStatus($body)
300 // remove possible broken chars here to avoid simplexml_load_string errors
301 return parent::parseMultiStatus(Tinebase_Helper::removeIllegalXMLChars($body));