Пример #1
1
 /**
  * PropFind
  *
  * This method handler is invoked before any after properties for a
  * resource are fetched. This allows us to add in any CalDAV specific
  * properties.
  *
  * @param DAV\PropFind $propFind
  * @param DAV\INode $node
  * @return void
  */
 function propFind(DAV\PropFind $propFind, DAV\INode $node)
 {
     $ns = '{' . self::NS_CALDAV . '}';
     if ($node instanceof ICalendarObjectContainer) {
         $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize);
         $propFind->handle($ns . 'supported-calendar-data', function () {
             return new Xml\Property\SupportedCalendarData();
         });
         $propFind->handle($ns . 'supported-collation-set', function () {
             return new Xml\Property\SupportedCollationSet();
         });
     }
     if ($node instanceof DAVACL\IPrincipal) {
         $principalUrl = $node->getPrincipalUrl();
         $propFind->handle('{' . self::NS_CALDAV . '}calendar-home-set', function () use($principalUrl) {
             $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl) . '/';
             return new Href($calendarHomePath);
         });
         // The calendar-user-address-set property is basically mapped to
         // the {DAV:}alternate-URI-set property.
         $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-address-set', function () use($node) {
             $addresses = $node->getAlternateUriSet();
             $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/';
             return new Href($addresses, false);
         });
         // For some reason somebody thought it was a good idea to add
         // another one of these properties. We're supporting it too.
         $propFind->handle('{' . self::NS_CALENDARSERVER . '}email-address-set', function () use($node) {
             $addresses = $node->getAlternateUriSet();
             $emails = [];
             foreach ($addresses as $address) {
                 if (substr($address, 0, 7) === 'mailto:') {
                     $emails[] = substr($address, 7);
                 }
             }
             return new Xml\Property\EmailAddressSet($emails);
         });
         // These two properties are shortcuts for ical to easily find
         // other principals this principal has access to.
         $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
         $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
         if ($propFind->getStatus($propRead) === 404 || $propFind->getStatus($propWrite) === 404) {
             $aclPlugin = $this->server->getPlugin('acl');
             $membership = $aclPlugin->getPrincipalMembership($propFind->getPath());
             $readList = [];
             $writeList = [];
             foreach ($membership as $group) {
                 $groupNode = $this->server->tree->getNodeForPath($group);
                 $listItem = Uri\split($group)[0] . '/';
                 // If the node is either ap proxy-read or proxy-write
                 // group, we grab the parent principal and add it to the
                 // list.
                 if ($groupNode instanceof Principal\IProxyRead) {
                     $readList[] = $listItem;
                 }
                 if ($groupNode instanceof Principal\IProxyWrite) {
                     $writeList[] = $listItem;
                 }
             }
             $propFind->set($propRead, new Href($readList));
             $propFind->set($propWrite, new Href($writeList));
         }
     }
     // instanceof IPrincipal
     if ($node instanceof ICalendarObject) {
         // The calendar-data property is not supposed to be a 'real'
         // property, but in large chunks of the spec it does act as such.
         // Therefore we simply expose it as a property.
         $propFind->handle('{' . self::NS_CALDAV . '}calendar-data', function () use($node) {
             $val = $node->get();
             if (is_resource($val)) {
                 $val = stream_get_contents($val);
             }
             // Taking out \r to not screw up the xml output
             return str_replace("\r", "", $val);
         });
     }
 }
Пример #2
0
 public function propFind(DAV\PropFind $propFind, DAV\INode $node)
 {
     // Add extra Windows properties to the node:
     if (method_exists($node, 'getIsHidden')) {
         $propFind->set('{DAV:}ishidden', $node->getIsHidden() ? '1' : '0');
     }
     if (method_exists($node, 'getIsReadonly')) {
         $propFind->set('{DAV:}isreadonly', $node->getIsReadonly() ? '1' : '0');
     }
     if (method_exists($node, 'getWin32Props')) {
         $propFind->set('{urn:schemas-microsoft-com:}Win32FileAttributes', $node->getWin32Props());
     }
 }
