EGroupware /
egroupware
| 1 | <?php |
||||
| 2 | /** |
||||
| 3 | * EGroupware: CalDAV/CardDAV/GroupDAV access: Principals handlers |
||||
| 4 | * |
||||
| 5 | * @link http://www.egroupware.org |
||||
| 6 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
| 7 | * @package api |
||||
| 8 | * @subpackage caldav |
||||
| 9 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
| 10 | * @copyright (c) 2008-18 by Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
| 11 | */ |
||||
| 12 | |||||
| 13 | namespace EGroupware\Api\CalDAV; |
||||
| 14 | |||||
| 15 | use EGroupware\Api; |
||||
| 16 | |||||
| 17 | // explicit import classes without namespace |
||||
| 18 | use resources_bo, resources_acl_bo; |
||||
| 19 | use calendar_bo; |
||||
| 20 | |||||
| 21 | /** |
||||
| 22 | * EGroupware: GroupDAV access: groupdav/caldav/carddav principals handlers |
||||
| 23 | * |
||||
| 24 | * First-level properties used in this class should have the property name as their key, |
||||
| 25 | * to allow to check if required properties are set! |
||||
| 26 | * Principals::add_principal() converts simple associative props (name => value pairs) |
||||
| 27 | * to name => Api\CalDAV(name, value) pairs. |
||||
| 28 | * |
||||
| 29 | * Permanent error_log() calls should use $this->caldav->log($str) instead, to be send to PHP error_log() |
||||
| 30 | * and our request-log (prefixed with "### " after request and response, like exceptions). |
||||
| 31 | */ |
||||
| 32 | class Principals extends Handler |
||||
| 33 | { |
||||
| 34 | /** |
||||
| 35 | * Instance of resources_bo |
||||
| 36 | * |
||||
| 37 | * @var resources_bo |
||||
| 38 | */ |
||||
| 39 | private static $resources; |
||||
| 40 | |||||
| 41 | /** |
||||
| 42 | * Constructor |
||||
| 43 | * |
||||
| 44 | * @param string $app 'calendar', 'addressbook' or 'infolog' |
||||
| 45 | * @param Api\CalDAV $caldav calling class |
||||
| 46 | */ |
||||
| 47 | function __construct($app, Api\CalDAV $caldav) |
||||
| 48 | { |
||||
| 49 | parent::__construct($app, $caldav); |
||||
| 50 | |||||
| 51 | if (!isset(self::$resources)) self::$resources = new resources_bo(); |
||||
| 52 | } |
||||
| 53 | |||||
| 54 | /** |
||||
| 55 | * Supported reports and methods implementing them or what to return eg. "501 Not Implemented" |
||||
| 56 | * |
||||
| 57 | * @var array |
||||
| 58 | */ |
||||
| 59 | public $supported_reports = array( |
||||
| 60 | 'acl-principal-prop-set' => array( |
||||
| 61 | 'method' => 'acl_principal_prop_set_report', |
||||
| 62 | ), |
||||
| 63 | /*'principal-match' => array( |
||||
| 64 | // an other report calendarserver announces |
||||
| 65 | ),*/ |
||||
| 66 | 'principal-property-search' => array( |
||||
| 67 | 'method' => 'principal_property_search_report', |
||||
| 68 | ), |
||||
| 69 | 'principal-search-property-set' => array( |
||||
| 70 | 'method' => 'principal_search_property_set_report', |
||||
| 71 | ), |
||||
| 72 | 'expand-property' => array( |
||||
| 73 | 'method' => 'expand_property_report', |
||||
| 74 | ), |
||||
| 75 | /* seems only be used 'til OS X 10.6, no longer in 10.7 |
||||
| 76 | 'addressbook-findshared' => array( |
||||
| 77 | 'ns' => Api\CalDAV::ADDRESSBOOKSERVER, |
||||
| 78 | 'method' => 'addressbook_findshared_report', |
||||
| 79 | ),*/ |
||||
| 80 | ); |
||||
| 81 | |||||
| 82 | /** |
||||
| 83 | * Generate supported-report-set property |
||||
| 84 | * |
||||
| 85 | * Currently we return all reports independed of path |
||||
| 86 | * |
||||
| 87 | * @param string $path eg. '/principals/' |
||||
| 88 | * @param array $reports =null |
||||
| 89 | * @return array Api\CalDAV::mkprop('supported-report-set', ...) |
||||
| 90 | */ |
||||
| 91 | protected function supported_report_set($path, array $reports=null) |
||||
| 92 | { |
||||
| 93 | unset($path); // not used, but required by function signature |
||||
| 94 | |||||
| 95 | if (is_null($reports)) $reports = $this->supported_reports; |
||||
| 96 | |||||
| 97 | $supported = array(); |
||||
| 98 | foreach($reports as $name => $data) |
||||
| 99 | { |
||||
| 100 | $supported[$name] = Api\CalDAV::mkprop('supported-report',array( |
||||
| 101 | Api\CalDAV::mkprop('report',array( |
||||
| 102 | !$data['ns'] ? Api\CalDAV::mkprop($name, '') : |
||||
| 103 | Api\CalDAV::mkprop($data['ns'], $name, ''))))); |
||||
| 104 | } |
||||
| 105 | return $supported; |
||||
| 106 | } |
||||
| 107 | |||||
| 108 | /** |
||||
| 109 | * Handle propfind request for an application folder |
||||
| 110 | * |
||||
| 111 | * @param string $path |
||||
| 112 | * @param array &$options |
||||
| 113 | * @param array &$files |
||||
| 114 | * @param int $user account_id |
||||
| 115 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 116 | */ |
||||
| 117 | function propfind($path,&$options,&$files,$user) |
||||
| 118 | { |
||||
| 119 | if (($report = isset($_GET['report']) ? $_GET['report'] : $options['root']['name']) && $report != 'propfind') |
||||
| 120 | { |
||||
| 121 | $report_data = $this->supported_reports[$report]; |
||||
| 122 | if (isset($report_data) && ($method = $report_data['method']) && method_exists($this, $method)) |
||||
| 123 | { |
||||
| 124 | return $this->$method($path, $options, $files, $user); |
||||
| 125 | } |
||||
| 126 | $this->caldav->log(__METHOD__."('$path', ".array2string($options).",, $user) not implemented report, returning 501 Not Implemented"); |
||||
| 127 | return '501 Not Implemented'; |
||||
| 128 | } |
||||
| 129 | list(,,$type,$name,$rest) = explode('/',$path,5); |
||||
| 130 | // /principals/users/$name/ |
||||
| 131 | // /users/$name/calendar-proxy-read/ |
||||
| 132 | // /users/$name/calendar-proxy-write/ |
||||
| 133 | // /groups/$name/ |
||||
| 134 | // /resources/$resource/ |
||||
| 135 | // /locations/$resource/ |
||||
| 136 | // /__uids__/$uid/.../ |
||||
| 137 | |||||
| 138 | switch($type) |
||||
| 139 | { |
||||
| 140 | case 'users': |
||||
| 141 | $files['files'] = $this->propfind_users($name,$rest,$options); |
||||
| 142 | break; |
||||
| 143 | case 'groups': |
||||
| 144 | $files['files'] = $this->propfind_groups($name,$rest,$options); |
||||
| 145 | break; |
||||
| 146 | case 'resources': |
||||
| 147 | $files['files'] = $this->propfind_resources($name,$rest,$options,false); |
||||
| 148 | break; |
||||
| 149 | case 'locations': |
||||
| 150 | $files['files'] = $this->propfind_resources($name,$rest,$options,true); |
||||
| 151 | break; |
||||
| 152 | /*case '__uids__': |
||||
| 153 | $files['files'] = $this->propfind_uids($name,$rest,$options); |
||||
| 154 | break;*/ |
||||
| 155 | case '': |
||||
| 156 | $files['files'] = $this->propfind_principals($options); |
||||
| 157 | break; |
||||
| 158 | default: |
||||
| 159 | return '404 Not Found'; |
||||
| 160 | } |
||||
| 161 | if (!is_array($files['files'])) |
||||
| 162 | { |
||||
| 163 | return $files['files']; |
||||
| 164 | } |
||||
| 165 | return true; |
||||
| 166 | } |
||||
| 167 | |||||
| 168 | /** |
||||
| 169 | * Handle addressbook-findshared Addressbookserver report |
||||
| 170 | * |
||||
| 171 | * Required for Apple Addressbook on Mac (addressbook-findshared REPORT) |
||||
| 172 | * |
||||
| 173 | * @param string $path |
||||
| 174 | * @param array $options |
||||
| 175 | * @param array &$files |
||||
| 176 | * @param int $user account_id |
||||
| 177 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 178 | */ |
||||
| 179 | /* Seens only to work 'til OS X 10.6, no longer in 10.7 AND response seems NOT correct for 10.6 |
||||
| 180 | function addressbook_findshared_report($path,$options,&$files,$user) |
||||
| 181 | { |
||||
| 182 | error_log(__METHOD__."('$path', ".array2string($options).",, $user)"); |
||||
| 183 | $files['files'] = array(); |
||||
| 184 | $files['files'][] = $this->add_collection($path); // will be removed for reports |
||||
| 185 | foreach($this->get_shared_addressbooks() as $path) |
||||
| 186 | { |
||||
| 187 | $files['files'][] = $f = $this->add_collection($path.'addressbook/', array( |
||||
| 188 | 'resourcetype' => array(Api\CalDAV::mkprop(Api\CalDAV::CARDDAV,'addressbook','')), |
||||
| 189 | )); |
||||
| 190 | error_log(__METHOD__."() ".array2string($f)); |
||||
| 191 | } |
||||
| 192 | return true; |
||||
| 193 | }*/ |
||||
| 194 | |||||
| 195 | /** |
||||
| 196 | * Handle expand-property reports (all from http://calendarserver.org/ns/ namespace) seen from newer iCal on OS X |
||||
| 197 | * - expanded-group-member-set |
||||
| 198 | * - expanded-group-membership |
||||
| 199 | * - calendar-proxy-read-for |
||||
| 200 | * - calendar-proxy-write-for |
||||
| 201 | * |
||||
| 202 | * Example requests: |
||||
| 203 | * |
||||
| 204 | * REPORT /egw/groupdav.php/principals/groups/groupname/ HTTP/1.1 |
||||
| 205 | * User-Agent: CalendarStore/5.0.3 (1204.1); iCal/5.0.3 (1605.3); Mac OS X/10.7.4 (11E53) |
||||
| 206 | * |
||||
| 207 | * <?xml version="1.0" encoding="UTF-8"?> |
||||
| 208 | * <A:expand-property xmlns:A="DAV:"> |
||||
| 209 | * <A:property name="expanded-group-member-set" namespace="http://calendarserver.org/ns/"> |
||||
| 210 | * <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 211 | * <A:property name="last-name" namespace="http://calendarserver.org/ns/"/> |
||||
| 212 | * <A:property name="calendar-user-type" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 213 | * <A:property name="principal-URL" namespace="DAV:"/> |
||||
| 214 | * <A:property name="displayname" namespace="DAV:"/> |
||||
| 215 | * <A:property name="record-type" namespace="http://calendarserver.org/ns/"/> |
||||
| 216 | * <A:property name="first-name" namespace="http://calendarserver.org/ns/"/> |
||||
| 217 | * </A:property> |
||||
| 218 | * </A:expand-property> |
||||
| 219 | * |
||||
| 220 | * REPORT /egw/groupdav.php/principals/users/username/ HTTP/1.1 |
||||
| 221 | * User-Agent: CalendarStore/5.0.3 (1204.1); iCal/5.0.3 (1605.3); Mac OS X/10.7.4 (11E53) |
||||
| 222 | * |
||||
| 223 | * <?xml version="1.0" encoding="UTF-8"?> |
||||
| 224 | * <A:expand-property xmlns:A="DAV:"> |
||||
| 225 | * <A:property name="calendar-proxy-read-for" namespace="http://calendarserver.org/ns/"> |
||||
| 226 | * <A:property name="displayname" namespace="DAV:"/> |
||||
| 227 | * <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 228 | * <A:property name="email-address-set" namespace="http://calendarserver.org/ns/"/> |
||||
| 229 | * </A:property> |
||||
| 230 | * <A:property name="calendar-proxy-write-for" namespace="http://calendarserver.org/ns/"> |
||||
| 231 | * <A:property name="displayname" namespace="DAV:"/> |
||||
| 232 | * <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 233 | * <A:property name="email-address-set" namespace="http://calendarserver.org/ns/"/> |
||||
| 234 | * </A:property> |
||||
| 235 | * </A:expand-property> |
||||
| 236 | * |
||||
| 237 | * REPORT /egroupware/groupdav.php/principals/users/username/ HTTP/1.1 |
||||
| 238 | * User-Agent: DAVKit/4.0.3 (732.2); CalendarStore/4.0.4 (997.7); iCal/4.0.4 (1395.7); Mac OS X/10.6.8 (10K549) |
||||
| 239 | * |
||||
| 240 | * <?xml version="1.0" encoding="utf-8" ?> |
||||
| 241 | * <x0:expand-property xmlns:x0="DAV:"> |
||||
| 242 | * <x0:property name="calendar-proxy-write-for" namespace="http://calendarserver.org/ns/"> |
||||
| 243 | * <x0:property name="displayname"/> |
||||
| 244 | * <x0:property name="principal-URL"/> |
||||
| 245 | * <x0:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 246 | * </x0:property> |
||||
| 247 | * <x0:property name="calendar-proxy-read-for" namespace="http://calendarserver.org/ns/"> |
||||
| 248 | * <x0:property name="displayname"/> |
||||
| 249 | * <x0:property name="principal-URL"/> |
||||
| 250 | * <x0:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/> |
||||
| 251 | * </x0:property> |
||||
| 252 | * </x0:expand-property> |
||||
| 253 | * |
||||
| 254 | * @param string $path |
||||
| 255 | * @param array &$options |
||||
| 256 | * @param array &$files |
||||
| 257 | * @param int $user account_id |
||||
| 258 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 259 | */ |
||||
| 260 | function expand_property_report($path,&$options,&$files,$user) |
||||
| 261 | { |
||||
| 262 | //error_log(__METHOD__."('$path', ".array2string($options).",, $user)"); |
||||
| 263 | $requested_props = $options['other']; |
||||
| 264 | while(($requested_prop = array_shift($requested_props))) |
||||
| 265 | { |
||||
| 266 | if ($requested_prop['name'] != 'property' || $requested_prop['depth'] != 1) continue; |
||||
| 267 | |||||
| 268 | $prop_ns = $requested_prop['attrs']['namespace']; |
||||
| 269 | $prop_name = $requested_prop['attrs']['name']; |
||||
| 270 | $prop_path = $path; |
||||
| 271 | // calendarserver has some special property-names for expansion |
||||
| 272 | switch($prop_name) |
||||
| 273 | { |
||||
| 274 | case 'calendar-proxy-read-for': |
||||
| 275 | case 'calendar-proxy-write-for': |
||||
| 276 | $prop_path = $path . substr($prop_name, 0, -4).'/'; |
||||
| 277 | $prop_name = 'group-member-set'; |
||||
| 278 | $prop_ns = Api\CalDAV::DAV; |
||||
| 279 | break; |
||||
| 280 | |||||
| 281 | case 'expanded-group-member-set': |
||||
| 282 | case 'expanded-group-membership': |
||||
| 283 | // remove 'expanded-' prefix |
||||
| 284 | $prop_name = substr($prop_name, 9); |
||||
| 285 | $prop_ns = Api\CalDAV::DAV; |
||||
| 286 | break; |
||||
| 287 | } |
||||
| 288 | // run regular propfind for requested property |
||||
| 289 | $options['depth'] = '0'; |
||||
| 290 | $options['root']['name'] = 'propfind'; |
||||
| 291 | $options['props'] = array(array( |
||||
| 292 | 'name' => $prop_name, |
||||
| 293 | 'xmlns' => $prop_ns, |
||||
| 294 | )); |
||||
| 295 | $prop_files = array(); |
||||
| 296 | $this->caldav->options = $options; // also modify global variable |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 297 | if (empty($prop_name) || $this->propfind($prop_path, $options, $prop_files, $user) !== true) |
||||
| 298 | { |
||||
| 299 | $this->caldav->log('### NO expand-property report for '.$requested_prop['attrs']['name']); |
||||
| 300 | continue; |
||||
| 301 | } |
||||
| 302 | // find prop to expand |
||||
| 303 | foreach($prop_files['files'][0]['props'] as $expand_prop) |
||||
| 304 | { |
||||
| 305 | if ($expand_prop['name'] === $prop_name) break; |
||||
| 306 | } |
||||
| 307 | if ($expand_prop['name'] !== $prop_name || !is_array($expand_prop['val']) || |
||||
| 308 | $expand_prop['val'] && $expand_prop['val'][0]['name'] !== 'href') |
||||
| 309 | { |
||||
| 310 | $this->caldav->log('### NO expand-property report for '.$requested_prop['attrs']['name'].' ('.$prop_name.')'); |
||||
| 311 | continue; |
||||
| 312 | } |
||||
| 313 | |||||
| 314 | // requested properties of each href are in depth=2 properties |
||||
| 315 | // set them as regular propfind properties to $options['props'] |
||||
| 316 | $options2 = array('props' => 'all'); |
||||
| 317 | while(($prop = array_shift($requested_props)) && $prop['depth'] >= 2) |
||||
| 318 | { |
||||
| 319 | if ($prop['name'] == 'property' && $prop['depth'] == 2) |
||||
| 320 | { |
||||
| 321 | if (!is_array($options2['props'])) // is "all" initially |
||||
| 322 | { |
||||
| 323 | $options2['props'] = array(); |
||||
| 324 | } |
||||
| 325 | $options2['props'][] = array( |
||||
| 326 | 'name' => $prop['attrs']['name'], |
||||
| 327 | 'xmlns' => isset($prop['attrs']['namespace']) ? $prop['attrs']['namespace'] : $prop['xmlns'], |
||||
| 328 | ); |
||||
| 329 | } |
||||
| 330 | } |
||||
| 331 | // put back evtl. read top-level property |
||||
| 332 | if ($prop && $prop['depth'] == 1) array_unshift($requested_props, $prop); |
||||
| 333 | $this->caldav->options = $options2; // also modify global variable |
||||
| 334 | |||||
| 335 | // run regular profind to get requested 2.-level properties for each href |
||||
| 336 | foreach($expand_prop['val'] as $key => &$prop_val) |
||||
| 337 | { |
||||
| 338 | list(,$expand_path) = explode($this->caldav->base_uri, $prop_val['val']); |
||||
| 339 | //error_log(__METHOD__."('$path', ..., $user) calling propfind('$expand_path', ".array2string($options2).', '.array2string($prop_val).", $user)"); |
||||
| 340 | if ($this->propfind($expand_path, $options2, $prop_val, $user) !== true || !isset($prop_val['files'][0])) |
||||
| 341 | { |
||||
| 342 | // do NOT return that path, eg. perms give rights but account_selection="groupmembers" forbids access |
||||
| 343 | unset($expand_prop['val'][$key]); |
||||
| 344 | continue; |
||||
| 345 | } |
||||
| 346 | $prop_val = $prop_val['files'][0]; |
||||
| 347 | } |
||||
| 348 | // setting top-level name and namespace |
||||
| 349 | $expand_prop['ns'] = $requested_prop['attrs']['namespace']; |
||||
| 350 | $expand_prop['name'] = $requested_prop['attrs']['name']; |
||||
| 351 | // setting 2.-level props, so Api\CalDAV can filter unwanted ones out or mark missing ones |
||||
| 352 | $expand_prop['props'] = $options2['props']; |
||||
| 353 | // add top-level path and property |
||||
| 354 | $files['files'][0]['path'] = $path; |
||||
| 355 | $files['files'][0]['props'][] = $expand_prop; |
||||
| 356 | } |
||||
| 357 | // we can use 'all' here, as we return only requested properties |
||||
| 358 | $options['props'] = 'all'; |
||||
| 359 | |||||
| 360 | return true; |
||||
| 361 | } |
||||
| 362 | |||||
| 363 | /** |
||||
| 364 | * Handle principal-property-search report |
||||
| 365 | * |
||||
| 366 | * Current implementation runs a full infinity propfind and filters out not matching resources. |
||||
| 367 | * |
||||
| 368 | * Eg. from Lightning on the principal collection /principals/: |
||||
| 369 | * <D:principal-property-search xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav"> |
||||
| 370 | * <D:property-search> |
||||
| 371 | * <D:prop> |
||||
| 372 | * <C:calendar-home-set/> |
||||
| 373 | * </D:prop> |
||||
| 374 | * <D:match>/egroupware/groupdav.php</D:match> |
||||
| 375 | * </D:property-search> |
||||
| 376 | * <D:prop> |
||||
| 377 | * <C:calendar-home-set/> |
||||
| 378 | * <C:calendar-user-address-set/> |
||||
| 379 | * <C:schedule-inbox-URL/> |
||||
| 380 | * <C:schedule-outbox-URL/> |
||||
| 381 | * </D:prop> |
||||
| 382 | * </D:principal-property-search> |
||||
| 383 | * |
||||
| 384 | * Hack for Lightning: it requests calendar-home-set matching our root (/egroupware/groupdav.php), |
||||
| 385 | * but interprets returning all principals (all have a matching calendar-home-set) as NOT supporting CalDAV scheduling |
||||
| 386 | * --> search only current user's principal, when Lightning searches for calendar-home-set |
||||
| 387 | * |
||||
| 388 | * Example from iOS iCal autocompleting invitees using calendarserver-principal-property-search WebDAV extension |
||||
| 389 | * <x0:principal-property-search xmlns:x2="urn:ietf:params:xml:ns:caldav" xmlns:x1="http://calendarserver.org/ns/" xmlns:x0="DAV:" test="anyof"> |
||||
| 390 | * <x0:property-search> |
||||
| 391 | * <x0:prop> |
||||
| 392 | * <x0:displayname/> |
||||
| 393 | * </x0:prop> |
||||
| 394 | * <x0:match match-type="contains">beck</x0:match> |
||||
| 395 | * </x0:property-search> |
||||
| 396 | * <x0:property-search> |
||||
| 397 | * <x0:prop> |
||||
| 398 | * <x1:email-address-set/> |
||||
| 399 | * </x0:prop> |
||||
| 400 | * <x0:match match-type="starts-with">beck</x0:match> |
||||
| 401 | * </x0:property-search> |
||||
| 402 | * <x0:property-search> |
||||
| 403 | * <x0:prop> |
||||
| 404 | * <x1:first-name/> |
||||
| 405 | * </x0:prop> |
||||
| 406 | * <x0:match match-type="starts-with">beck</x0:match> |
||||
| 407 | * </x0:property-search> |
||||
| 408 | * <x0:property-search> |
||||
| 409 | * <x0:prop> |
||||
| 410 | * <x1:last-name/> |
||||
| 411 | * </x0:prop> |
||||
| 412 | * <x0:match match-type="starts-with">beck</x0:match> |
||||
| 413 | * </x0:property-search> |
||||
| 414 | * <x0:prop> |
||||
| 415 | * <x1:first-name/> |
||||
| 416 | * <x1:last-name/> |
||||
| 417 | * <x0:displayname/> |
||||
| 418 | * <x1:email-address-set/> |
||||
| 419 | * <x2:calendar-user-address-set/> |
||||
| 420 | * <x1:record-type/> |
||||
| 421 | * <x0:principal-URL/> |
||||
| 422 | * </x0:prop> |
||||
| 423 | * </x0:principal-property-search> |
||||
| 424 | * |
||||
| 425 | * @param string $path |
||||
| 426 | * @param array $options |
||||
| 427 | * @param array &$files |
||||
| 428 | * @param int $user account_id |
||||
| 429 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 430 | */ |
||||
| 431 | function principal_property_search_report($path,&$options,&$files,$user) |
||||
| 432 | { |
||||
| 433 | //error_log(__METHOD__."('$path', ".array2string($options).",, $user)"); |
||||
| 434 | |||||
| 435 | // cant find the test attribute to root principal-property-search element in WebDAV rfc, but iPhones use it ... |
||||
| 436 | $anyof = !empty($options['root']['attrs']['test']) && $options['root']['attrs']['test'] == 'anyof'; // "allof" (default) or "anyof" |
||||
| 437 | |||||
| 438 | // parse property-search prop(s) contained in $options['other'] |
||||
| 439 | foreach($options['other'] as $n => $prop) |
||||
| 440 | { |
||||
| 441 | switch($prop['name']) |
||||
| 442 | { |
||||
| 443 | case 'apply-to-principal-collection-set': // optinal prop to apply search on principal-collection-set == '/principals/' |
||||
| 444 | $path = '/principals/'; |
||||
| 445 | break; |
||||
| 446 | case 'property-search': |
||||
| 447 | $property_search = $n; // should be 1 |
||||
| 448 | break; |
||||
| 449 | case 'prop': |
||||
| 450 | if (isset($property_search)) |
||||
| 451 | { |
||||
| 452 | $search_props[$property_search] = array(); |
||||
| 453 | } |
||||
| 454 | break; |
||||
| 455 | case 'match': |
||||
| 456 | if (isset($property_search) && is_array($search_props[$property_search])) |
||||
| 457 | { |
||||
| 458 | $search_props[$property_search]['match'] = $prop['data']; |
||||
| 459 | // optional match-type: "contains" (default), "starts-with", "ends-with", "equals" |
||||
| 460 | $search_props[$property_search]['match-type'] = $prop['attrs']['match-type']; |
||||
| 461 | } |
||||
| 462 | break; |
||||
| 463 | default: |
||||
| 464 | if (isset($property_search) && $search_props[$property_search] === array()) |
||||
| 465 | { |
||||
| 466 | $search_props[$property_search] = $prop; |
||||
| 467 | } |
||||
| 468 | break; |
||||
| 469 | } |
||||
| 470 | } |
||||
| 471 | if (!isset($property_search) || !$search_props || !isset($search_props[$property_search]['match'])) |
||||
| 472 | { |
||||
| 473 | $this->caldav->log(__METHOD__."('$path',...) Could not parse options[other]=".array2string($options['other'])); |
||||
| 474 | return '400 Bad Request'; |
||||
| 475 | } |
||||
| 476 | // make sure search property is included in toplevel props (can be missing and defaults to property-search/prop's) |
||||
| 477 | foreach($search_props as $prop) |
||||
| 478 | { |
||||
| 479 | if (!$this->caldav->prop_requested($prop['name'], $prop['xmlns'])) |
||||
| 480 | { |
||||
| 481 | $options['props'][] = array( |
||||
| 482 | 'name' => $prop['name'], |
||||
| 483 | 'xmlns' => $prop['xmlns'], |
||||
| 484 | ); |
||||
| 485 | } |
||||
| 486 | // Hack for Lightning prior 1.1.1 (TB 9): it requests calendar-home-set matching our root (/egroupware/groupdav.php), |
||||
| 487 | // but interprets returning all principals (all have a matching calendar-home-set) as NOT supporting CalDAV scheduling |
||||
| 488 | // --> search only current user's principal |
||||
| 489 | if ($prop['name'] == 'calendar-home-set' && stripos($_SERVER['HTTP_USER_AGENT'], 'Lightning') !== false && |
||||
| 490 | substr($search_props[0]['match'],-13) == '/groupdav.php') |
||||
| 491 | { |
||||
| 492 | $path = '/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'; |
||||
| 493 | $this->caldav->log('Enabling hack for Lightning prior 1.1.1 for searching calendar-home-set matching "/groupdav.php": limiting search to '.$path); |
||||
| 494 | } |
||||
| 495 | } |
||||
| 496 | // check type attribute to limit search on a certain tree |
||||
| 497 | if (isset($options['root']['attrs']['type'])) |
||||
| 498 | { |
||||
| 499 | switch($options['root']['attrs']['type']) |
||||
| 500 | { |
||||
| 501 | case 'INDIVIDUAL': |
||||
| 502 | $path = '/principals/users/'; |
||||
| 503 | break; |
||||
| 504 | case 'GROUP': |
||||
| 505 | $path = '/principals/groups/'; |
||||
| 506 | break; |
||||
| 507 | case 'ROOM': |
||||
| 508 | $path = '/principals/locations/'; |
||||
| 509 | break; |
||||
| 510 | case 'RESOURCE': |
||||
| 511 | $path = '/principals/resources/'; |
||||
| 512 | break; |
||||
| 513 | } |
||||
| 514 | } |
||||
| 515 | // run "regular" propfind |
||||
| 516 | $options['other'] = array(); |
||||
| 517 | $options['root']['name'] = 'propfind'; |
||||
| 518 | // search all principals, but not the proxys, rfc requires depth=0, but to search all principals |
||||
| 519 | $options['depth'] = 5 - count(explode('/', $path)); // /principals/ --> 3 |
||||
| 520 | |||||
| 521 | if (($ret = $this->propfind($path, $options, $files, $user)) !== true) |
||||
| 522 | { |
||||
| 523 | return $ret; |
||||
| 524 | } |
||||
| 525 | // now filter out not matching "files" |
||||
| 526 | foreach($files['files'] as $n => $resource) |
||||
| 527 | { |
||||
| 528 | if (count(explode('/', $resource['path'])) < 5) // hack to only return principals, not the collections itself |
||||
| 529 | { |
||||
| 530 | unset($files['files'][$n]); |
||||
| 531 | continue; |
||||
| 532 | } |
||||
| 533 | // match with $search_props |
||||
| 534 | $matches = 0; |
||||
| 535 | foreach($search_props as $search_prop) |
||||
| 536 | { |
||||
| 537 | // search resource for $search_prop |
||||
| 538 | foreach($resource['props'] as $prop) |
||||
| 539 | { |
||||
| 540 | if ($prop['name'] === $search_prop['name']) break; |
||||
| 541 | } |
||||
| 542 | if ($prop['name'] === $search_prop['name']) // search_prop NOT found |
||||
| 543 | { |
||||
| 544 | foreach((array)$prop['val'] as $value) |
||||
| 545 | { |
||||
| 546 | if (is_array($value)) $value = $value['val']; // eg. href prop |
||||
| 547 | if (self::match($value, $search_prop['match'], $search_prop['match-type']) !== false) // prop does match |
||||
| 548 | { |
||||
| 549 | ++$matches; |
||||
| 550 | //error_log("$matches: $resource[path]: $search_prop[name]=".array2string($prop['name'] !== $search_prop['name'] ? null : $prop['val'])." does match '$search_prop[match]'"); |
||||
| 551 | break; |
||||
| 552 | } |
||||
| 553 | } |
||||
| 554 | } |
||||
| 555 | if ($anyof && $matches || $matches == count($search_props)) |
||||
|
0 ignored issues
–
show
|
|||||
| 556 | { |
||||
| 557 | //error_log("$resource[path]: anyof=$anyof, $matches matches --> keep"); |
||||
| 558 | continue 2; // enough matches --> keep |
||||
| 559 | } |
||||
| 560 | } |
||||
| 561 | //error_log("$resource[path]: anyof=$anyof, $matches matches --> skip"); |
||||
| 562 | unset($files['files'][$n]); |
||||
| 563 | } |
||||
| 564 | return $ret; |
||||
| 565 | } |
||||
| 566 | |||||
| 567 | /** |
||||
| 568 | * Match using $match_type |
||||
| 569 | * |
||||
| 570 | * It's not defined in WebDAV ACL, but CardDAV:text-match seems similar |
||||
| 571 | * |
||||
| 572 | * @param string $value value to test |
||||
| 573 | * @param string $match criteria/sub-string |
||||
| 574 | * @param string $match_type ='contains' 'starts-with', 'ends-with' or 'equals' |
||||
| 575 | */ |
||||
| 576 | private static function match($value, $match, $match_type='contains') |
||||
| 577 | { |
||||
| 578 | switch($match_type) |
||||
| 579 | { |
||||
| 580 | case 'equals': |
||||
| 581 | return $value === $match; |
||||
| 582 | |||||
| 583 | case 'starts-with': |
||||
| 584 | return stripos($value, $match) === 0; |
||||
| 585 | |||||
| 586 | case 'ends-with': |
||||
| 587 | return stripos($value, $match) === strlen($value) - strlen($match); |
||||
| 588 | |||||
| 589 | case 'contains': |
||||
| 590 | default: |
||||
| 591 | return stripos($value, $match) !== false; |
||||
| 592 | } |
||||
| 593 | } |
||||
| 594 | |||||
| 595 | /** |
||||
| 596 | * Handle principal-search-property-set report |
||||
| 597 | * |
||||
| 598 | * REPORT /principals/ HTTP/1.1 |
||||
| 599 | * <?xml version="1.0" encoding="utf-8" ?> |
||||
| 600 | * <x0:principal-search-property-set xmlns:x0="DAV:"/> |
||||
| 601 | * |
||||
| 602 | * <?xml version='1.0' encoding='UTF-8'?> |
||||
| 603 | * <principal-search-property-set xmlns='DAV:'> |
||||
| 604 | * <principal-search-property> |
||||
| 605 | * <prop> |
||||
| 606 | * <displayname/> |
||||
| 607 | * </prop> |
||||
| 608 | * <description xml:lang='en'>Display Name</description> |
||||
| 609 | * </principal-search-property> |
||||
| 610 | * <principal-search-property> |
||||
| 611 | * <prop> |
||||
| 612 | * <email-address-set xmlns='http://calendarserver.org/ns/'/> |
||||
| 613 | * </prop> |
||||
| 614 | * <description xml:lang='en'>Email Addresses</description> |
||||
| 615 | * </principal-search-property> |
||||
| 616 | * <principal-search-property> |
||||
| 617 | * <prop> |
||||
| 618 | * <last-name xmlns='http://calendarserver.org/ns/'/> |
||||
| 619 | * </prop> |
||||
| 620 | * <description xml:lang='en'>Last Name</description> |
||||
| 621 | * </principal-search-property> |
||||
| 622 | * <principal-search-property> |
||||
| 623 | * <prop> |
||||
| 624 | * <calendar-user-type xmlns='urn:ietf:params:xml:ns:caldav'/> |
||||
| 625 | * </prop> |
||||
| 626 | * <description xml:lang='en'>Calendar User Type</description> |
||||
| 627 | * </principal-search-property> |
||||
| 628 | * <principal-search-property> |
||||
| 629 | * <prop> |
||||
| 630 | * <first-name xmlns='http://calendarserver.org/ns/'/> |
||||
| 631 | * </prop> |
||||
| 632 | * <description xml:lang='en'>First Name</description> |
||||
| 633 | * </principal-search-property> |
||||
| 634 | * <principal-search-property> |
||||
| 635 | * <prop> |
||||
| 636 | * <calendar-user-address-set xmlns='urn:ietf:params:xml:ns:caldav'/> |
||||
| 637 | * </prop> |
||||
| 638 | * <description xml:lang='en'>Calendar User Address Set</description> |
||||
| 639 | * </principal-search-property> |
||||
| 640 | * </principal-search-property-set> |
||||
| 641 | * |
||||
| 642 | * @param string $path |
||||
| 643 | * @param array $options |
||||
| 644 | * @param array &$files |
||||
| 645 | * @param int $user account_id |
||||
| 646 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 647 | */ |
||||
| 648 | function principal_search_property_set_report($path,&$options,&$files,$user) |
||||
| 649 | { |
||||
| 650 | unset($path, $options, $files, $user); // not used, but required by function signature |
||||
| 651 | |||||
| 652 | static $search_props = array( |
||||
| 653 | // from iOS iCal |
||||
| 654 | 'displayname' => 'Display Name', |
||||
| 655 | 'email-address-set' => array('description' => 'Email Addresses', 'ns' => Api\CalDAV::CALENDARSERVER), |
||||
| 656 | 'last-name' => array('description' => 'Last Name', 'ns' => Api\CalDAV::CALENDARSERVER), |
||||
| 657 | 'calendar-user-type' => array('description' => 'Calendar User Type', 'ns' => Api\CalDAV::CALDAV), |
||||
| 658 | 'first-name' => array('description' => 'First Name', 'ns' => Api\CalDAV::CALENDARSERVER), |
||||
| 659 | 'calendar-user-address-set' => array('description' => 'Calendar User Address Set', 'ns' => Api\CalDAV::CALDAV), |
||||
| 660 | // Lightning |
||||
| 661 | 'calendar-home-set' => array('description' => 'Calendar Home Set', 'ns' => Api\CalDAV::CALENDARSERVER), |
||||
| 662 | // others, we generally support all properties of the principal |
||||
| 663 | ); |
||||
| 664 | header('Content-type: text/xml; charset=UTF-8'); |
||||
| 665 | |||||
| 666 | $xml = new \XMLWriter; |
||||
| 667 | $xml->openMemory(); |
||||
| 668 | $xml->setIndent(true); |
||||
| 669 | $xml->startDocument('1.0', 'UTF-8'); |
||||
| 670 | $xml->startElementNs(null, 'principal-search-property-set', 'DAV:'); |
||||
| 671 | |||||
| 672 | foreach($search_props as $name => $data) |
||||
| 673 | { |
||||
| 674 | $xml->startElement('principal-search-property'); |
||||
| 675 | $xml->startElement('prop'); |
||||
| 676 | if (is_array($data) && !empty($data['ns'])) |
||||
| 677 | { |
||||
| 678 | $xml->writeElementNs(null, $name, $data['ns']); |
||||
| 679 | } |
||||
| 680 | else |
||||
| 681 | { |
||||
| 682 | $xml->writeElement($name); |
||||
| 683 | } |
||||
| 684 | $xml->endElement(); // prop |
||||
| 685 | |||||
| 686 | $xml->startElement('description'); |
||||
| 687 | $xml->writeAttribute('xml:lang', 'en'); |
||||
| 688 | $xml->text(is_array($data) ? $data['description'] : $data); |
||||
| 689 | $xml->endElement(); // description |
||||
| 690 | |||||
| 691 | $xml->endElement(); // principal-search-property |
||||
| 692 | } |
||||
| 693 | $xml->endElement(); // principal-search-property-set |
||||
| 694 | $xml->endDocument(); |
||||
| 695 | echo $xml->outputMemory(); |
||||
| 696 | |||||
| 697 | exit; |
||||
|
0 ignored issues
–
show
|
|||||
| 698 | } |
||||
| 699 | |||||
| 700 | /** |
||||
| 701 | * Handle principal-property-search report |
||||
| 702 | * |
||||
| 703 | * Current implementation runs a full (infinity) propfind, as we have principals only once in the principal collection. |
||||
| 704 | * |
||||
| 705 | * Example from WebDAV ACL rfc 3744: |
||||
| 706 | * REPORT /index.html HTTP/1.1 |
||||
| 707 | * Host: www.example.com |
||||
| 708 | * Content-Type: text/xml; charset="utf-8" |
||||
| 709 | * Content-Length: xxxx |
||||
| 710 | * Depth: 0 |
||||
| 711 | * |
||||
| 712 | * <?xml version="1.0" encoding="utf-8" ?> |
||||
| 713 | * <D:acl-principal-prop-set xmlns:D="DAV:"> |
||||
| 714 | * <D:prop> |
||||
| 715 | * <D:displayname/> |
||||
| 716 | * </D:prop> |
||||
| 717 | * </D:acl-principal-prop-set> |
||||
| 718 | * |
||||
| 719 | * Response is a multistatus as for a propfind. Seems the only diverence is, that prinipals are only returned once |
||||
| 720 | * (even if they exists multiple times), only principals get returned (eg. not the collections they are in) AND the depth 0. |
||||
| 721 | * |
||||
| 722 | * @param string $path |
||||
| 723 | * @param array $options |
||||
| 724 | * @param array &$files |
||||
| 725 | * @param int $user account_id |
||||
| 726 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 727 | */ |
||||
| 728 | function acl_principal_prop_set_report($path,&$options,&$files,$user) |
||||
| 729 | { |
||||
| 730 | //error_log(__METHOD__."('$path', ".array2string($options).",, $user)"); |
||||
| 731 | |||||
| 732 | // run "regular" propfind |
||||
| 733 | $options['root']['name'] = 'propfind'; |
||||
| 734 | // search all principals, but not the proxys, rfc requires depth=0, but to search all principals |
||||
| 735 | $options['depth'] = 5 - count(explode('/', $path)); // /principals/ --> 3 |
||||
| 736 | |||||
| 737 | // we need the resourcetype to only return principals |
||||
| 738 | $options['props']['resourcetype'] = array( |
||||
| 739 | 'name' => 'resourcetype', |
||||
| 740 | 'xmlns' => 'DAV:', |
||||
| 741 | ); |
||||
| 742 | if (($ret = $this->propfind($path, $options, $files, $user)) !== true) |
||||
| 743 | { |
||||
| 744 | return $ret; |
||||
| 745 | } |
||||
| 746 | // now filter out not matching "files" |
||||
| 747 | foreach($files['files'] as $n => $resource) |
||||
| 748 | { |
||||
| 749 | foreach($resource['props']['resourcetype']['val'] as $prop) |
||||
| 750 | { |
||||
| 751 | if ($prop['name'] == 'principal') continue 2; |
||||
| 752 | } |
||||
| 753 | unset($files['files'][$n]); // not a principal --> do not return |
||||
| 754 | } |
||||
| 755 | // we should not return it |
||||
| 756 | unset($options['props']['resourcetype']); |
||||
| 757 | |||||
| 758 | return $ret; |
||||
| 759 | } |
||||
| 760 | |||||
| 761 | /** |
||||
| 762 | * Do propfind in /pricipals/users |
||||
| 763 | * |
||||
| 764 | * @param string $name name of account or empty |
||||
| 765 | * @param string $rest rest of path behind account-name |
||||
| 766 | * @param array $options |
||||
| 767 | * @return array|string array with files or HTTP error code |
||||
| 768 | */ |
||||
| 769 | protected function propfind_users($name,$rest,array $options) |
||||
| 770 | { |
||||
| 771 | //error_log(__METHOD__."($name,$rest,".array2string($options).')'); |
||||
| 772 | if (empty($name)) |
||||
| 773 | { |
||||
| 774 | $files = array(); |
||||
| 775 | // add /pricipals/users/ entry |
||||
| 776 | $files[] = $this->add_collection('/principals/users/', array('displayname' => lang('Users'))); |
||||
| 777 | |||||
| 778 | if ($options['depth']) |
||||
| 779 | { |
||||
| 780 | if ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'none' && |
||||
| 781 | !isset($GLOBALS['egw_info']['user']['apps']['admin'])) |
||||
| 782 | { |
||||
| 783 | if (($account = $this->accounts->read($GLOBALS['egw_info']['user']['account_id']))) |
||||
| 784 | { |
||||
| 785 | $files[] = $this->add_account($account); |
||||
| 786 | } |
||||
| 787 | } |
||||
| 788 | else |
||||
| 789 | { |
||||
| 790 | // add all users (account_selection == groupmembers is handled by accounts->search()) |
||||
| 791 | foreach($this->accounts->search(array('type' => 'accounts','order' => 'account_lid')) as $account) |
||||
| 792 | { |
||||
| 793 | $files[] = $this->add_account($account); |
||||
| 794 | } |
||||
| 795 | } |
||||
| 796 | } |
||||
| 797 | } |
||||
| 798 | else |
||||
| 799 | { |
||||
| 800 | if (!($id = $this->accounts->name2id($name,'account_lid','u')) || |
||||
| 801 | !($account = $this->accounts->read($id)) || |
||||
| 802 | !$this->accounts->visible($name)) |
||||
| 803 | { |
||||
| 804 | $this->caldav->log(__METHOD__."('$name', ...) account '$name' NOT found OR not visible to you (check account-selection preference)!"); |
||||
| 805 | return '404 Not Found'; |
||||
| 806 | } |
||||
| 807 | while (substr($rest,-1) == '/') |
||||
| 808 | { |
||||
| 809 | $rest = substr($rest,0,-1); |
||||
| 810 | } |
||||
| 811 | switch((string)$rest) |
||||
| 812 | { |
||||
| 813 | case '': |
||||
| 814 | $files[] = $this->add_account($account); |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 815 | if ($options['depth']) |
||||
| 816 | { |
||||
| 817 | $files[] = $this->add_proxys('users/'.$account['account_lid'], 'calendar-proxy-read'); |
||||
| 818 | $files[] = $this->add_proxys('users/'.$account['account_lid'], 'calendar-proxy-write'); |
||||
| 819 | } |
||||
| 820 | break; |
||||
| 821 | case 'calendar-proxy-read': |
||||
| 822 | case 'calendar-proxy-write': |
||||
| 823 | $files = array(); |
||||
| 824 | $files[] = $this->add_proxys('users/'.$account['account_lid'], $rest); |
||||
| 825 | break; |
||||
| 826 | default: |
||||
| 827 | return '404 Not Found'; |
||||
| 828 | } |
||||
| 829 | } |
||||
| 830 | return $files; |
||||
| 831 | } |
||||
| 832 | |||||
| 833 | /** |
||||
| 834 | * Do propfind in /pricipals/groups |
||||
| 835 | * |
||||
| 836 | * @param string $name name of group or empty |
||||
| 837 | * @param string $rest rest of path behind account-name |
||||
| 838 | * @param array $options |
||||
| 839 | * @return array|string array with files or HTTP error code |
||||
| 840 | */ |
||||
| 841 | protected function propfind_groups($name,$rest,array $options) |
||||
| 842 | { |
||||
| 843 | //echo "<p>".__METHOD__."($name,$rest,".array2string($options).")</p>\n"; |
||||
| 844 | if (empty($name)) |
||||
| 845 | { |
||||
| 846 | $files = array(); |
||||
| 847 | // add /pricipals/users/ entry |
||||
| 848 | $files[] = $this->add_collection('/principals/groups/', array('displayname' => lang('Groups'))); |
||||
| 849 | |||||
| 850 | if ($options['depth']) |
||||
| 851 | { |
||||
| 852 | // only show own groups, if account-selection is groupmembers or none |
||||
| 853 | $type = in_array($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'], array('groupmembers','none')) ? |
||||
| 854 | 'owngroups' : 'groups'; |
||||
| 855 | |||||
| 856 | // add all groups or only membergroups |
||||
| 857 | foreach($this->accounts->search(array('type' => $type,'order' => 'account_lid')) as $account) |
||||
| 858 | { |
||||
| 859 | $files[] = $this->add_group($account); |
||||
| 860 | } |
||||
| 861 | } |
||||
| 862 | } |
||||
| 863 | else |
||||
| 864 | { |
||||
| 865 | if (!($id = $this->accounts->name2id($name,'account_lid','g')) || |
||||
| 866 | !($account = $this->accounts->read($id)) || |
||||
| 867 | // do NOT allow other groups, if account-selection is groupmembers or none |
||||
| 868 | in_array($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'], array('groupmembers','none')) && |
||||
| 869 | !in_array($account['account_id'], $this->accounts->memberships($GLOBALS['egw_info']['user']['account_id'],true))) |
||||
| 870 | { |
||||
| 871 | return '404 Not Found'; |
||||
| 872 | } |
||||
| 873 | while (substr($rest,-1) == '/') |
||||
| 874 | { |
||||
| 875 | $rest = substr($rest,0,-1); |
||||
| 876 | } |
||||
| 877 | switch((string)$rest) |
||||
| 878 | { |
||||
| 879 | case '': |
||||
| 880 | $files[] = $this->add_group($account); |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 881 | if ($options['depth']) |
||||
| 882 | { |
||||
| 883 | $files[] = $this->add_proxys('groups/'.$account['account_lid'], 'calendar-proxy-read'); |
||||
| 884 | $files[] = $this->add_proxys('groups/'.$account['account_lid'], 'calendar-proxy-write'); |
||||
| 885 | } |
||||
| 886 | break; |
||||
| 887 | case 'calendar-proxy-read': |
||||
| 888 | case 'calendar-proxy-write': |
||||
| 889 | $files = array(); |
||||
| 890 | $files[] = $this->add_proxys('groups/'.$account['account_lid'], $rest); |
||||
| 891 | break; |
||||
| 892 | default: |
||||
| 893 | return '404 Not Found'; |
||||
| 894 | } |
||||
| 895 | } |
||||
| 896 | return $files; |
||||
| 897 | } |
||||
| 898 | |||||
| 899 | /** |
||||
| 900 | * Get shared addressbooks of current user |
||||
| 901 | * |
||||
| 902 | * @return array with path relative to base URI (without addressbook postfix!) |
||||
| 903 | */ |
||||
| 904 | protected function get_shared_addressbooks() |
||||
| 905 | { |
||||
| 906 | $addressbooks = array(); |
||||
| 907 | $ab_home_set = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set']; |
||||
| 908 | if (empty($ab_home_set)) $ab_home_set = 'P'; // personal addressbook |
||||
| 909 | $addressbook_home_set = explode(',', $ab_home_set); |
||||
| 910 | // replace symbolic id's with real nummeric id's |
||||
| 911 | foreach(array( |
||||
| 912 | 'P' => $GLOBALS['egw_info']['user']['account_id'], |
||||
| 913 | 'G' => $GLOBALS['egw_info']['user']['account_primary_group'], |
||||
| 914 | 'U' => '0', |
||||
| 915 | ) as $sym => $id) |
||||
| 916 | { |
||||
| 917 | if (($key = array_search($sym, $addressbook_home_set)) !== false) |
||||
| 918 | { |
||||
| 919 | $addressbook_home_set[$key] = $id; |
||||
| 920 | } |
||||
| 921 | } |
||||
| 922 | if (in_array('O',$addressbook_home_set)) // "all in one" from groupdav.php/addressbook/ |
||||
| 923 | { |
||||
| 924 | $addressbooks[] = '/'; |
||||
| 925 | } |
||||
| 926 | foreach(array_keys($GLOBALS['egw']->contacts->get_addressbooks(Api\Acl::READ)) as $id) |
||||
| 927 | { |
||||
| 928 | if ((in_array('A',$addressbook_home_set) || in_array((string)$id,$addressbook_home_set)) && |
||||
| 929 | is_numeric($id) && ($owner = $this->accounts->id2name($id))) |
||||
| 930 | { |
||||
| 931 | $addressbooks[] = '/'.$owner.'/'; |
||||
|
0 ignored issues
–
show
Are you sure
$owner of type string|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 932 | } |
||||
| 933 | } |
||||
| 934 | return $addressbooks; |
||||
| 935 | } |
||||
| 936 | |||||
| 937 | /** |
||||
| 938 | * Add collection of a single account to a collection |
||||
| 939 | * |
||||
| 940 | * @param array $account |
||||
| 941 | * @return array with values for keys 'path' and 'props' |
||||
| 942 | */ |
||||
| 943 | protected function add_account(array $account) |
||||
| 944 | { |
||||
| 945 | $addressbooks = $calendars = array(); |
||||
| 946 | // since we "show" shared addressbooks and calendars in the user home, no need for individualiced homes |
||||
| 947 | $addressbooks[] = Api\CalDAV::mkprop('href', |
||||
| 948 | $this->base_uri.'/'.$account['account_lid'].'/'); |
||||
| 949 | $calendars[] = Api\CalDAV::mkprop('href', |
||||
| 950 | $this->base_uri.'/'.$account['account_lid'].'/'); |
||||
| 951 | |||||
| 952 | $displayname = Api\Translation::convert($account['account_fullname'], Api\Translation::charset(),'utf-8'); |
||||
| 953 | |||||
| 954 | return $this->add_principal('users/'.$account['account_lid'], array( |
||||
| 955 | 'getetag' => $this->get_etag($account), |
||||
| 956 | 'displayname' => $displayname, |
||||
| 957 | // CalDAV |
||||
| 958 | 'calendar-home-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-home-set',$calendars), |
||||
| 959 | // CalDAV scheduling |
||||
| 960 | 'schedule-outbox-URL' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'schedule-outbox-URL',array( |
||||
| 961 | Api\CalDAV::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/outbox/'))), |
||||
| 962 | 'schedule-inbox-URL' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'schedule-inbox-URL',array( |
||||
| 963 | Api\CalDAV::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/inbox/'))), |
||||
| 964 | 'calendar-user-address-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-address-set',array( |
||||
| 965 | Api\CalDAV::mkprop('href','mailto:'.$account['account_email']), |
||||
| 966 | Api\CalDAV::mkprop('href',$this->base_uri(true).'/principals/users/'.$account['account_lid'].'/'), |
||||
| 967 | Api\CalDAV::mkprop('href',$this->base_uri(false).'/principals/users/'.$account['account_lid'].'/'), |
||||
| 968 | Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('accounts', $account['account_id'])), |
||||
| 969 | )), |
||||
| 970 | 'calendar-user-type' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-type','INDIVIDUAL'), |
||||
| 971 | // Calendarserver |
||||
| 972 | 'email-address-set' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'email-address-set',array( |
||||
| 973 | Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'email-address',$account['account_email']))), |
||||
| 974 | 'last-name' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'last-name',$account['account_lastname']), |
||||
| 975 | 'first-name' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'first-name',$account['account_firstname']), |
||||
| 976 | 'record-type' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'record-type','users'), |
||||
| 977 | // WebDAV ACL and CalDAV proxy |
||||
| 978 | 'group-membership' => $this->principal_set('group-membership', $this->accounts->memberships($account['account_id']), |
||||
| 979 | array('calendar', 'resources'), $account['account_id']), // add proxy-rights |
||||
| 980 | 'alternate-URI-set' => array( |
||||
| 981 | Api\CalDAV::mkprop('href','mailto:'.$account['account_email'])), |
||||
| 982 | // CardDAV |
||||
| 983 | 'addressbook-home-set' => Api\CalDAV::mkprop(Api\CalDAV::CARDDAV,'addressbook-home-set',$addressbooks), |
||||
| 984 | 'principal-address' => Api\CalDAV::mkprop(Api\CalDAV::CARDDAV,'principal-address', |
||||
| 985 | $GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts'] === '1' ? '' : array( |
||||
| 986 | Api\CalDAV::mkprop('href',$this->base_uri.'/addressbook-accounts/'.$account['person_id'].'.vcf'))), |
||||
| 987 | // CardDAV directory |
||||
| 988 | 'directory-gateway' => Api\CalDAV::mkprop(Api\CalDAV::CARDDAV, 'directory-gateway',array( |
||||
| 989 | Api\CalDAV::mkprop('href', $this->base_uri.'/addressbook/'))), |
||||
| 990 | 'resource-id' => array(Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('accounts', $account['account_id']))), |
||||
| 991 | )); |
||||
| 992 | } |
||||
| 993 | |||||
| 994 | /** |
||||
| 995 | * Convert CalDAV principal URL to a calendar uid |
||||
| 996 | * |
||||
| 997 | * @param string $url |
||||
| 998 | * @param string|array $only_type =null allowed types, return false for other (valid) types, eg. "users", "groups" or "resources", default all |
||||
| 999 | * @param string $cn =null common name to be stored in case of an "e" uid |
||||
| 1000 | * @return int|string|boolean integer account_id, string calendar uid or false if not a supported uid |
||||
| 1001 | */ |
||||
| 1002 | static public function url2uid($url, $only_type=null, $cn=null) |
||||
| 1003 | { |
||||
| 1004 | if (!$only_type) $only_type = array('users', 'groups', 'resources', 'locations', 'mailto'); |
||||
| 1005 | |||||
| 1006 | if ($url[0] == '/') |
||||
| 1007 | { |
||||
| 1008 | $schema = 'http'; |
||||
| 1009 | } |
||||
| 1010 | else |
||||
| 1011 | { |
||||
| 1012 | list($schema, $rest) = explode(':', $url, 2); |
||||
| 1013 | } |
||||
| 1014 | if (empty($rest)) return false; |
||||
| 1015 | |||||
| 1016 | switch(strtolower($schema)) |
||||
| 1017 | { |
||||
| 1018 | case 'http': |
||||
| 1019 | case 'https': |
||||
| 1020 | list(,$rest) = explode('/groupdav.php/principals/', $url); |
||||
| 1021 | list($type, $name) = explode('/', $rest); |
||||
| 1022 | switch($type) |
||||
| 1023 | { |
||||
| 1024 | case 'users': |
||||
| 1025 | case 'groups': |
||||
| 1026 | $uid = $GLOBALS['egw']->accounts->name2id($name, 'account_lid', $type[0]); // u=users, g=groups |
||||
| 1027 | break; |
||||
| 1028 | case 'resources': |
||||
| 1029 | case 'locations': |
||||
| 1030 | $uid = 'r'.(int)$name; |
||||
| 1031 | break; |
||||
| 1032 | } |
||||
| 1033 | break; |
||||
| 1034 | |||||
| 1035 | case 'mailto': |
||||
| 1036 | if (($uid = $GLOBALS['egw']->accounts->name2id($rest, 'account_email'))) |
||||
| 1037 | { |
||||
| 1038 | $type = $uid > 0 ? 'users' : 'groups'; |
||||
| 1039 | break; |
||||
| 1040 | } |
||||
| 1041 | // search contacts for email |
||||
| 1042 | if ((list($data) = $GLOBALS['egw']->contacts->search(array('email' => $rest, 'email_home' => $rest), |
||||
| 1043 | array('id','egw_addressbook.account_id as account_id','n_fn'), |
||||
| 1044 | 'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC', |
||||
| 1045 | '','',false,'OR'))) |
||||
| 1046 | { |
||||
| 1047 | // found an addressbook entry |
||||
| 1048 | $uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id']; |
||||
| 1049 | $type = 'users'; |
||||
| 1050 | } |
||||
| 1051 | else // just store email-address |
||||
| 1052 | { |
||||
| 1053 | $uid = $cn && $rest[0] != '<' && $cn != $rest ? 'e'.$cn.' <'.$rest.'>' : 'e'.$rest; |
||||
| 1054 | $type = 'users'; |
||||
| 1055 | } |
||||
| 1056 | break; |
||||
| 1057 | |||||
| 1058 | case 'urn': |
||||
| 1059 | list($urn_type, $uid) = explode(':', $rest, 2); |
||||
| 1060 | list($type, $id, $install_id) = explode('-', $uid); |
||||
| 1061 | if ($type == 'accounts' && empty($id)) // groups have a negative id, eg. "urn:uuid:accounts--1-..." |
||||
| 1062 | { |
||||
| 1063 | list($type, , $id_abs, $install_id) = explode('-', $uid); |
||||
| 1064 | $id = -$id_abs; |
||||
| 1065 | } |
||||
| 1066 | // own urn |
||||
| 1067 | if ($urn_type === 'uuid' && $install_id === $GLOBALS['egw_info']['server']['install_id']) |
||||
| 1068 | { |
||||
| 1069 | if ($type == 'accounts') |
||||
| 1070 | { |
||||
| 1071 | $type = $id > 0 ? 'users' : 'groups'; |
||||
| 1072 | $uid = $id; |
||||
| 1073 | } |
||||
| 1074 | else |
||||
| 1075 | { |
||||
| 1076 | static $calendar_bo=null; |
||||
| 1077 | if (is_null($calendar_bo)) $calendar_bo = new calendar_bo(); |
||||
| 1078 | foreach($calendar_bo->resources as $letter => $info) |
||||
| 1079 | { |
||||
| 1080 | if ($info['app'] == $type || $info['app'] == 'resources' && $type == 'location') |
||||
| 1081 | { |
||||
| 1082 | $uid = $letter.$id; |
||||
| 1083 | } |
||||
| 1084 | } |
||||
| 1085 | } |
||||
| 1086 | } |
||||
| 1087 | // todo: store urn's from other EGroupware / calendarservers like email addresses ("CN <urn>" or "urn", maybe with 'u' prefix) |
||||
| 1088 | break; |
||||
| 1089 | |||||
| 1090 | default: |
||||
| 1091 | if (isset($GLOBALS['groupdav']) && is_a($GLOBALS['groupdav'],'groupdav')) |
||||
| 1092 | { |
||||
| 1093 | $GLOBALS['groupdav']->log(__METHOD__."('$url') unsupported principal URL '$url'!"); |
||||
| 1094 | } |
||||
| 1095 | return false; |
||||
| 1096 | } |
||||
| 1097 | //error_log(__METHOD__."('$url', ".array2string($only_type).") uid='$uid', type='$type' --> returning ".array2string($uid && in_array($type, $only_type) ? $uid : false)); |
||||
| 1098 | return $uid && in_array($type, $only_type) ? $uid : false; |
||||
| 1099 | } |
||||
| 1100 | |||||
| 1101 | /** |
||||
| 1102 | * Add collection of a single group to a collection |
||||
| 1103 | * |
||||
| 1104 | * @param array $account |
||||
| 1105 | * @return array with values for keys 'path' and 'props' |
||||
| 1106 | */ |
||||
| 1107 | protected function add_group(array $account) |
||||
| 1108 | { |
||||
| 1109 | $displayname = Api\Translation::convert(lang('Group').' '.$account['account_lid'], Api\Translation::charset(), 'utf-8'); |
||||
| 1110 | |||||
| 1111 | // only return current user, if account-selection == 'none' |
||||
| 1112 | if ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'none') |
||||
| 1113 | { |
||||
| 1114 | $groupmembers = array($GLOBALS['egw_info']['user']['account_id'] => $GLOBALS['egw_info']['user']['account_lid']); |
||||
| 1115 | } |
||||
| 1116 | else |
||||
| 1117 | { |
||||
| 1118 | $groupmembers = $this->accounts->members($account['account_id']); |
||||
| 1119 | } |
||||
| 1120 | |||||
| 1121 | return $this->add_principal('groups/'.$account['account_lid'], array( |
||||
| 1122 | 'getetag' => $this->get_etag($account), |
||||
| 1123 | 'displayname' => $displayname, |
||||
| 1124 | 'calendar-home-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-home-set',array( |
||||
| 1125 | Api\CalDAV::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), |
||||
| 1126 | 'addressbook-home-set' => Api\CalDAV::mkprop(Api\CalDAV::CARDDAV,'addressbook-home-set',array( |
||||
| 1127 | Api\CalDAV::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), |
||||
| 1128 | 'calendar-user-address-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-address-set',array( |
||||
| 1129 | Api\CalDAV::mkprop('href',$this->base_uri(true).'/principals/groups/'.$account['account_lid'].'/'), |
||||
| 1130 | Api\CalDAV::mkprop('href',$this->base_uri(false).'/principals/groups/'.$account['account_lid'].'/'), |
||||
| 1131 | Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('accounts', $account['account_id'])), |
||||
| 1132 | )), |
||||
| 1133 | 'record-type' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'record-type','groups'), |
||||
| 1134 | 'calendar-user-type' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-type','GROUP'), |
||||
| 1135 | 'group-member-set' => $this->principal_set('group-member-set', $groupmembers), |
||||
| 1136 | 'resource-id' => array(Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('accounts', $account['account_id']))), |
||||
| 1137 | )); |
||||
| 1138 | } |
||||
| 1139 | |||||
| 1140 | /** |
||||
| 1141 | * Add collection of a single resource to a collection |
||||
| 1142 | * |
||||
| 1143 | * @param array $resource |
||||
| 1144 | * @param boolean $is_location =null |
||||
| 1145 | * @return array with values for keys 'path' and 'props' |
||||
| 1146 | */ |
||||
| 1147 | protected function add_principal_resource(array $resource, $is_location=null) |
||||
| 1148 | { |
||||
| 1149 | $displayname = null; |
||||
| 1150 | $name = $this->resource2name($resource, $is_location, $displayname); |
||||
| 1151 | |||||
| 1152 | return $this->add_principal($name, array( |
||||
| 1153 | 'getetag' => $this->get_resource_etag($resource), |
||||
| 1154 | 'displayname' => $displayname, |
||||
| 1155 | 'calendar-user-address-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-address-set',array( |
||||
| 1156 | Api\CalDAV::mkprop('href',$this->base_uri(true).'/principals/'.$name.'/'), |
||||
| 1157 | Api\CalDAV::mkprop('href',$this->base_uri(false).'/principals/'.$name.'/'), |
||||
| 1158 | Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('resources', $resource['res_id'])), |
||||
| 1159 | )), |
||||
| 1160 | 'record-type' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'record-type',$is_location ? 'locations' : 'resources'), |
||||
| 1161 | 'calendar-user-type' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-user-type',$is_location ? 'ROOM' : 'RESOURCE'), |
||||
| 1162 | 'resource-id' => array(Api\CalDAV::mkprop('href','urn:uuid:'.Api\CalDAV::generate_uid('resources', $resource['res_id']))), |
||||
| 1163 | // Calendarserver also reports empty email-address-set, thought iCal still does not show resources (only locations) |
||||
| 1164 | 'email-address-set' => Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER,'email-address-set',''), |
||||
| 1165 | 'calendar-home-set' => Api\CalDAV::mkprop(Api\CalDAV::CALDAV,'calendar-home-set',array( |
||||
| 1166 | Api\CalDAV::mkprop('href',$this->base_uri.'/'.$name.'/'))), |
||||
| 1167 | )); |
||||
| 1168 | } |
||||
| 1169 | |||||
| 1170 | /** |
||||
| 1171 | * Get path of a resource-principal (relative to principal collection) |
||||
| 1172 | * |
||||
| 1173 | * @param array|int $resource |
||||
| 1174 | * @param boolean &$is_location=null |
||||
| 1175 | * @param string &$displayname=null on return displayname of resource |
||||
| 1176 | * @return string eg. "locations/123-some-room" or "resouces/345-some-device" |
||||
| 1177 | */ |
||||
| 1178 | public static function resource2name($resource, &$is_location=null, &$displayname=null) |
||||
| 1179 | { |
||||
| 1180 | if (!is_array($resource) && !($resource = self::read_resource($resource))) |
||||
| 1181 | { |
||||
| 1182 | return null; |
||||
| 1183 | } |
||||
| 1184 | if (is_null($is_location)) $is_location = self::resource_is_location($resource); |
||||
| 1185 | |||||
| 1186 | $displayname = Api\Translation::convert($resource['name'], Api\Translation::charset(), 'utf-8'); |
||||
| 1187 | |||||
| 1188 | return ($is_location ? 'locations/' : 'resources/').$resource['res_id'].'-'. |
||||
| 1189 | preg_replace('/[^a-z0-9]+/i','-', Api\Translation::to_ascii($resource['name'])); |
||||
| 1190 | } |
||||
| 1191 | |||||
| 1192 | /** |
||||
| 1193 | * Check if resource is a location |
||||
| 1194 | * |
||||
| 1195 | * @param array|int $resource |
||||
| 1196 | * @return boolean |
||||
| 1197 | */ |
||||
| 1198 | public static function resource_is_location($resource) |
||||
| 1199 | { |
||||
| 1200 | static $location_cats=null; |
||||
| 1201 | if (is_null($location_cats)) |
||||
| 1202 | { |
||||
| 1203 | $config = Api\Config::read('resources'); |
||||
| 1204 | $location_cats = $config['location_cats'] ? explode(',', $config['location_cats']) : array(); |
||||
| 1205 | } |
||||
| 1206 | if (!is_array($resource) && !($resource = self::read_resource($resource))) |
||||
| 1207 | { |
||||
| 1208 | return null; |
||||
| 1209 | } |
||||
| 1210 | return $resource['cat_id'] && in_array($resource['cat_id'], $location_cats); |
||||
| 1211 | } |
||||
| 1212 | |||||
| 1213 | /** |
||||
| 1214 | * Read a resource |
||||
| 1215 | * |
||||
| 1216 | * @param int $res_id resource-id |
||||
| 1217 | * @return array with values for res_id, cat_id and name |
||||
| 1218 | */ |
||||
| 1219 | public static function read_resource($res_id) |
||||
| 1220 | { |
||||
| 1221 | static $cache=null; // some per-request caching |
||||
| 1222 | |||||
| 1223 | if (isset(self::$all_resources) && isset(self::$all_resources[$res_id])) |
||||
| 1224 | { |
||||
| 1225 | return self::$all_resources[$res_id]; |
||||
| 1226 | } |
||||
| 1227 | |||||
| 1228 | if (!isset($cache[$res_id])) |
||||
| 1229 | { |
||||
| 1230 | if (!isset(self::$resources)) self::$resources = new resources_bo(); |
||||
| 1231 | |||||
| 1232 | if (!($cache[$res_id] = self::$resources->read($res_id))) |
||||
| 1233 | { |
||||
| 1234 | return null; |
||||
| 1235 | } |
||||
| 1236 | } |
||||
| 1237 | return $cache[$res_id]; |
||||
| 1238 | } |
||||
| 1239 | |||||
| 1240 | /** |
||||
| 1241 | * Get an etag for a resource |
||||
| 1242 | * |
||||
| 1243 | * @param array $resource |
||||
| 1244 | * @return string |
||||
| 1245 | */ |
||||
| 1246 | protected function get_resource_etag(array $resource) |
||||
| 1247 | { |
||||
| 1248 | return md5(serialize($resource)).'-'.(self::resource_is_location($resource) ? 'l' : 'r'); |
||||
| 1249 | } |
||||
| 1250 | |||||
| 1251 | /** |
||||
| 1252 | * Cache for get_resources |
||||
| 1253 | * |
||||
| 1254 | * @var array |
||||
| 1255 | */ |
||||
| 1256 | private static $all_resources; |
||||
| 1257 | /** |
||||
| 1258 | * Get all resources (we cache the resources here, to only query them once per request) |
||||
| 1259 | * |
||||
| 1260 | * @param int $user =null account_if of user, or null for current user |
||||
| 1261 | * @return array of array with values for res_id, cat_id and name (no other values1) |
||||
| 1262 | */ |
||||
| 1263 | public static function get_resources($user=null) |
||||
| 1264 | { |
||||
| 1265 | if (!isset(self::$all_resources)) |
||||
| 1266 | { |
||||
| 1267 | if (!isset(self::$resources)) self::$resources = new resources_bo($user); |
||||
| 1268 | |||||
| 1269 | self::$all_resources = array(); |
||||
| 1270 | $query = array( |
||||
| 1271 | 'show_bookable' => true, // ignore non-bookable resources |
||||
| 1272 | 'filter2' => -3, |
||||
| 1273 | 'start' => 0, |
||||
| 1274 | 'num_rows' => 10000, // return all aka first 10000 entries |
||||
| 1275 | ); |
||||
| 1276 | $rows = $readonlys = null; |
||||
| 1277 | if (self::$resources->get_rows($query, $rows, $readonlys)) |
||||
| 1278 | { |
||||
| 1279 | //_debug_array($rows); |
||||
| 1280 | foreach($rows as $resource) |
||||
| 1281 | { |
||||
| 1282 | self::$all_resources[$resource['res_id']] = array_intersect_key($resource, array('res_id'=>true,'cat_id'=>true,'name'=>true)); |
||||
| 1283 | } |
||||
| 1284 | } |
||||
| 1285 | } |
||||
| 1286 | return self::$all_resources; |
||||
| 1287 | } |
||||
| 1288 | |||||
| 1289 | /** |
||||
| 1290 | * Get category based ACL rights for resouces |
||||
| 1291 | * |
||||
| 1292 | * Cached to not query it multiple times per request |
||||
| 1293 | * |
||||
| 1294 | * @return array of 'L'.$cat_id => array($account_id => $rights) pairs |
||||
| 1295 | */ |
||||
| 1296 | protected function get_resource_rights() |
||||
| 1297 | { |
||||
| 1298 | static $grants=null; |
||||
| 1299 | |||||
| 1300 | if (is_null($grants)) |
||||
| 1301 | { |
||||
| 1302 | $grants = $this->acl->get_location_grants('L%', 'resources'); |
||||
| 1303 | } |
||||
| 1304 | return $grants; |
||||
| 1305 | } |
||||
| 1306 | |||||
| 1307 | /** |
||||
| 1308 | * Add a collection |
||||
| 1309 | * |
||||
| 1310 | * @param string $path |
||||
| 1311 | * @param array $props =array() extra properties 'resourcetype' is added anyway, name => value pairs or name => Api\CalDAV([namespace,]name,value) |
||||
| 1312 | * @return array with values for keys 'path' and 'props' |
||||
| 1313 | */ |
||||
| 1314 | protected function add_collection($path, array $props = array()) |
||||
| 1315 | { |
||||
| 1316 | if ($this->caldav->prop_requested('supported-report-set')) |
||||
| 1317 | { |
||||
| 1318 | $props['supported-report-set'] = $this->supported_report_set($path); |
||||
| 1319 | } |
||||
| 1320 | return $this->caldav->add_collection($path, $props); |
||||
| 1321 | } |
||||
| 1322 | |||||
| 1323 | /** |
||||
| 1324 | * Add a principal collection |
||||
| 1325 | * |
||||
| 1326 | * @param string $principal relative to principal-collection-set, eg. "users/username" |
||||
| 1327 | * @param array $props =array() extra properties 'resourcetype' is added anyway |
||||
| 1328 | * @param string $principal_url =null include given principal url, relative to principal-collection-set, default $principal |
||||
| 1329 | * @return array with values for keys 'path' and 'props' |
||||
| 1330 | */ |
||||
| 1331 | protected function add_principal($principal, array $props = array(), $principal_url=null) |
||||
| 1332 | { |
||||
| 1333 | $props['resourcetype'][] = Api\CalDAV::mkprop('principal', ''); |
||||
| 1334 | |||||
| 1335 | // required props per WebDAV ACL |
||||
| 1336 | foreach(array('alternate-URI-set', 'group-membership') as $name) |
||||
| 1337 | { |
||||
| 1338 | if (!isset($props[$name])) $props[$name] = Api\CalDAV::mkprop($name,''); |
||||
| 1339 | } |
||||
| 1340 | if (!$principal_url) $principal_url = $principal; |
||||
|
0 ignored issues
–
show
|
|||||
| 1341 | |||||
| 1342 | $props['principal-URL'] = array( |
||||
| 1343 | Api\CalDAV::mkprop('href',$this->base_uri.'/principals/'.$principal.'/')); |
||||
| 1344 | |||||
| 1345 | return $this->add_collection('/principals/'.$principal.'/', $props); |
||||
| 1346 | } |
||||
| 1347 | |||||
| 1348 | /** |
||||
| 1349 | * Add a proxy collection for given principal and type |
||||
| 1350 | * |
||||
| 1351 | * A proxy is a user or group who has the right to act on behalf of the user |
||||
| 1352 | * |
||||
| 1353 | * @param string $principal relative to principal-collection-set, eg. "users/username" |
||||
| 1354 | * @param string $type eg. 'calendar-proxy-read' or 'calendar-proxy-write' |
||||
| 1355 | * @param array $proxys =array() |
||||
| 1356 | * @param array $resource =null resource to use (to not query it multiple times from the database) |
||||
| 1357 | * @return array with values for 'path' and 'props' |
||||
| 1358 | */ |
||||
| 1359 | protected function add_proxys($principal, $type, array $proxys=array(), array $resource=null) |
||||
| 1360 | { |
||||
| 1361 | list($app,,$what) = explode('-', $type); |
||||
| 1362 | |||||
| 1363 | if (true) $proxys = array(); // ignore parameter! |
||||
| 1364 | list($account_type,$account) = explode('/', $principal); |
||||
| 1365 | |||||
| 1366 | switch($account_type) |
||||
| 1367 | { |
||||
| 1368 | case 'users': |
||||
| 1369 | case 'groups': |
||||
| 1370 | $account = $location = $this->accounts->name2id($account, 'account_lid', $account_type[0]); |
||||
| 1371 | $right = $what == 'write' ? Api\Acl::EDIT : Api\Acl::READ; |
||||
| 1372 | $mask = $what == 'write' ? Api\Acl::EDIT : Api\Acl::EDIT|Api\Acl::READ; // do NOT report write+read in read |
||||
| 1373 | break; |
||||
| 1374 | |||||
| 1375 | case 'locations': |
||||
| 1376 | case 'resources': |
||||
| 1377 | $app = 'resources'; |
||||
| 1378 | if (!is_array($resource) || $resource['res_id'] == (int)$account) |
||||
| 1379 | { |
||||
| 1380 | $resource = self::read_resource((int)$account); |
||||
| 1381 | } |
||||
| 1382 | $location = 'L'.$resource['cat_id']; |
||||
| 1383 | $right = $what == 'write' ? resources_acl_bo::DIRECT_BOOKING : resources_acl_bo::CAL_READ; |
||||
| 1384 | $mask = $what == 'write' ? resources_acl_bo::DIRECT_BOOKING : resources_acl_bo::DIRECT_BOOKING|resources_acl_bo::CAL_READ; // do NOT report write+read in read |
||||
| 1385 | break; |
||||
| 1386 | } |
||||
| 1387 | static $principal2grants = array(); |
||||
| 1388 | $grants =& $principal2grants[$principal]; |
||||
| 1389 | if (!isset($grants)) |
||||
| 1390 | { |
||||
| 1391 | switch($app) |
||||
| 1392 | { |
||||
| 1393 | case 'resources': |
||||
| 1394 | $res_grants = $this->get_resource_rights(); |
||||
| 1395 | $grants = (array)$res_grants[$location]; // returns array($location => $grants) |
||||
| 1396 | break; |
||||
| 1397 | |||||
| 1398 | case 'calendar': |
||||
| 1399 | default: |
||||
| 1400 | $grants = $this->acl->get_all_location_rights($account, $app, $app != 'addressbook'); |
||||
| 1401 | break; |
||||
| 1402 | } |
||||
| 1403 | //echo "<p>type=$type --> app=$app, what=$what --> right=$right, mask=$mask, account=$account, location=$location --> grants=".array2string($grants)."</p>\n"; |
||||
| 1404 | } |
||||
| 1405 | foreach($grants as $account_id => $rights) |
||||
| 1406 | { |
||||
| 1407 | if ($account_id !== 'run' && $account_id != $account && ($rights & $mask) == $right && |
||||
| 1408 | ($account_lid = $this->accounts->id2name($account_id))) |
||||
| 1409 | { |
||||
| 1410 | // ignore "broken" grants (eg. negative account_id for a user), as they lead to further errors (no members) |
||||
| 1411 | if (($t = $this->accounts->exists($account_id) == 1 ? 'u' : 'g') !== $this->accounts->get_type($account_id)) |
||||
| 1412 | { |
||||
| 1413 | error_log(__METHOD__."('$principal', '$type') broken grants from #$account_id ($account_lid) type=$t ignored!"); |
||||
| 1414 | continue; |
||||
| 1415 | } |
||||
| 1416 | $proxys[$account_id] = $account_lid; |
||||
| 1417 | // no need to add group-members, ACL grants to groups are understood by WebDAV ACL (tested with iCal) |
||||
| 1418 | } |
||||
| 1419 | //echo "<p>$account_id ($account_lid) type=$t: (rights=$rights & mask=$mask) == right=$right --> ".array2string(($rights & $mask) == $right)."</p>\n"; |
||||
| 1420 | } |
||||
| 1421 | return $this->add_principal($principal.'/'.$type, array( |
||||
| 1422 | 'displayname' => lang('%1 proxy of %2', lang($app).' '.lang($what), basename($principal)), |
||||
|
0 ignored issues
–
show
The call to
lang() has too many arguments starting with lang($app) . ' ' . lang($what).
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. Loading history...
|
|||||
| 1423 | 'group-member-set' => $this->principal_set('group-member-set', $proxys), |
||||
| 1424 | 'getetag' => md5(serialize($proxys)), |
||||
| 1425 | 'resourcetype' => array(Api\CalDAV::mkprop(Api\CalDAV::CALENDARSERVER, $type, '')), |
||||
| 1426 | )); |
||||
| 1427 | } |
||||
| 1428 | |||||
| 1429 | /** |
||||
| 1430 | * Create a named property with set or principal-urls |
||||
| 1431 | * |
||||
| 1432 | * @param string $prop egw. 'group-member-set' or 'membership' |
||||
| 1433 | * @param array $accounts =array() account_id => account_lid pairs |
||||
| 1434 | * @param string|array $app_proxys =null applications for which proxys should be added |
||||
| 1435 | * @param int $account who is the proxy |
||||
| 1436 | * @return array with href props |
||||
| 1437 | */ |
||||
| 1438 | protected function principal_set($prop, array $accounts=array(), $app_proxys=null, $account=null) |
||||
| 1439 | { |
||||
| 1440 | unset($prop); // not used, but required by function signature |
||||
| 1441 | |||||
| 1442 | $set = array(); |
||||
| 1443 | foreach($accounts as $account_id => $account_lid) |
||||
| 1444 | { |
||||
| 1445 | if ($this->accounts->visible($account_lid)) // only add visible accounts, gives error in iCal otherwise |
||||
| 1446 | { |
||||
| 1447 | $set[] = Api\CalDAV::mkprop('href', $this->base_uri.'/principals/'.($account_id < 0 ? 'groups/' : 'users/').$account_lid.'/'); |
||||
| 1448 | } |
||||
| 1449 | } |
||||
| 1450 | if ($app_proxys) |
||||
| 1451 | { |
||||
| 1452 | foreach((array)$app_proxys as $app) |
||||
| 1453 | { |
||||
| 1454 | if (!isset($GLOBALS['egw_info']['user']['apps'][$app])) continue; |
||||
| 1455 | |||||
| 1456 | switch($app) |
||||
| 1457 | { |
||||
| 1458 | case 'resources': |
||||
| 1459 | $proxy_groups = $this->get_resource_proxy_groups($account); |
||||
| 1460 | break; |
||||
| 1461 | default: |
||||
| 1462 | $proxy_groups = $this->get_calendar_proxy_groups($account, $app); |
||||
| 1463 | break; |
||||
| 1464 | } |
||||
| 1465 | $set = array_merge($set, $proxy_groups); |
||||
| 1466 | } |
||||
| 1467 | } |
||||
| 1468 | return $set; |
||||
| 1469 | } |
||||
| 1470 | |||||
| 1471 | /** |
||||
| 1472 | * Get proxy-groups for given user $account: users or groups who GRANT proxy rights to $account |
||||
| 1473 | * |
||||
| 1474 | * @param int $account who is the proxy |
||||
| 1475 | * @param string|array $app_proxys =null applications for which proxys should be added |
||||
| 1476 | * @return array with href props |
||||
| 1477 | */ |
||||
| 1478 | protected function get_resource_proxy_groups($account) |
||||
| 1479 | { |
||||
| 1480 | $set = array(); |
||||
| 1481 | if (($resources = $this->get_resources())) |
||||
| 1482 | { |
||||
| 1483 | // location_grants = array(location => array(account_id => rights)) |
||||
| 1484 | $all_location_grants = $this->get_resource_rights(); |
||||
| 1485 | // get location grants for $account (incl. his memberships) |
||||
| 1486 | $memberships = $GLOBALS['egw']->accounts->memberships($account, true); |
||||
| 1487 | $location_grants = array(); |
||||
| 1488 | foreach($all_location_grants as $location => $grants) |
||||
| 1489 | { |
||||
| 1490 | foreach($grants as $account_id => $rights) |
||||
| 1491 | { |
||||
| 1492 | if (($rights & (resources_acl_bo::CAL_READ|resources_acl_bo::DIRECT_BOOKING)) && // we only care for these rights |
||||
| 1493 | ($account_id == $account || in_array($account_id, $memberships))) |
||||
| 1494 | { |
||||
| 1495 | if (!isset($location_grants[$location])) $location_grants[$location] = 0; |
||||
| 1496 | $location_grants[$location] |= $rights; |
||||
| 1497 | } |
||||
| 1498 | } |
||||
| 1499 | } |
||||
| 1500 | // now add proxy-groups for all resources user has rights to |
||||
| 1501 | foreach($resources as $resource) |
||||
| 1502 | { |
||||
| 1503 | $rights = $location_grants['L'.$resource['cat_id']]; |
||||
| 1504 | if (isset($rights)) |
||||
| 1505 | { |
||||
| 1506 | $set[] = Api\CalDAV::mkprop('href', $this->base_uri.'/principals/'.$this->resource2name($resource). |
||||
| 1507 | '/calendar-proxy-'.($rights & resources_acl_bo::DIRECT_BOOKING ? 'write' : 'read').'/'); |
||||
| 1508 | } |
||||
| 1509 | } |
||||
| 1510 | } |
||||
| 1511 | //echo "get_resource_proxy_groups($account)"; _debug_array($set); |
||||
| 1512 | return $set; |
||||
| 1513 | } |
||||
| 1514 | |||||
| 1515 | /** |
||||
| 1516 | * Get proxy-groups for given user $account: users or groups who GRANT proxy rights to $account |
||||
| 1517 | * |
||||
| 1518 | * @param int $account who is the proxy |
||||
| 1519 | * @param string $app ='calendar' applications for which proxys should be added |
||||
| 1520 | * @return array with href props |
||||
| 1521 | */ |
||||
| 1522 | protected function get_calendar_proxy_groups($account, $app='calendar') |
||||
| 1523 | { |
||||
| 1524 | $set = array(); |
||||
| 1525 | foreach($this->acl->get_grants($app, $app != 'addressbook', $account) as $account_id => $rights) |
||||
| 1526 | { |
||||
| 1527 | if ($account_id != $account && ($rights & Api\Acl::READ) && |
||||
| 1528 | ($account_lid = $this->accounts->id2name($account_id)) && |
||||
| 1529 | $this->accounts->visible($account_lid)) // only add visible accounts, gives error in iCal otherwise |
||||
| 1530 | { |
||||
| 1531 | $set[] = Api\CalDAV::mkprop('href', $this->base_uri.'/principals/'. |
||||
| 1532 | ($account_id < 0 ? 'groups/' : 'users/'). |
||||
| 1533 | $account_lid.'/'.$app.'-proxy-'.($rights & Api\Acl::EDIT ? 'write' : 'read').'/'); |
||||
|
0 ignored issues
–
show
Are you sure
$account_lid of type string|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 1534 | } |
||||
| 1535 | } |
||||
| 1536 | return $set; |
||||
| 1537 | } |
||||
| 1538 | |||||
| 1539 | /** |
||||
| 1540 | * Do propfind in /pricipals/(resources|locations) |
||||
| 1541 | * |
||||
| 1542 | * @param string $name name of group or empty |
||||
| 1543 | * @param string $rest rest of path behind account-name |
||||
| 1544 | * @param array $options |
||||
| 1545 | * @param boolean $do_locations =false false: /principal/resources, true: /principals/locations |
||||
| 1546 | * @return array|string array with files or HTTP error code |
||||
| 1547 | */ |
||||
| 1548 | protected function propfind_resources($name,$rest,array $options,$do_locations=false) |
||||
| 1549 | { |
||||
| 1550 | if (!isset($GLOBALS['egw_info']['user']['apps']['resources'])) |
||||
| 1551 | { |
||||
| 1552 | return '404 Not Found'; |
||||
| 1553 | } |
||||
| 1554 | //echo "<p>".__METHOD__."('$name', '$rest', ".array2string($options).', '.array2string($do_locations).")</p>\n"; |
||||
| 1555 | if (empty($name)) |
||||
| 1556 | { |
||||
| 1557 | $files = array(); |
||||
| 1558 | // add /pricipals/users/ entry |
||||
| 1559 | $files[] = $this->add_collection('/principals/'.($do_locations ? 'locations/' : 'resources/'), |
||||
| 1560 | array('displayname' => $do_locations ? lang('Locations') : lang('Resources'))); |
||||
| 1561 | |||||
| 1562 | if ($options['depth']) |
||||
| 1563 | { |
||||
| 1564 | if (($resources = $this->get_resources())) |
||||
| 1565 | { |
||||
| 1566 | //_debug_array($resources); |
||||
| 1567 | foreach($resources as $resource) |
||||
| 1568 | { |
||||
| 1569 | if (($is_location = self::resource_is_location($resource)) == $do_locations) |
||||
| 1570 | { |
||||
| 1571 | $files[] = $this->add_principal_resource($resource, $is_location); |
||||
| 1572 | } |
||||
| 1573 | } |
||||
| 1574 | } |
||||
| 1575 | } |
||||
| 1576 | } |
||||
| 1577 | else |
||||
| 1578 | { |
||||
| 1579 | if (!($resource = self::read_resource((int)$name)) || ($is_location = self::resource_is_location($resource)) != $do_locations) |
||||
| 1580 | { |
||||
| 1581 | return '404 Not Found'; |
||||
| 1582 | } |
||||
| 1583 | $path = ($is_location ? 'locations/' : 'resources/').$name; |
||||
| 1584 | while (substr($rest,-1) == '/') |
||||
| 1585 | { |
||||
| 1586 | $rest = substr($rest,0,-1); |
||||
| 1587 | } |
||||
| 1588 | switch((string)$rest) |
||||
| 1589 | { |
||||
| 1590 | case '': |
||||
| 1591 | $files[] = $this->add_principal_resource($resource); |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 1592 | if ($options['depth']) |
||||
| 1593 | { |
||||
| 1594 | $files[] = $this->add_proxys($path, 'calendar-proxy-read', array(), $resource); |
||||
| 1595 | $files[] = $this->add_proxys($path, 'calendar-proxy-write', array(), $resource); |
||||
| 1596 | } |
||||
| 1597 | break; |
||||
| 1598 | case 'calendar-proxy-read': |
||||
| 1599 | case 'calendar-proxy-write': |
||||
| 1600 | $files = array(); |
||||
| 1601 | $files[] = $this->add_proxys($path, $rest, array(), $resource); |
||||
| 1602 | break; |
||||
| 1603 | default: |
||||
| 1604 | return '404 Not Found'; |
||||
| 1605 | } |
||||
| 1606 | } |
||||
| 1607 | return $files; |
||||
| 1608 | } |
||||
| 1609 | |||||
| 1610 | /** |
||||
| 1611 | * Do propfind of /principals/ |
||||
| 1612 | * |
||||
| 1613 | * @param string $name name of group or empty |
||||
| 1614 | * @param string $rest name of rest of path behind group-name |
||||
| 1615 | * @param array $options |
||||
| 1616 | * @return array|string array with files or HTTP error code |
||||
| 1617 | */ |
||||
| 1618 | protected function propfind_principals(array $options) |
||||
| 1619 | { |
||||
| 1620 | //echo "<p>".__METHOD__."(".array($options).")</p>\n"; |
||||
| 1621 | $files = array(); |
||||
| 1622 | $files[] = $this->add_collection('/principals/'); |
||||
| 1623 | |||||
| 1624 | if ($options['depth']) |
||||
| 1625 | { |
||||
| 1626 | if (is_numeric($options['depth'])) --$options['depth']; |
||||
| 1627 | $files = array_merge($files, $this->propfind_users('','',$options), |
||||
| 1628 | $this->propfind_groups('','',$options)); |
||||
| 1629 | if ($GLOBALS['egw_info']['user']['apps']['resources']) |
||||
| 1630 | { |
||||
| 1631 | $files = array_merge($files, $this->propfind_resources('','',$options,false), // resources |
||||
| 1632 | $this->propfind_resources('','',$options,true)); // locations |
||||
| 1633 | } |
||||
| 1634 | //$files = array_merge($files,$this->propfind_uids('','',$options)); |
||||
| 1635 | } |
||||
| 1636 | return $files; |
||||
| 1637 | } |
||||
| 1638 | |||||
| 1639 | /** |
||||
| 1640 | * Handle get request for an applications entry |
||||
| 1641 | * |
||||
| 1642 | * @param array &$options |
||||
| 1643 | * @param int $id |
||||
| 1644 | * @param int $user =null account_id |
||||
| 1645 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 1646 | */ |
||||
| 1647 | function get(&$options,$id,$user=null) |
||||
| 1648 | { |
||||
| 1649 | unset($options, $id, $user); // not used, but required by function signature |
||||
| 1650 | |||||
| 1651 | return false; |
||||
| 1652 | } |
||||
| 1653 | |||||
| 1654 | /** |
||||
| 1655 | * Handle get request for an applications entry |
||||
| 1656 | * |
||||
| 1657 | * @param array &$options |
||||
| 1658 | * @param int $id |
||||
| 1659 | * @param int $user =null account_id of owner, default null |
||||
| 1660 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 1661 | */ |
||||
| 1662 | function put(&$options,$id,$user=null) |
||||
| 1663 | { |
||||
| 1664 | unset($options, $id, $user); // not used, but required by function signature |
||||
| 1665 | |||||
| 1666 | return false; |
||||
| 1667 | } |
||||
| 1668 | |||||
| 1669 | /** |
||||
| 1670 | * Handle get request for an applications entry |
||||
| 1671 | * |
||||
| 1672 | * @param array &$options |
||||
| 1673 | * @param int $id |
||||
| 1674 | * @param int $user account_id of collection owner |
||||
| 1675 | * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') |
||||
| 1676 | */ |
||||
| 1677 | function delete(&$options,$id,$user) |
||||
| 1678 | { |
||||
| 1679 | unset($options, $id, $user); // not used, but required by function signature |
||||
| 1680 | |||||
| 1681 | return false; |
||||
| 1682 | } |
||||
| 1683 | |||||
| 1684 | /** |
||||
| 1685 | * Read an entry |
||||
| 1686 | * |
||||
| 1687 | * @param string|int $id |
||||
| 1688 | * @return array/boolean array with entry, false if no read rights, null if $id does not exist |
||||
|
0 ignored issues
–
show
|
|||||
| 1689 | */ |
||||
| 1690 | function read($id) |
||||
| 1691 | { |
||||
| 1692 | unset($id); // not used, but required by function signature |
||||
| 1693 | |||||
| 1694 | return false; |
||||
| 1695 | } |
||||
| 1696 | |||||
| 1697 | /** |
||||
| 1698 | * Check if user has the neccessary rights on an entry |
||||
| 1699 | * |
||||
| 1700 | * @param int $acl Api\Acl::READ, Api\Acl::EDIT or Api\Acl::DELETE |
||||
| 1701 | * @param array|int $entry entry-array or id |
||||
| 1702 | * @return boolean null if entry does not exist, false if no access, true if access permitted |
||||
| 1703 | */ |
||||
| 1704 | function check_access($acl,$entry) |
||||
| 1705 | { |
||||
| 1706 | if ($acl != Api\Acl::READ) |
||||
| 1707 | { |
||||
| 1708 | return false; |
||||
| 1709 | } |
||||
| 1710 | if (!is_array($entry) && !$this->accounts->name2id($entry,'account_lid','u')) |
||||
|
0 ignored issues
–
show
The expression
$this->accounts->name2id...ry, 'account_lid', 'u') of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
| 1711 | { |
||||
| 1712 | return null; |
||||
| 1713 | } |
||||
| 1714 | return true; |
||||
| 1715 | } |
||||
| 1716 | |||||
| 1717 | /** |
||||
| 1718 | * Get the etag for an entry, can be reimplemented for other algorithm or field names |
||||
| 1719 | * |
||||
| 1720 | * @param array|int $account array with event or cal_id |
||||
| 1721 | * @return string/boolean string with etag or false |
||||
|
0 ignored issues
–
show
|
|||||
| 1722 | */ |
||||
| 1723 | function get_etag($account) |
||||
| 1724 | { |
||||
| 1725 | if (!is_array($account)) |
||||
| 1726 | { |
||||
| 1727 | $account = $this->read($account); |
||||
| 1728 | } |
||||
| 1729 | return $account['account_id'].':'.md5(serialize($account)). |
||||
| 1730 | // add md5 from calendar & resource grants, as they are listed as memberships |
||||
| 1731 | ':'.md5(serialize($this->acl->get_grants('calendar', true, $account['account_id'])). |
||||
| 1732 | serialize($this->get_resource_rights())). |
||||
| 1733 | // as the principal of current user is influenced by GroupDAV prefs, we have to include them in the etag |
||||
| 1734 | ($account['account_id'] == $GLOBALS['egw_info']['user']['account_id'] ? |
||||
| 1735 | ':'.md5(serialize($GLOBALS['egw_info']['user']['preferences']['groupdav'])) : ''); |
||||
| 1736 | } |
||||
| 1737 | |||||
| 1738 | /** |
||||
| 1739 | * Return privileges for current user, default is read and read-current-user-privilege-set |
||||
| 1740 | * |
||||
| 1741 | * Privileges are for the collection, not the resources / entries! |
||||
| 1742 | * |
||||
| 1743 | * @param string $path path of collection |
||||
| 1744 | * @param int $user =null owner of the collection, default current user |
||||
| 1745 | * @return array with privileges |
||||
| 1746 | */ |
||||
| 1747 | public function current_user_privileges($path, $user=null) |
||||
| 1748 | { |
||||
| 1749 | unset($path, $user); // not used, but required by function signature |
||||
| 1750 | |||||
| 1751 | return array('read', 'read-current-user-privilege-set'); |
||||
| 1752 | } |
||||
| 1753 | } |
||||
| 1754 |