Example #1
0
 /**
  * Handles a POST request
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 public function handle($resource)
 {
     $resource->assertLock();
     $headers = array();
     try {
         ob_start();
         $entity = $resource->method_POST($headers);
     } catch (DAV_Status $e) {
         ob_end_clean();
         throw $e;
     }
     if ($length = ob_get_length()) {
         $headers['Content-Length'] = $length;
         DAV::header($headers);
         ob_end_flush();
         return;
     } else {
         ob_end_clean();
     }
     if (is_string($entity)) {
         $headers['Content-Length'] = strlen($entity);
         DAV::header($headers);
         echo $entity;
         return;
     }
     DAV::header($headers);
 }
 /**
  * Returns the HTTP 405 Method Not Allowed status code
  * 
  * This will only be called when an unknown/unsupported HTTP method is used. So
  * We'll return the correct status code and explain which methods are allowed.
  * 
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 public function handle($resource)
 {
     $allow = implode(', ', self::$ALLOWED_METHODS);
     DAV::header("Allow: {$allow}");
     $status = new DAV_Status(DAV::HTTP_METHOD_NOT_ALLOWED, "Allowed methods: {$allow}");
     $status->output();
 }
 /**
  * Handles the UNLOCK request
  * 
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     if (!DAV::$LOCKPROVIDER) {
         throw new DAV_Status(DAV::HTTP_FORBIDDEN);
     }
     $lock = DAV::$LOCKPROVIDER->getlock(DAV::getPath());
     if (!$lock || $this->locktoken !== $lock->locktoken) {
         throw new DAV_Status(DAV::HTTP_CONFLICT, DAV::COND_LOCK_TOKEN_MATCHES_REQUEST_URI);
     }
     DAV::$LOCKPROVIDER->unlock($lock->lockroot);
     DAV::header(array('status' => DAV::HTTP_NO_CONTENT));
 }
 /**
  * Checks that the Depth header is correct and then handles a DELETE request
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     if (DAV::DEPTH_INF !== $this->depth()) {
         throw new DAV_Status(DAV::HTTP_BAD_REQUEST, 'Only Depth: infinity is allowed for DELETE requests.');
     }
     self::delete($resource);
     if (DAV_Multistatus::active()) {
         DAV_Multistatus::inst()->close();
     } else {
         DAV::header(array('status' => DAV::HTTP_NO_CONTENT));
     }
 }
 /**
  * Handles the OPTIONS request
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     $headers = array('DAV' => array('1' . (DAV::$LOCKPROVIDER ? ', 2' : '') . ', 3', 'access-control', '<http://apache.org/dav/propset/fs/1>'), 'MS-Author-Via' => 'DAV', 'Allow' => implode(', ', self::$ALLOWED_METHODS), 'Content-Length' => 0);
     if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
         $headers['Access-Control-Allow-Methods'] = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'];
     }
     if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
         $headers['Access-Control-Allow-Headers'] = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'];
     }
     if ($resource instanceof DAV_Resource) {
         DAV::header($resource->method_OPTIONS($headers));
     } else {
         DAV::header($headers);
     }
 }
 /**
  * This should be a identical copy of DAV_Multistatus::__construct()
  */
 private function __construct()
 {
     DAV::header(array('Content-Type' => 'application/xml; charset="utf-8"', 'status' => DAV::HTTP_MULTI_STATUS));
     echo DAV::xml_header() . '<D:multistatus xmlns:D="DAV:">';
 }
Example #7
0
 public function testHeader()
 {
     ob_start();
     DAV::header(array('status' => DAV::HTTP_EXPECTATION_FAILED, 'x-test-header' => 'with a test value'));
     $returnedValue = ob_get_contents();
     ob_end_clean();
     $this->assertSame("x-test-header: with a test value\nHTTP/1.1 417 Expectation Failed\n", $returnedValue, 'DAV::header() should print the correct headers (print them, not sent them, because we\'re in testing mode');
 }