Пример #3
0
 /**
  * Fetches properties for a path.
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 public function propFind($path, PropFind $propFind)
 {
     try {
         $node = $this->tree->getNodeForPath($path);
         if (!$node instanceof Node) {
             return;
         }
     } catch (ServiceUnavailable $e) {
         // might happen for unavailable mount points, skip
         return;
     } catch (NotFound $e) {
         // in some rare (buggy) cases the node might not be found,
         // we catch the exception to prevent breaking the whole list with a 404
         // (soft fail)
         \OC::$server->getLogger()->warning('Could not get node for path: \\"' . $path . '\\" : ' . $e->getMessage(), array('app' => 'files'));
         return;
     }
     $requestedProps = $propFind->get404Properties();
     // these might appear
     $requestedProps = array_diff($requestedProps, $this->ignoredProperties);
     if (empty($requestedProps)) {
         return;
     }
     if ($node instanceof Directory && $propFind->getDepth() !== 0) {
         // note: pre-fetching only supported for depth <= 1
         $this->loadChildrenProperties($node, $requestedProps);
     }
     $props = $this->getProperties($node, $requestedProps);
     foreach ($props as $propName => $propValue) {
         $propFind->set($propName, $propValue);
     }
 }
Пример #4
0
 /**
  * Fetches properties for a path.
  *
  * This method received a PropFind object, which contains all the
  * information about the properties that need to be fetched.
  *
  * Ususually you would just want to call 'get404Properties' on this object,
  * as this will give you the _exact_ list of properties that need to be
  * fetched, and haven't yet.
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 public function propFind($path, PropFind $propFind)
 {
     $propertyNames = $propFind->get404Properties();
     if (!$propertyNames) {
         return;
     }
     // error_log("propFind: path($path), " . print_r($propertyNames, true));
     $cachedNodes = \CB\Cache::get('DAVNodes');
     // error_log("propFind: " . print_r($cachedNodes, true));
     $path = trim($path, '/');
     $path = str_replace('\\', '/', $path);
     // Node with $path is not in cached nodes, return
     if (!array_key_exists($path, $cachedNodes)) {
         return;
     }
     $node = $cachedNodes[$path];
     // while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
     //     $propFind->set($row['name'], $row['value']);
     // }
     foreach ($propertyNames as $prop) {
         if ($prop == '{DAV:}creationdate') {
             $dttm = new \DateTime($node['cdate']);
             // $dttm->getTimestamp()
             $propFind->set($prop, \Sabre\HTTP\Util::toHTTPDate($dttm));
         } elseif ($prop == '{urn:schemas-microsoft-com:office:office}modifiedby' or $prop == '{DAV:}getmodifiedby') {
             // This has to be revised, because the User.login differs from User.DisplayName
             // moreover, during an edit, Word will check for File Properties and we
             // tell Word that the file is modified by another user
             // $propFind->set($prop, \CB\User::getDisplayName($node['uid']));
         }
     }
 }
Пример #5
0
 function testPropFindNothingToDo()
 {
     $backend = $this->getBackend();
     $propFind = new PropFind('dir', ['{DAV:}displayname']);
     $propFind->set('{DAV:}displayname', 'foo');
     $backend->propFind('dir', $propFind);
     $this->assertEquals('foo', $propFind->get('{DAV:}displayname'));
 }
 function testDontTouchOtherMimeTypes()
 {
     $this->server->httpRequest = new HTTP\Request('GET', '/addressbooks/user1/book1/card1.vcf', ['User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1']);
     $propFind = new PropFind('hello', ['{DAV:}getcontenttype']);
     $propFind->set('{DAV:}getcontenttype', 'text/plain');
     $this->carddavPlugin->propFindLate($propFind, new \Sabre\DAV\SimpleCollection('foo'));
     $this->assertEquals('text/plain', $propFind->get('{DAV:}getcontenttype'));
 }
Пример #7
0
 public function testPropFind()
 {
     $propName = '{http://calendarserver.org/ns/}subscribed-strip-alarms';
     $propFind = new PropFind('foo', [$propName]);
     $propFind->set($propName, null, 200);
     $plugin = new Plugin();
     $plugin->propFind($propFind, new \Sabre\DAV\SimpleCollection('hi'));
     $this->assertFalse(is_null($propFind->get($propName)));
 }
Пример #8
0
 /**
  * Fetches properties for a path.
  *
  * This method received a PropFind object, which contains all the
  * information about the properties that need to be fetched.
  *
  * Ususually you would just want to call 'get404Properties' on this object,
  * as this will give you the _exact_ list of properties that need to be
  * fetched, and haven't yet.
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 function propFind($path, PropFind $propFind)
 {
     if (!isset($this->data[$path])) {
         return;
     }
     foreach ($this->data[$path] as $name => $value) {
         $propFind->set($name, $value);
     }
 }
Пример #9
0
 /**
  * Triggered after properties have been fetched.
  *
  * @param PropFind $propFind
  * @param INode $node
  * @return void
  */
 function propFind(PropFind $propFind, INode $node)
 {
     // There's a bunch of properties that must appear as a self-closing
     // xml-element. This event handler ensures that this will be the case.
     $props = ['{http://calendarserver.org/ns/}subscribed-strip-alarms', '{http://calendarserver.org/ns/}subscribed-strip-attachments', '{http://calendarserver.org/ns/}subscribed-strip-todos'];
     foreach ($props as $prop) {
         if ($propFind->getStatus($prop) === 200) {
             $propFind->set($prop, '', 200);
         }
     }
 }
