/**
  * (non-PHPdoc)
  * @see Sabre\DAV\Collection::getChild()
  */
 public function getChild($_name)
 {
     $eventId = $_name instanceof Tinebase_Record_Interface ? $_name->getId() : $this->_getIdFromName($_name);
     // check if child exists in calendarQuery cache
     if ($this->_calendarQueryCache && isset($this->_calendarQueryCache[$eventId])) {
         $child = $this->_calendarQueryCache[$eventId];
         // remove entries from cache / they will not be used anymore
         unset($this->_calendarQueryCache[$eventId]);
         if (empty($this->_calendarQueryCache)) {
             $this->_calendarQueryCache = null;
         }
         return $child;
     }
     $modelName = $this->_application->name . '_Model_' . $this->_model;
     if ($_name instanceof $modelName) {
         $object = $_name;
     } else {
         $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter';
         $filter = new $filterClass(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_container->getId()), array('condition' => 'OR', 'filters' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $eventId), array('field' => 'uid', 'operator' => 'equals', 'value' => $eventId)))));
         $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord();
         if ($object == null) {
             throw new Sabre\DAV\Exception\NotFound('Object not found');
         }
     }
     $httpRequest = new Sabre\HTTP\Request();
     // lie about existence of event of request is a PUT request from an ATTENDEE for an already existing event
     // to prevent ugly (and not helpful) error messages on the client
     if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') {
         if ($object->organizer != Tinebase_Core::getUser()->contact_id && Calendar_Model_Attender::getOwnAttender($object->attendee) !== null) {
             throw new Sabre\DAV\Exception\NotFound('Object not found');
         }
     }
     $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model;
     return new $objectClass($this->_container, $object);
 }
예제 #2
0
    /**
     * Returns the HTTP Prefer header information.
     *
     * The prefer header is defined in:
     * http://tools.ietf.org/html/draft-snell-http-prefer-14
     *
     * This method will return an array with options.
     *
     * Currently, the following options may be returned:
     *  [
     *      'return-asynch'         => true,
     *      'return-minimal'        => true,
     *      'return-representation' => true,
     *      'wait'                  => 30,
     *      'strict'                => true,
     *      'lenient'               => true,
     *  ]
     *
     * This method also supports the Brief header, and will also return
     * 'return-minimal' if the brief header was set to 't'.
     *
     * For the boolean options, false will be returned if the headers are not
     * specified. For the integer options it will be 'null'.
     *
     * @return array
     */
    function getHTTPPrefer() {

        $result = [
            // can be true or false
            'respond-async' => false,
            // Could be set to 'representation' or 'minimal'.
            'return'        => null,
            // Used as a timeout, is usually a number.
            'wait'          => null,
            // can be 'strict' or 'lenient'.
            'handling'      => false,
        ];

        if ($prefer = $this->httpRequest->getHeader('Prefer')) {

            $result = array_merge(
                $result,
                \Sabre\HTTP\parsePrefer($prefer)
            );

        } elseif ($this->httpRequest->getHeader('Brief') == 't') {
            $result['return'] = 'minimal';
        }

        return $result;

    }
예제 #3
0
 /**
  * Returns the HTTP Prefer header information.
  *
  * The prefer header is defined in:
  * http://tools.ietf.org/html/draft-snell-http-prefer-14
  *
  * This method will return an array with options.
  *
  * Currently, the following options may be returned:
  *  [
  *      'return-asynch'         => true,
  *      'return-minimal'        => true,
  *      'return-representation' => true,
  *      'wait'                  => 30,
  *      'strict'                => true,
  *      'lenient'               => true,
  *  ]
  *
  * This method also supports the Brief header, and will also return
  * 'return-minimal' if the brief header was set to 't'.
  *
  * For the boolean options, false will be returned if the headers are not
  * specified. For the integer options it will be 'null'.
  *
  * @return array
  */
 function getHTTPPrefer()
 {
     $result = ['return-asynch' => false, 'return-minimal' => false, 'return-representation' => false, 'wait' => null, 'strict' => false, 'lenient' => false];
     if ($prefer = $this->httpRequest->getHeader('Prefer')) {
         $parameters = array_map('trim', explode(',', $prefer));
         foreach ($parameters as $parameter) {
             // Right now our regex only supports the tokens actually
             // specified in the draft. We may need to expand this if new
             // tokens get registered.
             if (!preg_match('/^(?P<token>[a-z0-9-]+)(?:=(?P<value>[0-9]+))?$/', $parameter, $matches)) {
                 continue;
             }
             switch ($matches['token']) {
                 case 'return-asynch':
                 case 'return-minimal':
                 case 'return-representation':
                 case 'strict':
                 case 'lenient':
                     $result[$matches['token']] = true;
                     break;
                 case 'wait':
                     $result[$matches['token']] = $matches['value'];
                     break;
             }
         }
     } elseif ($this->httpRequest->getHeader('Brief') == 't') {
         $result['return-minimal'] = true;
     }
     return $result;
 }