Example #8
0
 /**
  * Handle the HEAD request
  * 
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     $headers = self::common($resource);
     DAV::header($headers);
     return;
 }
Example #9
0
 /**
  * Serve WebDAV HTTP request.
  * @param DAV_Registry $registry
  */
 public function handleRequest()
 {
     // We want to catch every exception thrown in this code, and report about it
     // to the user appropriately.
     $shallow_lock = false;
     try {
         $shallow_lock = $this->check_if_headers();
         $resource = DAV::$REGISTRY->resource(DAV::getPath());
         if (!$resource || !$resource->isVisible() and in_array($_SERVER['REQUEST_METHOD'], array('ACL', 'COPY', 'DELETE', 'GET', 'HEAD', 'MOVE', 'OPTIONS', 'POST', 'PROPFIND', 'PROPPATCH', 'REPORT', 'UNLOCK'))) {
             throw new DAV_Status(DAV::HTTP_NOT_FOUND);
         }
         if ('/' !== substr(DAV::getPath(), -1) && ($resource && $resource instanceof DAV_Collection || 'MKCOL' === $_SERVER['REQUEST_METHOD'])) {
             $newPath = DAV::getPath() . '/';
             DAV::setPath(DAV::encodeURIFullPath($newPath));
             DAV::header(array('Content-Location' => DAV::path2uri($newPath)));
         }
         $this->handle($resource);
     } catch (Exception $e) {
         if (!$e instanceof DAV_Status) {
             $e = new DAV_Status(DAV::HTTP_INTERNAL_SERVER_ERROR, "{$e}");
         }
         $e->output();
     }
     if ($shallow_lock) {
         DAV::$REGISTRY->shallowUnlock();
     }
     if (DAV_Multistatus::active()) {
         DAV_Multistatus::inst()->close();
     }
 }
Example #10
0
 /**
  * Shows a decent HTML page with an error for the end-user
  * @param   type  $message  The message to show. Could contain HTML.
  * @param   type  $status   The HTTP status code to return
  * @return  void
  */
 public static function htmlError($message, $status = DAV::HTTP_OK)
 {
     DAV::header(array('status' => $status));
     require 'views/' . 'html_error.php';
     exit;
 }