Пример #10
0
 /**
  * Fetches properties for a path.
  *
  * This method received a PropFind object, which contains all the
  * information about the properties that need to be fetched.
  *
  * Ususually you would just want to call 'get404Properties' on this object,
  * as this will give you the _exact_ list of properties that need to be
  * fetched, and haven't yet.
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 function propFind($path, PropFind $propFind)
 {
     $propertyNames = $propFind->get404Properties();
     if (!$propertyNames) {
         return;
     }
     $query = 'SELECT name, value FROM propertystorage WHERE path = ?';
     $stmt = $this->pdo->prepare($query);
     $stmt->execute([$path]);
     while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
         $propFind->set($row['name'], $row['value']);
     }
 }
Пример #11
0
 /**
  * Fetches properties for a path.
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 public function propFind($path, PropFind $propFind)
 {
     $requestedProps = $propFind->get404Properties();
     // these might appear
     $requestedProps = array_diff($requestedProps, $this->ignoredProperties);
     if (empty($requestedProps)) {
         return;
     }
     $props = $this->getProperties($path, $requestedProps);
     foreach ($props as $propName => $propValue) {
         $propFind->set($propName, $propValue);
     }
 }
Пример #12
0
 /**
  * Fetches properties for a path.
  *
  * This method received a PropFind object, which contains all the
  * information about the properties that need to be fetched.
  *
  * Ususually you would just want to call 'get404Properties' on this object,
  * as this will give you the _exact_ list of properties that need to be
  * fetched, and haven't yet.
  *
  * However, you can also support the 'allprops' property here. In that
  * case, you should check for $propFind->isAllProps().
  *
  * @param string $path
  * @param PropFind $propFind
  * @return void
  */
 function propFind($path, PropFind $propFind)
 {
     if (!$propFind->isAllProps() && count($propFind->get404Properties()) === 0) {
         return;
     }
     $query = 'SELECT name, value, valuetype FROM propertystorage WHERE path = ?';
     $stmt = $this->pdo->prepare($query);
     $stmt->execute([$path]);
     while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
         switch ($row['valuetype']) {
             case null:
             case self::VT_STRING:
                 $propFind->set($row['name'], $row['value']);
                 break;
             case self::VT_XML:
                 $propFind->set($row['name'], new Complex($row['value']));
                 break;
             case self::VT_OBJECT:
                 $propFind->set($row['name'], unserialize($row['value']));
                 break;
         }
     }
 }