예제 #4
0
 /**
  * Returns the HTTP Prefer header information.
  *
  * The prefer header is defined in:
  * http://tools.ietf.org/html/draft-snell-http-prefer-14
  *
  * This method will return an array with options.
  *
  * Currently, the following options may be returned:
  *  [
  *      'return-asynch'         => true,
  *      'return-minimal'        => true,
  *      'return-representation' => true,
  *      'wait'                  => 30,
  *      'strict'                => true,
  *      'lenient'               => true,
  *  ]
  *
  * This method also supports the Brief header, and will also return
  * 'return-minimal' if the brief header was set to 't'.
  *
  * For the boolean options, false will be returned if the headers are not
  * specified. For the integer options it will be 'null'.
  *
  * @return array
  */
 function getHTTPPrefer()
 {
     $result = ['respond-async' => false, 'return' => null, 'wait' => null, 'handling' => false];
     if ($prefer = $this->httpRequest->getHeader('Prefer')) {
         $result = array_merge($result, \Sabre\HTTP\parsePrefer($prefer));
     } elseif ($this->httpRequest->getHeader('Brief') == 't') {
         $result['return'] = 'minimal';
     }
     return $result;
 }
 /**
  *
  */
 public function process()
 {
     $this->emit('process:before', [['request' => $this->httpRequest]]);
     // set Content Security Policy and CORS headers
     $this->httpResponse->addHeader('Content-Security-Policy', "default-src *");
     $this->httpResponse->addHeader('X-Content-Security-Policy', "default-src *");
     if ($this->httpRequest->hasHeader('Origin')) {
         // TODO: allow to configure allowed origins
         $this->httpResponse->addHeader('Access-Control-Allow-Origin', "*");
     }
     // FIXME: respond to OPTIONS requests directly and without validation
     if ($this->httpRequest->getMethod() == 'OPTIONS') {
         $this->httpResponse->addHeader('Access-Control-Request-Method', 'GET, POST, OPTIONS');
         $this->httpResponse->addHeader('Access-Control-Allow-Headers', $this->httpRequest->getHeader('Access-Control-Request-Headers'));
         $this->httpResponse->setStatus(204);
         $this->sapi->sendResponse($this->httpResponse);
         return;
     }
     // extract route from request (jmap, auth|.well-known/jmap, upload)
     if ($route = $this->getRouteMatch($this->httpRequest->getPath())) {
         try {
             call_user_func($this->routes[$route], $this->httpRequest, $this->httpResponse);
         } catch (\RuntimeException $e) {
             if ($e instanceof Exception\ProcessorException) {
                 $this->httpResponse->setStatus($e->getStatusCode());
             } else {
                 $this->httpResponse->setStatus(500);
             }
             $this->logger->err(strval($e));
             $this->emit('process:error', [['request' => $this->httpRequest, 'exception' => $e]]);
         }
     } else {
         // TODO: throw invalid route error
         $this->httpResponse->setStatus(404);
     }
     $this->emit('process:after', [['response' => $this->httpResponse]]);
     $this->sapi->sendResponse($this->httpResponse);
 }