Example #11
0
 /**
  * Checks and handles the GET request
  *
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     $headers = self::common($resource);
     try {
         ob_start();
         $entity = $resource->method_GET();
     } catch (DAV_Status $e) {
         ob_end_clean();
         throw $e;
     }
     if ($length = ob_get_length()) {
         $headers['Content-Length'] = $length;
         DAV::header($headers);
         ob_end_flush();
         return;
     } else {
         ob_end_clean();
     }
     if (!is_resource($entity)) {
         $entity = "{$entity}";
         $headers['Content-Length'] = strlen($entity);
         DAV::header($headers);
         echo "{$entity}";
         return;
     }
     // GET handler returned a stream
     // Try to find out the length of the total entity:
     if (isset($headers['Content-Length'])) {
         $entity_length = (int) $headers['Content-Length'];
     } else {
         $stat = @fstat($entity);
         $entity_length = !is_array($stat) || !isset($stat['size']) ? '*' : $stat['size'];
     }
     // process Range: header if present
     $ranges = self::range_header($entity_length);
     if (isset($headers['status']) && substr($header['status'], 0, 3) !== '200' || empty($ranges)) {
         // No byte ranges, or unexpected status code.
         // We just relay everything as-is.
         DAV::header($headers);
         $time = time() + 60;
         while (!feof($entity)) {
             if (time() > $time) {
                 set_time_limit(120);
                 $time = time() + 60;
             }
             echo fread($entity, DAV::$CHUNK_SIZE);
         }
         fclose($entity);
         return;
     }
     // One or more Ranges!
     $headers['status'] = DAV::HTTP_PARTIAL_CONTENT;
     if (1 === count($ranges)) {
         $range = $ranges[0];
         $content_length = $range['end'] - $range['start'] + 1;
         $headers['Content-Length'] = $content_length;
         $headers['Content-Range'] = "bytes {$range['start']}-{$range['end']}/{$entity_length}";
         DAV::header($headers);
         if (0 !== fseek($entity, $range['start'], SEEK_SET)) {
             // The stream is not seekable
             $size = $range['start'];
             while ($size && !feof($entity)) {
                 $buffer = fread($entity, $size < DAV::$CHUNK_SIZE ? $size : DAV::$CHUNK_SIZE);
                 $size -= strlen($buffer);
             }
         }
         // fpassthru() seems to be buggy when the stream handle points to the start (byte 0) of a stream, so let's not use it (but do some testing some place else)
         // You can uncomment this if statement and test if it works by applying the Range header to a GET request for a file larger than 2 or 2.5 GB: Range: bytes=0-
         //    if ( $entity_length === $range['end'] + 1 ) {
         //      fpassthru($entity);
         //    }else{
         $time = time() + 60;
         $size = $content_length;
         while ($size && !feof($entity)) {
             $buffer = fread($entity, $size < DAV::$CHUNK_SIZE ? $size : DAV::$CHUNK_SIZE);
             $size -= strlen($buffer);
             echo $buffer;
             if (time() > $time) {
                 set_time_limit(120);
                 $time = time() + 60;
             }
         }
         if ($size) {
             trigger_error(var_export(debug_backtrace(), true), E_USER_WARNING);
         }
         //    }
         fclose($entity);
         return;
     }
     // Multiple ranges!
     $multipart_separator = 'SDisk_' . strtr(microtime(), '. ', '__');
     // Remove all Content-* headers from the HTTP response headers.
     // They are moved to the body parts.
     $partheaders = array();
     foreach (array_keys($headers) as $header) {
         if (substr(strtolower($header), 0, 8) === 'content-') {
             $partheaders[$header] = $headers[$header];
             unset($headers[$header]);
         }
     }
     $headers['Content-Type'] = "multipart/byteranges; boundary={$multipart_separator}";
     DAV::header($headers);
     echo "This is a message in multipart MIME format.\r\n";
     $current_position = 0;
     foreach ($ranges as $range) {
         if (0 === fseek($entity, $range['start'], SEEK_SET)) {
             $current_position = $range['start'];
         } elseif ($range['start'] >= $current_position) {
             $skip = $range['start'] - $current_position;
             while ($skip && !feof($entity)) {
                 $buffer = fread($entity, $skip < DAV::$CHUNK_SIZE ? $skip : DAV::$CHUNK_SIZE);
                 $skip -= strlen($buffer);
                 echo $buffer;
             }
             $current_position = $range['start'] - $skip;
             if ($skip) {
                 $current_position = $range['start'] - $skip;
                 trigger_error(var_export(debug_backtrace(), true), E_USER_WARNING);
                 continue;
             }
         } else {
             trigger_error(var_export(debug_backtrace(), true), E_USER_WARNING);
             continue;
         }
         echo "\r\n--{$multipart_separator}\r\n";
         $partheaders['Content-Range'] = "{$range['start']}-{$range['end']}/{$entity_length}";
         $partheaders['Content-Length'] = $range['end'] - $range['start'] + 1;
         foreach ($partheaders as $header => $value) {
             echo "{$header}: {$value}\r\n";
         }
         echo "\r\n";
         if ($entity_length === $range['end'] + 1) {
             fpassthru($entity);
         } else {
             $size = $range['end'] - $range['start'] + 1;
             while ($size && !feof($entity)) {
                 $buffer = fread($entity, $size < DAV::$CHUNK_SIZE ? $size : DAV::$CHUNK_SIZE);
                 $size -= strlen($buffer);
                 echo $buffer;
             }
             $current_position = $range['end'] + 1 - $size;
             if ($size) {
                 trigger_error(var_export(debug_backtrace(), true), E_USER_WARNING);
             }
         }
     }
     echo "\r\n--{$multipart_separator}--\r\n";
     fclose($entity);
 }
Example #12
0
 /**
  * This method is called when DAV receives an 401 Unauthenticated exception.
  * @return bool true if a response has been sent to the user.
  */
 public function unauthorized()
 {
     DAV::header(array('WWW-Authenticate' => 'Basic realm="' . BeeHub::$CONFIG['authentication']['realm'] . '"', 'Content-Type' => BeeHub::best_xhtml_type()));
     BeeHub::htmlError(file_get_contents(dirname(dirname(__FILE__)) . '/views/error_unauthorized.html'), DAV::HTTP_UNAUTHORIZED);
 }
