0010080: caldav client / import caldav tasks/task lists via CLI
[tine20] / tine20 / Calendar / Import / CalDav / Client.php
1 <?php
2 //./tine20.php --username unittest --method Calendar.importCalDav url="https://osx-testfarm-mavericks-server.hh.metaways.de:8443" caldavuserfile=caldavuserfile.csv
3
4 /**
5  * Tine 2.0
6  * 
7  * @package     Calendar
8  * @subpackage  Import
9  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
10  * @author      Paul Mehrer <p.mehrer@metaways.de>
11  * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
12  */
13
14 /**
15  * Calendar_Import_CalDAV
16  * 
17  * @package     Calendar
18  * @subpackage  Import
19  */
20 class Calendar_Import_CalDav_Client extends Tinebase_Import_CalDav_Client
21 {
22     protected $calendars = array();
23     protected $calendarICSs = array();
24     protected $existingRecordIds = array();
25     protected $maxBulkRequest = 20;
26     protected $mapToDefaultContainer = 'calendar';
27     protected $decorator = null;
28     
29     protected $component = 'VEVENT';
30     protected $skipComonent = 'VTODO';
31     protected $modelName = 'Calendar_Model_Event';
32     protected $appName = 'Calendar';
33     protected $webdavFrontend = 'Calendar_Frontend_WebDAV_Event';
34     
35     const findAllCalendarsRequest =
36 '<?xml version="1.0"?>
37 <d:propfind xmlns:d="DAV:">
38   <d:prop>
39     <d:resourcetype />
40     <d:acl />
41     <d:displayname />
42     <x:supported-calendar-component-set xmlns:x="urn:ietf:params:xml:ns:caldav"/>
43   </d:prop>
44 </d:propfind>';
45     
46     const findAllCalendarICSsRequest = 
47 '<?xml version="1.0"?>
48 <d:propfind xmlns:d="DAV:">
49   <d:prop>
50     <x:calendar-data xmlns:x="urn:ietf:params:xml:ns:caldav"/>
51   </d:prop>
52 </d:propfind>';
53     
54     const getAllCalendarDataRequest =
55 '<?xml version="1.0"?>
56 <b:calendar-multiget xmlns:a="DAV:" xmlns:b="urn:ietf:params:xml:ns:caldav">
57   <a:prop>
58     <b:calendar-data />
59     <a:getetag />
60   </a:prop>
61 ';
62     
63     const getEventETagsRequest =
64 '<?xml version="1.0"?>
65 <b:calendar-multiget xmlns:a="DAV:" xmlns:b="urn:ietf:params:xml:ns:caldav">
66   <a:prop>
67     <a:getetag />
68   </a:prop>
69 ';
70     
71     public function __construct(array $a, $flavor)
72     {
73         parent::__construct($a);
74         
75         $flavor = 'Calendar_Import_CalDav_Decorator_' . $flavor;
76         $this->decorator = new $flavor($this);
77     }
78     
79     public function findAllCalendars()
80     {
81         if ('' == $this->calendarHomeSet && ! $this->findCalendarHomeSet())
82             return false;
83         
84         //issue with follow location in curl!?!?
85         if ($this->calendarHomeSet[strlen($this->calendarHomeSet)-1] !== '/')
86             $this->calendarHomeSet .= '/';
87         
88         try {
89             $result = $this->calDavRequest('PROPFIND', $this->calendarHomeSet, $this->decorator->preparefindAllCalendarsRequest(self::findAllCalendarsRequest), 1);
90         } catch (Tinebase_Exception $te) {
91             if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
92                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' request failed');
93             Tinebase_Exception::log($te);
94             return false;
95         }
96         
97         foreach ($result as $uri => $response) {
98             if (isset($response['{DAV:}resourcetype']) && isset($response['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']) && 
99                     $response['{DAV:}resourcetype']->is('{urn:ietf:params:xml:ns:caldav}calendar') &&
100                     in_array($this->component, $response['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue())) {
101                 $this->calendars[$uri]['acl'] = $response['{DAV:}acl'];
102                 $this->calendars[$uri]['displayname'] = $response['{DAV:}displayname'];
103                 $this->decorator->processAdditionalCalendarProperties($this->calendars[$uri], $response);
104                 $this->resolvePrincipals($this->calendars[$uri]['acl']->getPrivileges());
105             }
106         }
107         
108         if (count($this->calendars) > 0) {
109             return true;
110         } else {
111             if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
112                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' couldn\'t find a calendar');
113             return false;
114         }
115     }
116     
117     public function findAllCalendarICSs()
118     {
119         if (count($this->calendars) < 1 && ! $this->findAllCalendars())
120             return false;
121         
122         foreach ($this->calendars as $calUri => $calendar) {
123             $result = $this->calDavRequest('PROPFIND', $calUri, self::findAllCalendarICSsRequest, 1);
124             foreach ($result as $ics => $value) {
125                 if (strpos($ics, '.ics') !== FALSE)
126                     $this->calendarICSs[$calUri][] = $ics;
127             }
128         }
129         
130         if (count($this->calendarICSs) > 0) {
131             return true;
132         } else {
133             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE))
134                 Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' all found calendars are empty');
135             return false;
136         }
137     }
138     
139     protected function findContainerForCalendar($calendarUri, $displayname, $defaultCalendarsName, $type, $application_id)
140     {
141         // sha1() the whole calendar uri as it is very hard to separate a uuid string from the uri otherwise
142         $uuid = sha1($calendarUri);
143         
144         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__
145                 . ' $calendarUri = ' . $calendarUri . ' / $displayname = ' . $displayname 
146                 . ' / $defaultCalendarsName = ' . $defaultCalendarsName . ' / $uuid = ' . $uuid);
147         
148         $filter = new Tinebase_Model_ContainerFilter(array(
149             array(
150                 'field' => 'uuid', 
151                 'operator' => 'equals', 
152                 'value' => $uuid
153             ),
154             array(
155                 'field' => 'model', 
156                 'operator' => 'equals', 
157                 'value' => $this->modelName
158             ),
159         ));
160         $existingCalendar = Tinebase_Container::getInstance()->search($filter)->getFirstRecord();
161         if ($existingCalendar) {
162             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__
163                 . ' Found existing container ' . $existingCalendar->name . ' (id: ' . $existingCalendar->getId() . ')');
164             return $existingCalendar;
165         }
166         
167         $counter = '';
168         
169         if ($defaultCalendarsName == $displayname) {
170             $existingCalendar = Tinebase_Container::getInstance()->getDefaultContainer($this->modelName);
171             if (! $existingCalendar->uuid) {
172                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__
173                     . ' Found existing calendar with the same name');
174                 $existingCalendar->uuid = $uuid;
175                 return $existingCalendar;
176             }
177             $existingCalendar = null;
178             $counter = 1;
179         }
180         
181         try {
182             while (true) {
183                 $existingCalendar = Tinebase_Container::getInstance()->getContainerByName($this->appName, $displayname . $counter, $type, Tinebase_Core::getUser());
184                 
185                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
186                     . ' Got calendar: ' . $existingCalendar->name . ' (id: ' . $existingCalendar->getId() . ')');
187                 
188                 if (! $existingCalendar->uuid) {
189                     $existingCalendar->uuid = $uuid;
190                     return $existingCalendar;
191                 }
192                 $counter += 1;
193             }
194         } catch (Tinebase_Exception_NotFound $e) {
195             $newContainer = new Tinebase_Model_Container(array(
196                 'name'              => $displayname . $counter,
197                 'type'              => $type,
198                 'backend'           => 'Sql',
199                 'application_id'    => $application_id,
200                 'model'             => $this->modelName,
201                 'uuid'              => $uuid
202             ));
203             
204             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
205                     . ' Adding container: ' . $newContainer->name . ' for model ' . $this->modelName);
206             
207             return Tinebase_Container::getInstance()->addContainer($newContainer);
208         }
209     }
210     
211     public function importAllCalendars()
212     {
213         if (count($this->calendars) < 1 && ! $this->findAllCalendars()) {
214             return false;
215         }
216         
217         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__ 
218             . ' Importing all calendars for user ' . $this->userName);
219         
220         Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->sendNotifications(false);
221         Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->useNotes(false);
222         Sabre\VObject\Component\VCalendar::$propertyMap['ATTACH'] = '\\Calendar_Import_CalDav_SabreAttachProperty';
223         
224         $this->decorator->initCalendarImport();
225         
226         $application_id = Tinebase_Application::getInstance()->getApplicationByName($this->appName)->getId();
227         $type = Tinebase_Model_Container::TYPE_PERSONAL;
228         
229         $defaultCalendarsName = $this->_getDefaultCalendarsName();
230         
231         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__
232             . ' Calendar uris to import: ' . print_r(array_keys($this->calendars), true));
233         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__
234             . ' Calendars to import: ' . print_r($this->calendars, true));
235         
236         foreach ($this->calendars as $calUri => $cal) {
237             $container = $this->findContainerForCalendar($calUri, $cal['displayname'], $defaultCalendarsName,
238                     $type, $application_id);
239             
240             $this->decorator->setCalendarProperties($container, $this->calendars[$calUri]);
241             
242             $grants = $this->getCalendarGrants($calUri);
243             Tinebase_Container::getInstance()->setGrants($container->getId(), $grants, TRUE, FALSE);
244         }
245     }
246     
247     /**
248      * decide which calendar to use as default calendar
249      * if there is a remote default calendar, use that. If not, use the first we find
250      * 
251      * @return string
252      */
253     protected function _getDefaultCalendarsName() 
254     {
255         $defaultCalendarsName = '';
256         foreach ($this->calendarICSs as $calUri => $calICSs) {
257             if ($this->mapToDefaultContainer == $this->calendars[$calUri]['displayname']) {
258                 return $this->calendars[$calUri]['displayname'];
259             } elseif ($defaultCalendarsName === '') {
260                 $defaultCalendarsName = $this->calendars[$calUri]['displayname'];
261             }
262         }
263         return $defaultCalendarsName;
264     }
265     
266     /**
267      * 
268      * @param string $onlyCurrentUserOrganizer
269      * @return boolean
270      * 
271      * @todo check if $onlyCurrentUserOrganizer is needed
272      * @todo check deletes
273      */
274     public function updateAllCalendarData($onlyCurrentUserOrganizer = false)
275     {
276         if (count($this->calendarICSs) < 1 && ! $this->findAllCalendarICSs()) {
277             if (Tinebase_Core::isLogLevel(Zend_Log::INFO))
278                 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' no calendars found for: ' . $this->userName);
279             return false;
280         }
281         
282         $newICSs = array();
283         $calendarEventBackend = Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->getBackend();
284         
285         foreach ($this->calendarICSs as $calUri => $calICSs) {
286             $start = 0;
287             $max = count($calICSs);
288             $etags = array();
289             do {
290                 $requestEnd = '';
291                 for ($i = $start; $i < $max && $i < ($this->maxBulkRequest+$start); ++$i) {
292                     $requestEnd .= '  <a:href>' . $calICSs[$i] . "</a:href>\n";
293                 }
294                 $start = $i;
295                 $requestEnd .= '</b:calendar-multiget>';
296                 $result = $this->calDavRequest('REPORT', $calUri, self::getEventETagsRequest . $requestEnd, 1);
297                 
298                 foreach ($result as $key => $value) {
299                     if (isset($value['{DAV:}getetag'])) {
300                         $name = explode('/', $key);
301                         $name = end($name);
302                         $id = $this->_getEventIdFromName($name);
303                         $etags[$key] = array( 'id' => $id, 'etag' => $value['{DAV:}getetag']);
304                     }
305                 }
306             } while($start < $max);
307             
308             //check etags
309             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' '
310                 . ' Got ' . count($etags) . ' etags');
311             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
312                 . ' etags: ' . print_r($etags, true));
313             
314             // @todo find out deleted events
315             foreach ($etags as $ics => $data) {
316                 try {
317                     $etagCheck = $calendarEventBackend->checkETag($data['id'], $data['etag']);
318                     if ($etagCheck) {
319                         continue; // same
320                     } else {
321                         $eventExists = true; // different
322                     }
323                 } catch (Tinebase_Exception_NotFound $tenf) {
324                     $eventExists = false;
325                 }
326                 
327                 if (!isset($newICSs[$calUri])) {
328                     $newICSs[$calUri] = array();
329                     $this->existingRecordIds[$calUri] = array();
330                 }
331                 $newICSs[$calUri][] = $ics;
332                 if ($eventExists) {
333                     $this->existingRecordIds[$calUri][] = $data['id'];
334                 }
335             }
336         }
337         
338         if (($count = count($newICSs)) > 0) {
339             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' ' 
340                     . $count . ' calendar(s) changed for: ' . $this->userName);
341             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' ' 
342                     . ' events changed: ' . print_r($newICSs, true));
343             $this->calendarICSs = $newICSs;
344             $this->importAllCalendarData($onlyCurrentUserOrganizer, /* $update = */ true);
345         } else {
346             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE))
347                 Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' no changes found for: ' . $this->userName);
348         }
349     }
350     
351     protected function _getEventIdFromName($name)
352     {
353         $id = ($pos = strpos($name, '.')) === false ? $name : substr($name, 0, $pos);
354         if (strlen($id) > 40) {
355             $id = sha1($id);
356         }
357         return $id;
358     }
359     
360     public function importAllCalendarData($onlyCurrentUserOrganizer = false, $update = false)
361     {
362         if (count($this->calendarICSs) < 1 && ! $this->findAllCalendarICSs()) {
363             return false;
364         }
365         
366         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
367             . ' Importing all calendar data for user ' . $this->userName . ' with ics uris: ' . print_r(array_keys($this->calendarICSs), true));
368         
369         Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->sendNotifications(false);
370         Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->useNotes(false);
371         Sabre\VObject\Component\VCalendar::$propertyMap['ATTACH'] = '\\Calendar_Import_CalDav_SabreAttachProperty';
372         
373         $this->decorator->initCalendarImport();
374         
375         $application_id = Tinebase_Application::getInstance()->getApplicationByName($this->appName)->getId();
376         $type = Tinebase_Model_Container::TYPE_PERSONAL;
377         $defaultContainer = Tinebase_Container::getInstance()->getDefaultContainer($this->modelName);
378         
379         $defaultCalendarsName = $this->_getDefaultCalendarsName();
380         
381         foreach ($this->calendarICSs as $calUri => $calICSs) {
382             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
383                 . ' Processing calendar ' . print_r($this->calendars[$calUri], true));
384             
385             $container = $this->findContainerForCalendar($calUri, $this->calendars[$calUri]['displayname'], $defaultCalendarsName,
386                     $type, $application_id);
387             
388             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__
389                     . ' User container: ' . print_r($container->toArray(), true));
390             
391             $this->decorator->setCalendarProperties($container, $this->calendars[$calUri]);
392             
393             // we shouldnt do the grants here as the caldav user file may not contain all users, so setting the grants wont work properly!
394             // use importAllCalendars to have the grants set
395             //$grants = $this->getCalendarGrants($calUri);
396             //Tinebase_Container::getInstance()->setGrants($container->getId(), $grants, TRUE, FALSE);
397             
398             $start = 0;
399             $max = count($calICSs);
400             do {
401                 $etags = array();
402                 $requestEnd = '';
403                 for ($i = $start; $i < $max && $i < ($this->maxBulkRequest+$start); ++$i) {
404                     $requestEnd .= '  <a:href>' . $calICSs[$i] . "</a:href>\n";
405                 }
406                 $start = $i;
407                 $requestEnd .= '</b:calendar-multiget>';
408                 $result = $this->calDavRequest('REPORT', $calUri, self::getAllCalendarDataRequest . $requestEnd, 1);
409                 
410                 foreach ($result as $key => $value) {
411                     if (! isset($value['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
412                         continue;
413                     }
414                     
415                     $data = $value['{urn:ietf:params:xml:ns:caldav}calendar-data'];
416                     
417                     if (strpos($data, 'BEGIN:' . $this->skipComonent) !== false) {
418                         if (Tinebase_Core::isLogLevel(Zend_Log::INFO))
419                             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Skipping ' . $this->skipComonent);
420                         continue;
421                     }
422                     
423                     $name = explode('/', $key);
424                     $name = end($name);
425                     $id = $this->_getEventIdFromName($name);
426                     try {
427                         if ($update && in_array($id, $this->existingRecordIds[$calUri])) {
428                             $webdavFrontend = new $this->webdavFrontend($container, $id);
429                             // @todo move this to separate fn
430                             if ($onlyCurrentUserOrganizer && $this->modelName === 'Calendar_Model_Event') {
431                                 // assert current user is organizer
432                                 if ($webdavFrontend->getRecord()->organizer && $webdavFrontend->getRecord()->organizer == Tinebase_Core::getUser()->contact_id) {
433                                     $webdavFrontend->put($data);
434                                 } else {
435                                     continue;
436                                 }
437                             } else {
438                                 $webdavFrontend->put($data);
439                             }
440                             
441                         } else {
442                             $webdavFrontend = call_user_func_array(array($this->webdavFrontend, 'create'), array(
443                                 $container,
444                                 $name,
445                                 $data,
446                                 $onlyCurrentUserOrganizer
447                             ));
448                         }
449                         
450                         if ($webdavFrontend) {
451                             $etags[$webdavFrontend->getRecord()->getId()] = $value['{DAV:}getetag'];
452                         }
453                     } catch (Exception $e) {
454                         if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
455                             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not create event from data: ' . $data);
456                         Tinebase_Exception::log($e);
457                     }
458                 }
459                 
460                 $this->_setEtags($etags);
461             } while($start < $max);
462         }
463         return true;
464     }
465     
466     protected function _setEtags($etags)
467     {
468         $calendarEventBackend = Tinebase_Core::getApplicationInstance($this->appName, $this->modelName)->getBackend();
469         $calendarEventBackend->setETags($etags);
470     }
471     
472     /**
473      * get Tine 2.0 group for given principal (by display name)
474      * - result is cached for 1 week
475      * 
476      * @param string $principal
477      * @return null|Tinebase_Model_Group
478      */
479     protected function _getGroupForPrincipal($principal)
480     {
481         $cacheId = convertCacheId('_getGroupForPrincipal' . $principal);
482         if (Tinebase_Core::getCache()->test($cacheId)) {
483             $group = Tinebase_Core::getCache()->load($cacheId);
484             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
485                     . ' Loading principal group from cache: ' . $group->name);
486             return $group;
487         }
488         
489         $group = null;
490         
491         $result = $this->calDavRequest('PROPFIND', $principal, self::resolvePrincipalRequest);
492         if (count($result['{DAV:}group-member-set']->getPrincipals()) > 0 && isset($result['{DAV:}displayname'])) {
493             $groupDescription = $result['{DAV:}displayname'];
494             try {
495                 $group = Tinebase_Group::getInstance()->getGroupByPropertyFromSqlBackend('description',$groupDescription);
496                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__
497                         . ' Found matching group ' . $group->name . ' (' . $group->description .') for principal ' . $principal);
498                 Tinebase_Core::getCache()->save($group, $cacheId, array(), /* 1 week */ 24*3600*7);
499             } catch (Tinebase_Exception_Record_NotDefined $ternd) {
500                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__
501                         . ' Group not found: ' . $groupDescription . ' ' . print_r($result, true));
502             }
503         }
504         
505         return $group;
506     }
507     
508     /**
509      * get grants for cal uri
510      * 
511      * @param string $calUri
512      * @return Tinebase_Record_RecordSet
513      */
514     public function getCalendarGrants($calUri)
515     {
516         $grants = array();
517         $user = array();
518         $type = array();
519         $privilege = array();
520         foreach ($this->calendars[$calUri]['acl']->getPrivileges() as $ace)
521         {
522             if ('{DAV:}authenticated' == $ace['principal']) {
523                 $user[] = 0;
524                 $type[] = Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE;
525                 $privilege[] = $ace['privilege'];
526             } elseif (isset($this->principals[$ace['principal']])) {
527                 $user[] = $this->principals[$ace['principal']]->getId();
528                 $type[] = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER;
529                 $privilege[] = $ace['privilege'];
530             } elseif (isset($this->principalGroups[$ace['principal']])) {
531                 foreach($this->principalGroups[$ace['principal']] as $principal) {
532                     if ('{DAV:}authenticated' == $principal) {
533                         $user[] = 0;
534                         $type[] = Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE;
535                         $privilege[] = $ace['privilege'];
536                     } elseif (isset($this->principals[$principal])) {
537                         $user[] = $this->principals[$principal]->getId();
538                         $type[] = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER;
539                         $privilege[] = $ace['privilege'];
540                     } else {
541                         $group = $this->_getGroupForPrincipal($principal);
542                         if ($group) {
543                             $user[] = $group->getId();
544                             $type[] = Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP;
545                             $privilege[] = $ace['privilege'];
546                         } else {
547                             if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
548                                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
549                                     . ' There is an unresolved principal: ' . $principal . ' in group: ' . $ace['principal']);
550                         }
551                     }
552                 }
553             } else {
554                 if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
555                     Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Couldn\'t resolve principal: '.$ace['principal']);
556             }
557         }
558         for ($i=0; $i<count($user); ++$i) {
559             switch ($privilege[$i]) {
560                 case '{DAV:}all':
561                     $grants[$user[$i]] = $this->_getAllGrants();
562                     break;
563                 case '{urn:ietf:params:xml:ns:caldav}read-free-busy':
564                     $grants[$user[$i]] = $this->_getFreeBusyGrants();
565                     break;
566                 case '{DAV:}read':
567                     $grants[$user[$i]] = $this->_getReadGrants();
568                     break;
569                 case '{DAV:}write':
570                     $grants[$user[$i]] = $this->_getWriteGrants();
571                     break;
572                 case '{DAV:}read-current-user-privilege-set':
573                     continue;
574                 default:
575                     if (Tinebase_Core::isLogLevel(Zend_Log::WARN))
576                         Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' unknown privilege: ' . $privilege[$i]);
577                     continue;
578             }
579             $grants[$user[$i]]['account_id'] = $user[$i];
580             $grants[$user[$i]]['account_type'] = $type[$i];
581         }
582         
583         if (Tinebase_Core::isLogLevel(Zend_Log::INFO))
584             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' found ' . count($grants) . ' grants for calendar: ' . $calUri);
585         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
586             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' grants: ' . print_r($grants, true));
587         
588         return new Tinebase_Record_RecordSet('Tinebase_Model_Grants', $grants, TRUE);
589     }
590     
591     protected function _getAllGrants()
592     {
593         return array(
594             Tinebase_Model_Grants::GRANT_READ => true,
595             Tinebase_Model_Grants::GRANT_ADD=> true,
596             Tinebase_Model_Grants::GRANT_EDIT=> true,
597             Tinebase_Model_Grants::GRANT_DELETE=> true,
598             Tinebase_Model_Grants::GRANT_EXPORT=> true,
599             Tinebase_Model_Grants::GRANT_SYNC=> true,
600             Tinebase_Model_Grants::GRANT_ADMIN=> true,
601             Tinebase_Model_Grants::GRANT_FREEBUSY=> true,
602             Tinebase_Model_Grants::GRANT_PRIVATE=> true,
603         );
604     }
605
606     protected function _getFreeBusyGrants()
607     {
608         return array(
609             Tinebase_Model_Grants::GRANT_FREEBUSY=> true,
610         );
611     }
612
613     protected function _getReadGrants()
614     {
615         return array(
616             Tinebase_Model_Grants::GRANT_READ=> true,
617             Tinebase_Model_Grants::GRANT_EXPORT=> true,
618             Tinebase_Model_Grants::GRANT_SYNC=> true,
619             Tinebase_Model_Grants::GRANT_FREEBUSY=> true,
620         );
621     }
622
623     protected function _getWriteGrants()
624     {
625         return array(
626             Tinebase_Model_Grants::GRANT_READ=> true,
627             Tinebase_Model_Grants::GRANT_ADD=> true,
628             Tinebase_Model_Grants::GRANT_EDIT=> true,
629             Tinebase_Model_Grants::GRANT_DELETE=> true,
630         );
631     }
632     
633     public function updateAllCalendarDataForUsers(array $users)
634     {
635         $result = true;
636         // first only update/import events where the current user is also the organizer
637         foreach ($users as $username => $pwd) {
638             $this->clearCurrentUserCalendarData();
639             $this->userName = $username;
640             $this->password = $pwd;
641             if (!$this->updateAllCalendarData(true)) {
642                 $result = false;
643             }
644         }
645         // then update all events again
646         foreach ($users as $username => $pwd) {
647             $this->clearCurrentUserCalendarData();
648             $this->userName = $username;
649             $this->password = $pwd;
650             if (!$this->updateAllCalendarData(false)) {
651                 $result = false;
652             }
653         }
654         return $result;
655     }
656     
657     public function importAllCalendarDataForUsers(array $users)
658     {
659         $result = true;
660         // first only import events where the current user is also the organizer
661         foreach ($users as $username => $pwd) {
662             $this->clearCurrentUserCalendarData();
663             $this->userName = $username;
664             $this->password = $pwd;
665             if (!$this->importAllCalendarData(true)) {
666                 $result = false;
667             }
668         }
669         // then import all events again
670         foreach ($users as $username => $pwd) {
671             $this->clearCurrentUserCalendarData();
672             $this->userName = $username;
673             $this->password = $pwd;
674             if (!$this->importAllCalendarData(false)) {
675                 $result = false;
676             }
677         }
678         return $result;
679     }
680     
681     public function importAllCalendarsForUsers(array $users)
682     {
683         if (!$this->findCurrentUserPrincipalForUsers($users)) {
684             return false;
685         }
686         
687         $result = true;
688         foreach ($users as $username => $pwd) {
689             $this->clearCurrentUserCalendarData();
690             $this->userName = $username;
691             $this->password = $pwd;
692             if (!$this->importAllCalendars()) {
693                 $result = false;
694             }
695         }
696         return $result;
697     }
698     
699     public function clearCurrentUserCalendarData()
700     {
701         $this->clearCurrentUserData();
702         $this->calendars = array();
703         $this->calendarICSs = array();
704     }
705 }