예제 #6
0
 /**
  * This method checks the main HTTP preconditions.
  *
  * Currently these are:
  *   * If-Match
  *   * If-None-Match
  *   * If-Modified-Since
  *   * If-Unmodified-Since
  *
  * The method will return true if all preconditions are met
  * The method will return false, or throw an exception if preconditions
  * failed. If false is returned the operation should be aborted, and
  * the appropriate HTTP response headers are already set.
  *
  * Normally this method will throw 412 Precondition Failed for failures
  * related to If-None-Match, If-Match and If-Unmodified Since. It will
  * set the status to 304 Not Modified for If-Modified_since.
  *
  * If the $handleAsGET argument is set to true, it will also return 304
  * Not Modified for failure of the If-None-Match precondition. This is the
  * desired behaviour for HTTP GET and HTTP HEAD requests.
  *
  * @param bool $handleAsGET
  * @return bool
  */
 public function checkPreconditions($handleAsGET = false)
 {
     $uri = $this->getRequestUri();
     $node = null;
     $lastMod = null;
     $etag = null;
     if ($ifMatch = $this->httpRequest->getHeader('If-Match')) {
         // If-Match contains an entity tag. Only if the entity-tag
         // matches we are allowed to make the request succeed.
         // If the entity-tag is '*' we are only allowed to make the
         // request succeed if a resource exists at that url.
         try {
             $node = $this->tree->getNodeForPath($uri);
         } catch (Exception\NotFound $e) {
             throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist', 'If-Match');
         }
         // Only need to check entity tags if they are not *
         if ($ifMatch !== '*') {
             // There can be multiple etags
             $ifMatch = explode(',', $ifMatch);
             $haveMatch = false;
             foreach ($ifMatch as $ifMatchItem) {
                 // Stripping any extra spaces
                 $ifMatchItem = trim($ifMatchItem, ' ');
                 $etag = $node->getETag();
                 if ($etag === $ifMatchItem) {
                     $haveMatch = true;
                 } else {
                     // Evolution has a bug where it sometimes prepends the "
                     // with a \. This is our workaround.
                     if (str_replace('\\"', '"', $ifMatchItem) === $etag) {
                         $haveMatch = true;
                     }
                 }
             }
             if (!$haveMatch) {
                 throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.', 'If-Match');
             }
         }
     }
     if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) {
         // The If-None-Match header contains an etag.
         // Only if the ETag does not match the current ETag, the request will succeed
         // The header can also contain *, in which case the request
         // will only succeed if the entity does not exist at all.
         $nodeExists = true;
         if (!$node) {
             try {
                 $node = $this->tree->getNodeForPath($uri);
             } catch (Exception\NotFound $e) {
                 $nodeExists = false;
             }
         }
         if ($nodeExists) {
             $haveMatch = false;
             if ($ifNoneMatch === '*') {
                 $haveMatch = true;
             } else {
                 // There might be multiple etags
                 $ifNoneMatch = explode(',', $ifNoneMatch);
                 $etag = $node->getETag();
                 foreach ($ifNoneMatch as $ifNoneMatchItem) {
                     // Stripping any extra spaces
                     $ifNoneMatchItem = trim($ifNoneMatchItem, ' ');
                     if ($etag === $ifNoneMatchItem) {
                         $haveMatch = true;
                     }
                 }
             }
             if ($haveMatch) {
                 if ($handleAsGET) {
                     $this->httpResponse->sendStatus(304);
                     return false;
                 } else {
                     throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).', 'If-None-Match');
                 }
             }
         }
     }
     if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) {
         // The If-Modified-Since header contains a date. We
         // will only return the entity if it has been changed since
         // that date. If it hasn't been changed, we return a 304
         // header
         // Note that this header only has to be checked if there was no If-None-Match header
         // as per the HTTP spec.
         $date = HTTP\Util::parseHTTPDate($ifModifiedSince);
         if ($date) {
             if (is_null($node)) {
                 $node = $this->tree->getNodeForPath($uri);
             }
             $lastMod = $node->getLastModified();
             if ($lastMod) {
                 $lastMod = new \DateTime('@' . $lastMod);
                 if ($lastMod <= $date) {
                     $this->httpResponse->sendStatus(304);
                     $this->httpResponse->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod));
                     return false;
                 }
             }
         }
     }
     if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) {
         // The If-Unmodified-Since will allow allow the request if the
         // entity has not changed since the specified date.
         $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince);
         // We must only check the date if it's valid
         if ($date) {
             if (is_null($node)) {
                 $node = $this->tree->getNodeForPath($uri);
             }
             $lastMod = $node->getLastModified();
             if ($lastMod) {
                 $lastMod = new \DateTime('@' . $lastMod);
                 if ($lastMod > $date) {
                     throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.', 'If-Unmodified-Since');
                 }
             }
         }
     }
     return true;
 }
예제 #7
0
 function testGetNonExistantHeader()
 {
     $this->assertNull($this->request->getHeader('doesntexist'));
     $this->assertNull($this->request->getHeader('Content-Length'));
 }