Example #13
0
 /**
  * Refreshes an already existing lock
  * 
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Statuss
  */
 private function handleRefreshLock($resource)
 {
     $if_header = $this->if_header;
     if (!isset($if_header[DAV::getPath()]) || !$if_header[DAV::getPath()]['lock']) {
         throw new DAV_Status(DAV::HTTP_BAD_REQUEST, array(DAV::COND_LOCK_TOKEN_SUBMITTED => new DAV_Element_href(DAV::getPath())));
     }
     // I think this can never evaluate to true, because DAV_Request already checks
     // whether the 'If' header matches the lock token of the resource. So if the
     // resource doesn't have a lock, this is already detected before this method
     // is called! (However, I don't dare to delete this yet and it doesn't hurt to
     // keep it)
     if (!($lock = DAV::$LOCKPROVIDER->getlock(DAV::getPath()))) {
         throw new DAV_Status(DAV::HTTP_PRECONDITION_FAILED, array(DAV::COND_LOCK_TOKEN_MATCHES_REQUEST_URI));
     }
     DAV::$LOCKPROVIDER->refresh($lock->lockroot, $lock->locktoken, $this->timeout);
     if (!($lockdiscovery = $resource->prop_lockdiscovery())) {
         throw new DAV_Status(DAV::HTTP_INTERNAL_SERVER_ERROR);
     }
     // Generate output:
     DAV::header('application/xml; charset="utf-8"');
     echo DAV::xml_header() . '<D:prop xmlns:D="DAV:"><D:lockdiscovery>' . $lockdiscovery . '</D:lockdiscovery></D:prop>';
 }
