Apache-WebDAV
view release on metacpan or search on metacpan
lib/Apache/WebDAV.pm view on Meta::CPAN
}
elsif($handler->test('d', $file))
{
push @errors, $file unless $handler->rmdir($file);
}
}
if(@errors)
{
return $self->delete_response($r, \@errors);
}
else
{
return HTTP_NO_CONTENT;
}
}
#
# Fetch a resource.
#
sub get
{
my ($self, $r, $handler) = @_;
my $path = $r->uri();
# If the requested path is a readable file, use the Filesys::Virtual
# interface to read the file and send it back to the client.
if($handler->test('f', $path) && $handler->test('r', $path))
{
$r->header_out('Last-Modified', $handler->modtime($path));
my $fh = $handler->open_read($path) or return NOT_FOUND;
my $file;
while(my $line = <$fh>)
{
$file .= $line;
}
$handler->close_read($fh);
$r->status(200);
$r->header_out('Content-Length', length($file));
$r->send_http_header();
$r->print($file);
return OK;
}
# If the requested path is a directory, it's unclear what we're supposed to
# do. Net::DAV::Server prints an HTML representation of the directory
# structure.
#
# Update: this happens if you connect with a regular browser, or if you
# connect using IE but don't check the Web Folder box. So just print a
# warning.
elsif($handler->test('d', $path))
{
$r->content_type('text/html; charset="utf-8"');
$r->send_http_header();
$r->print("If you are using IE, please use File -> Open and check the
Open As Web Folder box.");
}
else
{
return NOT_FOUND;
}
}
#
# Respond to a head request about a file.
#
sub head
{
my ($self, $r, $handler) = @_;
my $path = $r->uri();
if($handler->test('f', $path))
{
$r->header_out('Last-Modified', $handler->modtime($path));
}
elsif($handler->test('d', $path))
{
$r->content_type('text/html; charset="utf-8"');
$r->send_http_header();
}
else
{
return NOT_FOUND;
}
return OK;
}
#
# Create a "collection" which is actually a directory.
#
sub mkcol
{
my ($self, $r, $handler) = @_;
my $path = $r->uri();
my $content = $self->get_request_content($r);
if($content)
{
return 415; # huh?
}
elsif(!$handler->test('e', $path))
{
$handler->mkdir($path);
if(!$handler->test('d', $path))
{
return 409; # What?
}
else
{
return 201; # Created.
}
}
else
{
return HTTP_METHOD_NOT_ALLOWED;
}
}
#
# Move a resource to another location. I'm specifically performing a copy and
# then a delete, something that sort of makes sense but has specific drawbacks
# according to the WebDAV book. We'll worry about it later, because it's
# possible that none of our child modules will ever use this functionality.
#
sub move
{
my ($self, $r, $handler) = @_;
my $path = $r->uri();
my $destination = $r->header_in('Destination');
$destination = URI->new($destination)->path();
lib/Apache/WebDAV.pm view on Meta::CPAN
return 403;
}
else
{
return FORBIDDEN;
}
}
my $delete_result = $self->delete_resource($r, $handler, $path);
# Did the delete work properly?
if(!$delete_result)
{
return FORBIDDEN;
}
if($already_exists)
{
return 204;
}
else
{
return 201;
}
}
#
# Specify the options this WebDAV server supports.
#
sub options
{
my ($self, $r, $handler) = @_;
$r->header_out('Allow' => join(',', map { uc } keys %implemented));
$r->header_out('DAV' => '1,2,<http://apache.org/dav/propset/fs/1>');
$r->header_out('MS-Author-Via' => 'DAV');
$r->header_out('Keep-Alive' => 'timeout=15, max=96');
$r->send_http_header();
return OK;
}
#
# Get information about a file or a directory (or the contents of a directory).
#
sub propfind
{
my ($self, $r, $handler) = @_;
my $depth = $r->header_in('Depth');
my $uri = $r->uri();
# Make sure the resource exists
if(!$handler->test('e', $uri))
{
return NOT_FOUND;
}
$r->status(207);
$r->content_type('text/xml; charset="utf-8"');
my @files;
if($depth == 0)
{
@files = ($uri);
}
elsif($depth == 1)
{
$uri =~ s/\/$//; # strip trailing slash, we don't store it in the db
@files = $handler->list($uri);
# remove . and .. from the list
@files = grep( $_ !~ /^\.\.?$/, @files );
# Add a trailing slash to the directory if there isn't one already
if($uri !~ /\/$/)
{
$uri .= '/';
}
# Add the current folder to the front of the filename
@files = map { "$uri$_" } @files;
# Goliath only doesn't want to see the current/base directory in the
# response.
if($r->header_in('User-Agent') !~ /Goliath/)
{
push @files, $uri;
}
}
my %wanted_properties = $self->get_wanted_properties($r);
# The list of properties in order which a stat() call must return.
my @properties = qw(dev ino mode nlink uid gid rdev getcontentlength
atime getlastmodified creationdate);
# Loop through all the files and call stat() on each one. Keep track of
# which properties the client requested.
my @results;
foreach my $path (@files)
{
my %stat;
my $info;
my $handler = $self->get_handler_for_path($path);
$info->{'getcontenttype'} = 'application/octet-stream';
$info->{'resourcetype'} = '';
if($handler->test('d', $path))
{
$info->{'getcontenttype'} = 'httpd/unix-directory';
$info->{'resourcetype'} = 'collection';
}
@stat{@properties} = $handler->stat($path);
lib/Apache/WebDAV.pm view on Meta::CPAN
#
#
# Helper methods below here.
#
#
#
# This method builds up an xml response to a delete request ONLY IF the delete
# request had errors. A delete request with no errors sends only a header, not
# an associated XML document. So again, this method is only used when an error
# occurs.
#
# @arg $r apache object
# @arg $files arrayref of files that had errors
#
# @ret 200 OK
#
sub delete_response
{
my ($self, $r, $files) = @_;
# This is a bit screwed up. WebDrive doesn't properly parse 207 multistatus
# responses for deletes. So if it's webdrive, just send a generic error
# code. I know this sucks but the majority of our users use webdrive so
# we have to do it.
#
# Here is the response from their tech support:
#
# webdrive is not parsing the 207 multistatus response to look for the
# error code. If the DELETE returns an HTTP error like 403 instead of
# 207 then webdrive would recognize the error. Webdrive should parse
# the response but currently it doesn't for the DELETE command.
# It's nothing you are doing wrong, it's just something that wasn't
# fully implemented with webdrive and the delete command.
#
if($r->header_in('User-Agent') =~ /WebDrive/)
{
$r->status(FORBIDDEN);
$r->send_http_header();
return OK;
}
my $doc = new XML::LibXML::Document('1.0', 'utf-8');
my $multistat = $doc->createElement('D:multistatus');
$multistat->setAttribute('xmlns:D', 'DAV:');
$doc->setDocumentElement($multistat);
foreach my $file (@$files)
{
my $response = $doc->createElement('D:response');
$response->appendTextChild('D:href' => $file);
$response->appendTextChild('D:status' => 'HTTP/1.1 403 Forbidden');
$multistat->addChild($response);
}
$r->status(207);
$r->content_type('text/xml; charset="utf-8"');
$r->send_http_header();
if(!$r->header_only())
{
$r->print($doc->toString(1));
}
return OK;
}
#
# Build up a WebDAV flavored XML document containing a list of files in a
# directory. Most of this was copied from Net::DAV::Server, but I took out
# all the stuff specific to HTTP::Daemon, HTTP::Request and HTTP::Response
# (so it would be compatible with apache/mod_perl).
#
# @arg $r apache object
# @arg $files arrayref of files [{path => $path, stat => $info}, {etc...}]
#
# @ret 200 OK
#
sub list_response
{
my ($self, $r, $files) = @_;
my $doc = new XML::LibXML::Document('1.0', 'utf-8');
my $multistat = $doc->createElement('D:multistatus');
$multistat->setAttribute('xmlns:D', 'DAV:');
$doc->setDocumentElement($multistat);
foreach my $file (@$files)
{
my $path = $file->{'path'};
my $stat = $file->{'stat'};
my $resp = $doc->createElement('D:response');
$multistat->addChild($resp);
my $href = $doc->createElement('D:href');
$href->appendText(
File::Spec->catdir(
map { uri_escape encode_utf8 $_ } File::Spec->splitdir($path)
)
);
$resp->addChild($href);
my $okprops = $doc->createElement('D:prop');
foreach my $wanted_prop (keys %$stat)
{
# We set these down there automatically (we are faking quota
# support to keep webdrive happy).
next if $wanted_prop eq 'quota';
next if $wanted_prop eq 'quotaused';
next if $wanted_prop eq 'quota-available-bytes';
next if $wanted_prop eq 'quota-used-bytes';
next if $wanted_prop eq 'quota-assigned-bytes';
( run in 1.630 second using v1.01-cache-2.11-cpan-39bf76dae61 )