Пример #13
0
 /**
  * This event is triggered when fetching properties.
  *
  * This event is scheduled late in the process, after most work for
  * propfind has been done.
  */
 function propFindLate(DAV\PropFind $propFind, DAV\INode $node)
 {
     // If the request was made using the SOGO connector, we must rewrite
     // the content-type property. By default SabreDAV will send back
     // text/x-vcard; charset=utf-8, but for SOGO we must strip that last
     // part.
     if (strpos($this->server->httpRequest->getHeader('User-Agent'), 'Thunderbird') === false) {
         return;
     }
     $contentType = $propFind->get('{DAV:}getcontenttype');
     list($part) = explode(';', $contentType);
     if ($part === 'text/x-vcard' || $part === 'text/vcard') {
         $propFind->set('{DAV:}getcontenttype', 'text/x-vcard');
     }
 }
Пример #14
0
 /**
  * Fetches properties for a node.
  *
  * This event is called a bit later, so plugins have a chance first to
  * populate the result.
  *
  * @param PropFind $propFind
  * @param INode $node
  * @return void
  */
 function propFindNode(PropFind $propFind, INode $node)
 {
     if ($node instanceof IProperties && ($propertyNames = $propFind->get404Properties())) {
         $nodeProperties = $node->getProperties($propertyNames);
         foreach ($nodeProperties as $propertyName => $value) {
             $propFind->set($propertyName, $value, 200);
         }
     }
 }
Пример #15
0
 /**
  * Fetches properties for a node.
  *
  * This event is called a bit later, so plugins have a chance first to
  * populate the result.
  *
  * @param PropFind $propFind
  * @param INode $node
  * @return void
  */
 function propFindNode(PropFind $propFind, INode $node)
 {
     if ($node instanceof IProperties && ($propertyNames = $propFind->get404Properties())) {
         $nodeProperties = $node->getProperties($propertyNames);
         foreach ($propertyNames as $propertyName) {
             if (array_key_exists($propertyName, $nodeProperties)) {
                 $propFind->set($propertyName, $nodeProperties[$propertyName], 200);
             }
         }
     }
 }
Пример #16
0
 /**
  * Triggered before properties are looked up in specific nodes.
  *
  * @param DAV\PropFind $propFind
  * @param DAV\INode $node
  * @param array $requestedProperties
  * @param array $returnedProperties
  * @TODO really should be broken into multiple methods, or even a class.
  * @return bool
  */
 function propFind(DAV\PropFind $propFind, DAV\INode $node)
 {
     $path = $propFind->getPath();
     // Checking the read permission
     if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) {
         // User is not allowed to read properties
         // Returning false causes the property-fetching system to pretend
         // that the node does not exist, and will cause it to be hidden
         // from listings such as PROPFIND or the browser plugin.
         if ($this->hideNodesFromListings) {
             return false;
         }
         // Otherwise we simply mark every property as 403.
         foreach ($propFind->getRequestedProperties() as $requestedProperty) {
             $propFind->set($requestedProperty, null, 403);
         }
         return;
     }
     /* Adding principal properties */
     if ($node instanceof IPrincipal) {
         $propFind->handle('{DAV:}alternate-URI-set', function () use($node) {
             return new DAV\Property\HrefList($node->getAlternateUriSet());
         });
         $propFind->handle('{DAV:}principal-URL', function () use($node) {
             return new DAV\Property\Href($node->getPrincipalUrl() . '/');
         });
         $propFind->handle('{DAV:}group-member-set', function () use($node) {
             $members = $node->getGroupMemberSet();
             foreach ($members as $k => $member) {
                 $members[$k] = rtrim($member, '/') . '/';
             }
             return new DAV\Property\HrefList($members);
         });
         $propFind->handle('{DAV:}group-membership', function () use($node) {
             $members = $node->getGroupMembership();
             foreach ($members as $k => $member) {
                 $members[$k] = rtrim($member, '/') . '/';
             }
             return new DAV\Property\HrefList($members);
         });
         $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']);
     }
     $propFind->handle('{DAV:}principal-collection-set', function () {
         $val = $this->principalCollectionSet;
         // Ensuring all collections end with a slash
         foreach ($val as $k => $v) {
             $val[$k] = $v . '/';
         }
         return new DAV\Property\HrefList($val);
     });
     $propFind->handle('{DAV:}current-user-principal', function () {
         if ($url = $this->getCurrentUserPrincipal()) {
             return new Property\Principal(Property\Principal::HREF, $url . '/');
         } else {
             return new Property\Principal(Property\Principal::UNAUTHENTICATED);
         }
     });
     $propFind->handle('{DAV:}supported-privilege-set', function () use($node) {
         return new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node));
     });
     $propFind->handle('{DAV:}current-user-privilege-set', function () use($node, $propFind, $path) {
         if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
             $propFind->set('{DAV:}current-user-privilege-set', null, 403);
         } else {
             $val = $this->getCurrentUserPrivilegeSet($node);
             if (!is_null($val)) {
                 return new Property\CurrentUserPrivilegeSet($val);
             }
         }
     });
     $propFind->handle('{DAV:}acl', function () use($node, $propFind, $path) {
         /* The ACL property contains all the permissions */
         if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) {
             $propFind->set('{DAV:}acl', null, 403);
         } else {
             $acl = $this->getACL($node);
             if (!is_null($acl)) {
                 return new Property\Acl($this->getACL($node));
             }
         }
     });
     $propFind->handle('{DAV:}acl-restrictions', function () {
         return new Property\AclRestrictions();
     });
     /* Adding ACL properties */
     if ($node instanceof IACL) {
         $propFind->handle('{DAV:}owner', function () use($node) {
             return new DAV\Property\Href($node->getOwner() . '/');
         });
     }
 }