Example #14
0
 /**
  * Handles the PUT request
  *
  * This method checks whether the PUT request is valid and, if so, writes the
  * request body to the resource.
  *
  * @param DAV_Resource $resource
  */
 protected function handle($resource)
 {
     # This variable also flags if a new resource was created.
     $parent = null;
     if (!$resource) {
         if (!is_null($this->range_start)) {
             throw new DAV_Status(DAV::HTTP_NOT_FOUND);
         }
         $parent = DAV::$REGISTRY->resource(dirname(DAV::getPath()));
         if (!$parent || !$parent instanceof DAV_Collection) {
             throw new DAV_Status(DAV::HTTP_CONFLICT, 'Unable to PUT file in non-existing collection.');
         }
         $parent->assertLock();
         $parent->create_member(basename(DAV::getPath()));
         $resource = DAV::$REGISTRY->resource(DAV::getPath());
     } elseif ($resource instanceof DAV_Collection) {
         throw new DAV_Status(DAV::HTTP_METHOD_NOT_ALLOWED, 'Method PUT not supported on collections.');
     } else {
         $resource->assertLock();
     }
     if (is_null($this->range_start)) {
         try {
             if (isset($_SERVER['CONTENT_TYPE']) && 'application/octet-stream' !== $_SERVER['CONTENT_TYPE']) {
                 $resource->set_getcontenttype($_SERVER['CONTENT_TYPE']);
             } else {
                 $resource->set_getcontenttype(null);
             }
         } catch (DAV_Status $e) {
         }
         if (isset($_SERVER['HTTP_CONTENT_LANGUAGE'])) {
             try {
                 $resource->set_getcontentlanguage($_SERVER['HTTP_CONTENT_LANGUAGE']);
             } catch (DAV_Status $e) {
             }
         }
         $resource->storeProperties();
         $input = fopen('php://input', 'r');
         try {
             $resource->method_PUT($input);
             fclose($input);
         } catch (DAV_Status $e) {
             fclose($input);
             if ($parent) {
                 $parent->method_DELETE(basename(DAV::getPath()));
             }
             throw $e;
         }
     } else {
         $cl = $resource->user_prop_getcontentlength();
         if (!is_null($cl) && ($this->range_start > $cl or !is_null($this->range_total) && $this->range_total !== $cl)) {
             throw new DAV_Status(DAV::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
         }
         $input = fopen('php://input', 'r');
         try {
             $resource->method_PUT_range($input, $this->range_start, $this->range_end, $this->range_total);
             fclose($input);
         } catch (DAV_Status $e) {
             fclose($input);
             throw $e;
         }
     }
     if ($etag = $resource->prop_getetag()) {
         header('ETag: ' . htmlspecialchars_decode($etag));
     }
     if ($parent) {
         DAV::redirect(DAV::HTTP_CREATED, DAV::getPath());
     } else {
         DAV::header(array('status' => DAV::HTTP_NO_CONTENT));
     }
 }
Example #15
0
 /**
  * Sends this status to client.
  * @return void
  */
 public function output()
 {
     $status = $this->getCode();
     if ($status < 300) {
         throw new DAV_Status(DAV::HTTP_INTERNAL_SERVER_ERROR, "DAV_Status object with status {$status} " . var_export($this->getMessage(), true));
     }
     if (DAV::HTTP_UNAUTHORIZED === $status && DAV::$UNAUTHORIZED) {
         call_user_func(DAV::$UNAUTHORIZED);
         return;
     } elseif (!empty($this->conditions)) {
         $headers = array('status' => $status, 'Content-Type' => 'application/xml; charset="UTF-8"');
         if ($this->location) {
             $headers['Location'] = DAV::encodeURIFullPath($this->location);
         }
         DAV::header($headers);
         echo DAV::xml_header() . '<D:error xmlns:D="DAV:">';
         foreach ($this->conditions as $condition => $xml) {
             echo "\n<D:" . $condition;
             echo $xml ? ">{$xml}</D:{$condition}>" : "/>";
         }
         echo "\n</D:error>";
     } elseif ($this->location) {
         DAV::redirect($status, $this->location);
     } else {
         if (self::$RESPONSE_GENERATOR && in_array($_SERVER['REQUEST_METHOD'], array('GET', 'POST'))) {
             DAV::header(array('status' => $status));
             call_user_func(self::$RESPONSE_GENERATOR, $status, $this->getMessage());
         } else {
             DAV::header(array('status' => $status, 'Content-Type' => 'text/plain; charset="UTF-8"'));
             echo "HTTP/1.1 " . DAV::status_code($status) . "\n" . $this->getMessage();
         }
     }
 }
Example #16
0
 /**
  * Determines whether the copy request is valid and if so, copies the resources
  * 
  * @param DAV_Resource $resource
  * @return void
  * @throws DAV_Status
  */
 protected function handle($resource)
 {
     $destination = $this->destination();
     if ($resource instanceof DAV_Collection) {
         $destination = DAV::slashify($destination);
     } else {
         // The next line is here to make the litmus test succeed. The author of
         // litmus had eir own doubts wether this is actually desirable behaviour,
         // but chose to require this behaviour anyway:
         $destination = DAV::unslashify($destination);
     }
     // Can't move the root collection:
     if ($this instanceof DAV_Request_MOVE && '/' === DAV::getPath()) {
         throw new DAV_Status(DAV::HTTP_FORBIDDEN);
     }
     // Assert proper Depth: header value:
     if (DAV::DEPTH_1 === $this->depth() or $this instanceof DAV_Request_MOVE && DAV::DEPTH_INF !== $this->depth()) {
         throw new DAV_Status(DAV::HTTP_BAD_REQUEST, 'Illegal value for Depth: header.');
     }
     // Check: Can't move a collection to one of its members.
     if ($this instanceof DAV_Request_MOVE && '/' === substr(DAV::getPath(), -1) && 0 === strpos($destination, DAV::getPath())) {
         throw new DAV_Status(DAV::HTTP_FORBIDDEN, "Can't move a collection to itself or one of its members.");
     }
     $resourceCollection = $resource->collection();
     if ($this instanceof DAV_Request_MOVE) {
         $resourceCollection->assertLock();
         $resource->assertLock();
         $resource->assertMemberLocks();
     }
     if ('/' !== $destination[0]) {
         // Copy to an external URI?
         $isCreated = $resource->method_COPY_external($destination, $this->overwrite());
         if ($this instanceof DAV_Request_MOVE && !DAV_Multistatus::active()) {
             DAV_Request_DELETE::delete($resource);
         }
         if (DAV_Multistatus::active()) {
             DAV_Multistatus::inst()->close();
         } elseif ($isCreated) {
             DAV::redirect(DAV::HTTP_CREATED, $destination);
         } else {
             DAV::header(array('status' => DAV::HTTP_NO_CONTENT));
         }
         return;
     }
     // Check: Won't move a resource to one of its parents.
     if (0 === strpos(DAV::slashify(DAV::getPath()), DAV::slashify($destination))) {
         throw new DAV_Status(DAV::HTTP_NOT_IMPLEMENTED, "Won't move or copy a resource to one of its parents.");
     }
     $destinationResource = DAV::$REGISTRY->resource($destination);
     $destinationCollection = DAV::$REGISTRY->resource(dirname($destination));
     if (!$destinationCollection) {
         throw new DAV_Status(DAV::HTTP_CONFLICT, 'Unable to COPY to unexisting destination collection');
     }
     if ($destinationResource) {
         if (!$this->overwrite()) {
             throw new DAV_Status(DAV::HTTP_PRECONDITION_FAILED);
         } else {
             $destinationResource->assertLock();
         }
     } else {
         $destinationCollection->assertLock();
     }
     if ($this instanceof DAV_Request_MOVE) {
         if (DAV::$LOCKPROVIDER) {
             foreach (DAV::$LOCKPROVIDER->memberLocks(DAV::getPath()) as $lock) {
                 DAV::$LOCKPROVIDER->unlock($lock->lockroot);
             }
             if ($lock = DAV::$LOCKPROVIDER->getlock(DAV::getPath())) {
                 DAV::$LOCKPROVIDER->unlock($lock->lockroot);
             }
         }
         $resourceCollection->method_MOVE(basename($resource->path), $destination);
     } else {
         $this->copy_recursively($resource, $destination);
     }
     #<<<<<<<<
     #// This version always returns a 207 Multistatus wrapper:
     #if (!DAV_Multistatus::active())
     #  if ( $destinationResource )
     #    DAV_Multistatus::inst()->addStatus(
     #      $resource->path,
     #      new DAV_Status( DAV::HTTP_NO_CONTENT )
     #    );
     #  else
     #    DAV_Multistatus::inst()->addStatus(
     #      $resource->path,
     #      new DAV_Status(
     #        DAV::HTTP_CREATED, DAV::path2uri($destination)
     #      )
     #    );
     #DAV_Multistatus::inst()->close();
     #========
     if (DAV_Multistatus::active()) {
         DAV_Multistatus::inst()->close();
     } elseif ($destinationResource) {
         DAV::header(array('status' => DAV::HTTP_NO_CONTENT));
     } else {
         DAV::redirect(DAV::HTTP_CREATED, $destination);
     }
     #>>>>>>>>
 }