package HTTP::DAVServer::PROPFIND;
our $VERSION=0.1;
use strict;
use warnings;
=head1 NAME
HTTP::DAVServer::PROPFIND - Implements the PROPFIND method
=cut
use File::stat qw(stat);
our %fileProps = (
creationdate => 1,
displayname => 1,
getcontentlanguage => 1,
getcontentlength => 1,
getetag => 1,
getlastmodified => 1,
getcontenttype => 1,
lockdiscovery => 1,
resourcetype => 1,
source => 1,
supportedlock => 1,
);
our %collProps = (
creationdate => 1,
displayname => 1,
resourcetype => 1,
supportedlock => 1,
);
sub handle {
my ($self, $r, $url, $responder, $req) = @_;
$responder->badRequest( $r, "PROPFINDVAGUE" ) unless exists $req->{'{DAV:}prop'}
|| exists $req->{'{DAV:}allprop'}
|| exists $req->{'{DAV:}propname'};
$url =~ s#/*$##;
my $depth=$r->http('Depth');
$depth="infinite" unless $depth =~ /^(0|1|infinite)$/;
my $root = $HTTP::DAVServer::ROOT;
my $host = $HTTP::DAVServer::HOST;
# Property names response
if ( exists $req->{'{DAV:}propname'} ) {
my $xml="";
if ( $depth eq "0") {
$xml .= qq(\nhttp://$host$url\n\n);
map { $xml .= qq(\t<$_/>\n) } keys %fileProps if -f "$root/$url";
map { $xml .= qq(\t<$_/>\n) } keys %collProps if -d "$root/$url";
if (-r "$root/$url") {
$xml .= qq(\nHTTP/1.1 200 OK\n\n);
} else {
$xml .= qq(\nHTTP/1.1 404 Not Found\n\n);
}
} else {
if ( -f "$root/$url" ) {
$xml .= qq(\nhttp://$host$url\n\n);
map { $xml .= qq(\t<$_/>\n) } keys %fileProps;
$xml .= qq(\nHTTP/1.1 200 OK\n\n);
}
if ( -d "$root/$url" ) {
$xml .= qq(\nhttp://$host$url\n\n);
map { $xml .= qq(\t<$_/>\n) } keys %collProps;
$xml .= qq(\nHTTP/1.1 200 OK\n\n);
opendir DIR, "$root/$url";
while ( my $file = readdir DIR ) {
if (-f "$root/$url/$file") {
$xml .= qq(\n\n);
$xml .= qq(\nhttp://$host$url/$file\n\n);
map { $xml .= qq(\t<$_/>\n) } keys %fileProps;
$xml .= qq(\nHTTP/1.1 200 OK\n\n);
last;
}
}
}
}
$xml .= "\n";
$responder->multiStatus( $r, $xml );
}
my $reqProps=$req->{'{DAV:}prop'} || undef;
# Property values response
my $response=[];
fetchProps( $root, $url, "http://$host", $reqProps, $depth, $response );
my $xml = qq(\n) . join ( qq(\n\n), @$response) . qq(\n);
$responder->multiStatus( $r, $xml );
}
sub fetchProps {
my ($root, $url, $urlPrefix, $reqProps, $depth, $response) = @_;
$DB::single=1;
my $path="$root$url";
if ($depth eq "0") {
if (-d $path) {
return push @$response, collection( $path, $url, $urlPrefix, $reqProps );
} else {
return push @$response, file( $path, $url, $urlPrefix, $reqProps );
}
}
if ($depth eq "1") {
if (-d $path) {
fetchProps($root, $url, $urlPrefix, $reqProps, 0, $response);
opendir PATH, $path;
while (my $item = readdir PATH) {
next if $item =~ /^\.\.?$/;
fetchProps($root, "$url/$item", $urlPrefix, $reqProps, 0, $response);
}
closedir PATH;
} else {
fetchProps($root, $url, $urlPrefix, $reqProps, 0, $response);
}
}
if ($depth eq "infinite") {
if (-d $path) {
opendir PATH, $path;
while (my $item = readdir PATH) {
fetchProps($root, "$url/$item", $urlPrefix, $reqProps, $depth, $response);
}
closedir PATH;
} else {
fetchProps($root, $url, $urlPrefix, $reqProps, 0, $response);
}
}
}
sub collection {
my ($path, $url, $urlPrefix, $reqProps) = @_;
$reqProps ||= \%collProps;
my $stat=stat($path);
my $ret=qq(\t$urlPrefix$url/\n)
. qq(\t)
. qq(\n);
foreach my $reqProp ( keys %{$reqProps} ) {
$reqProp =~ s/^{DAV:}//;
$ret .= qq(\t<$reqProp>$reqProp>\n) and next if $reqProp eq "resourcetype";
if ($reqProp eq "creationdate") {
$ret .= qq(\t<$reqProp>) . HTTP::DAVServer::dateEpoch( $stat->mtime ) . qq($reqProp>\n);
next;
}
if ($reqProp eq "displayname") {
$path =~ /([^\/]+\/)$/;
$ret .= qq(\t<$reqProp>$1$reqProp>\n);
next;
}
warn "Collection property $reqProp not known\n" if $HTTP::DAVServer::WARN;
}
$ret .= qq()
. qq(HTTP/1.1 200 OK)
. qq();
return $ret;
}
sub file {
my ($path, $url, $urlPrefix, $reqProps) = @_;
$reqProps ||= \%fileProps;
my $stat=stat($path);
my $ret=qq(\t$urlPrefix$url\n)
. qq(\t)
. qq(\n);
foreach my $reqProp ( keys %{$reqProps} ) {
$reqProp =~ s/^{DAV:}//;
$ret .= qq(\t<$reqProp/>\n) and next if $reqProp eq "resourcetype";
if ($reqProp eq "creationdate") {
$ret .= qq(\t<$reqProp>) . HTTP::DAVServer::dateEpoch( $stat->mtime ) . qq($reqProp>\n);
next;
}
if ($reqProp eq "displayname") {
$path =~ /([^\/]+\/)$/;
$ret .= qq(\t<$reqProp>$1$reqProp>\n);
next;
}
if ($reqProp eq "getcontentlength") {
$ret .= qq(\t<$reqProp>) . $stat->size . qq($reqProp>\n);
next;
}
warn "Collection property $reqProp not known\n" if $HTTP::DAVServer::WARN;
}
$ret .= qq()
. qq(HTTP/1.1 200 OK)
. qq();
return $ret;
}
=head1 SUPPORT
For technical support please email to jlawrenc@cpan.org ...
for faster service please include "HTTP::DAVServer" and "help" in your subject line.
=head1 AUTHOR
Jay J. Lawrence - jlawrenc@cpan.org
Infonium Inc., Canada
http://www.infonium.ca/
=head1 COPYRIGHT
Copyright (c) 2003 Jay J. Lawrence, Infonium Inc. All rights reserved.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=head1 ACKNOWLEDGEMENTS
Thank you to the authors of my prequisite modules. With out your help this code
would be much more difficult to write!
XML::Simple - Grant McLean
XML::SAX - Matt Sergeant
DateTime - Dave Rolsky
Also the authors of litmus, a very helpful tool indeed!
=head1 SEE ALSO
HTTP::DAV, HTTP::Webdav, http://www.webdav.org/, RFC 2518
=cut
1;