Пример #17
0
 /**
  * This method handler is invoked during fetching of properties.
  *
  * We use this event to add calendar-auto-schedule-specific properties.
  *
  * @param PropFind $propFind
  * @param INode $node
  * @return void
  */
 function propFind(PropFind $propFind, INode $node)
 {
     if ($node instanceof DAVACL\IPrincipal) {
         $caldavPlugin = $this->server->getPlugin('caldav');
         $principalUrl = $node->getPrincipalUrl();
         // schedule-outbox-URL property
         $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function () use($principalUrl, $caldavPlugin) {
             $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
             if (!$calendarHomePath) {
                 return null;
             }
             $outboxPath = $calendarHomePath . '/outbox/';
             return new Href($outboxPath);
         });
         // schedule-inbox-URL property
         $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function () use($principalUrl, $caldavPlugin) {
             $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
             if (!$calendarHomePath) {
                 return null;
             }
             $inboxPath = $calendarHomePath . '/inbox/';
             return new Href($inboxPath);
         });
         $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function () use($principalUrl, $caldavPlugin) {
             // We don't support customizing this property yet, so in the
             // meantime we just grab the first calendar in the home-set.
             $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
             if (!$calendarHomePath) {
                 return null;
             }
             $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set';
             $result = $this->server->getPropertiesForPath($calendarHomePath, ['{DAV:}resourcetype', $sccs], 1);
             foreach ($result as $child) {
                 if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar') || $child[200]['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared')) {
                     // Node is either not a calendar or a shared instance.
                     continue;
                 }
                 if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) {
                     // Either there is no supported-calendar-component-set
                     // (which is fine) or we found one that supports VEVENT.
                     return new Href($child['href']);
                 }
             }
         });
         // The server currently reports every principal to be of type
         // 'INDIVIDUAL'
         $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function () {
             return 'INDIVIDUAL';
         });
     }
     // Mapping the old property to the new property.
     $propFind->handle('{http://calendarserver.org/ns/}calendar-availability', function () use($propFind, $node) {
         // In case it wasn't clear, the only difference is that we map the
         // old property to a different namespace.
         $availProp = '{' . self::NS_CALDAV . '}calendar-availability';
         $subPropFind = new PropFind($propFind->getPath(), [$availProp]);
         $this->server->getPropertiesByNode($subPropFind, $node);
         $propFind->set('{http://calendarserver.org/ns/}calendar-availability', $subPropFind->get($availProp), $subPropFind->getStatus($availProp));
     });
 }