HTTP::DAV - A WebDAV client library for Perl5
# DAV script that connects to a webserver, safely makes # a new directory and uploads all html files in # the /tmp directory.
use HTTP::DAV;
$d = new HTTP::DAV; $url = "http://host.org:8080/dav/";
$d->credentials( -user=>"pcollins",-pass =>"mypass", -url =>$url, -realm=>"DAV Realm" );
$d->open( -url=>"$url ) or die("Couldn't open $url: " .$d->message . "\n");
# Make a null lock on newdir $d->lock( -url => "$url/newdir", -timeout => "10m" ) or die "Won't put unless I can lock for 10 minutes\n";
# Make a new directory $d->mkcol( -url => "$url/newdir" ) or die "Couldn't make newdir at $url\n";
# Upload multiple files to newdir. if ( $d->put( -local => "/tmp/*.html", -url => $url ) ) { print "successfully uploaded multiple files to $url\n"; } else { print "put failed: " . $d->message . "\n"; }
$d->unlock( -url => $url );
HTTP::DAV is a Perl API for interacting with and modifying content on webservers using the WebDAV protocol. Now you can LOCK, DELETE and PUT files and much more on a DAV-enabled webserver.
HTTP::DAV is part of the PerlDAV project hosted at http://www.webdav.org/perldav/ and has the following features:
dave
, a fully-functional ftp-style interface written on top of the HTTP::DAV API and bundled by default with the HTTP::DAV library. (If you've already installed HTTP::DAV, then dave will also have been installed (probably into /usr/local/bin). You can see it's man page by typing ``perldoc dave'' or going to http://www.webdav.org/perldav/dave/.
LWP
for more information.
HTTP::DAV
essentially has two API's, one which is accessed through this module directly (HTTP::DAV) and is a simple abstraction to the rest of the HTTP::DAV::* Classes. The other interface consists of the HTTP::DAV::* classes which if required allow you to get ``down and dirty'' with your DAV and HTTP interactions.
The methods provided in HTTP::DAV
should do most of what you want. If, however, you need more control over the client's operations or need more info about the server's responses then you will need to understand the rest of the HTTP::DAV::* interfaces. A good place to start is with the HTTP::DAV::Resource
and HTTP::DAV::Response
documentation.
You can pass parameters to HTTP::DAV
methods in one of two ways: named or unnamed.
Named parameters provides for a simpler/easier to use interface. A named interface affords more readability and allows the developer to ignore a specific order on the parameters. (named parameters are also case insensitive)
Each argument name is preceded by a dash. Neither case nor order matters in the argument list. -url, -Url, and -URL are all acceptable. In fact, only the first argument needs to begin with a dash. If a dash is present in the first argument, HTTP::DAV
assumes dashes for the subsequent ones.
Each method can also be called with unnamed parameters which often makes sense for methods with only one parameter. But the developer will need to ensure that the parameters are passed in the correct order (as listed in the docs).
Doc: method( -url=>$url, [-depth=>$depth] ) Named: $d->method( -url=>$url, -depth=>$d ); # VALID Named: $d->method( -Depth=>$d, -Url=>$url ); # VALID Named: $d->method( Depth=>$d, Url=>$url ); # INVALID (needs -) Named: $d->method( -Arg2=>$val2 ); # INVALID, ARG1 is not optional Unnamed: $d->method( $val1 ); # VALID Unnamed: $d->method( $val2,$val1 ); # INVALID, ARG1 must come first.
IMPORTANT POINT!!!! If you specify a named parameter first but then forget for the second and third parameters, you WILL get weird things happen. E.g. this is bad:
$d->method( -url=>$url, $arg2, $arg3 ); # BAD BAD BAD
In all of the methods specified in PUBLIC METHODS there are some common concepts you'll need to understand:
-url=>"host.org/dav_dir/" # Absolute -url=>"/dav_dir/" # Relative -url=>"file.txt" # Relative
You can only use a relative URL if you have already ``open''ed an absolute URL.
The HTTP::DAV module now consistently uses the named parameter: URL. The lower-level HTTP::DAV::Resource interface inconsistently interchanges URL and URI. I'm working to resolve this, in the meantime, you'll just need to remember to use the right one by checking the documentation if you need to mix up your use of both interfaces.
The glob may contain the characters ``*'', ``?'' and the set operator ``[...]'' where ... contains multiple characters ([1t2]) or a range such ([1-5]). For the curious, the glob is converted to a regex and then matched: ``*'' to ``.*'', ``?'' to ``.'', and the [] is left untouched.
It is important to note that globs only operate at the leaf-level. For instance ``/my_dir/*/file.txt'' is not a valid glob.
If a glob matches no URL's the command will fail (which normally means returns 0).
Globs are useful in conjunction with CALLBACKS to provide feedback as each operation completes.
See the documentation for each method to determine whether it supports globbing.
Globs are useful for interactive style applications (see the source code for dave
as an example).
Example globs:
$dav1->delete(-url=>"/my_dir/file[1-3]"); # Matches file1, file2, file3 $dav1->delete(-url=>"/my_dir/file[1-3]*.txt");# Matches file1*.txt,file2*.txt,file3*.txt $dav1->delete(-url=>"/my_dir/*/file.txt"); # Invalid. Can only match at leaf-level
The rationale behind the callback is that a recursive get/put or an operation against many files (using a glob
) can actually take a long time to complete.
Example callback:
$d->get( -url=>$url, -to=>$to, -callback=>\&mycallback );
Your callback function MUST accept arguments as follows:
sub cat_callback {
my($status,$mesg,$url,$so_far,$length,$data)
= @_;
...
}
The status
argument specifies whether the operation has succeeded (1), failed (0), or is in progress (-1).
The mesg
argument is a status message. The status message could contain any string and often contains useful error messages or success messages.
The url
the remote URL.
The so_far
, length
- these parameters indicate how many bytes have been downloaded and how many we should expect. This is useful for doing ``56% to go'' style-gauges.
The data
parameter - is the actual data transferred. The cat
command uses this to print the data to the screen. This value will be empty for put
.
See the source code of dave
for a useful sample of how to setup a callback.
Note that these arguments are NOT named parameters.
All error messages set during a ``multi-operation'' request (for instance a recursive get/put) are also retrievable via the errors()
function once the operation has completed. See ERROR HANDLING
for more information.
HTTP::DAV
client
$d = HTTP::DAV->new()
The -useragent
parameter expects an HTTP::DAV::UserAgent
object. See the dave
program for an advanced example of a custom UserAgent that interactively prompts the user for their username and password.
URL
and/or REALM
.
When the client hits a protected resource it will check these credentials to see if either the URL
or REALM
match the authorization response.
Either URL
or REALM
must be provided.
returns no value
Example:
$d->credentials( -url=>'myhost.org:8080/test/', -user=>'pcollins', -pass=>'mypass');
$val
. 0=off 3=noisy.
$val
default is 0.
returns no value.
When the value is greater than 1, the HTTP::DAV::Comms
module will log all of the client<=>server interactions into /tmp/perldav_debug.txt.
For all of the following operations, URL can be absolute (http://host.org/dav/) or relative (../dir2/). The only operation that requires an absolute URL is open.
-url
-dest
-overwrite
-depth
The return value is always 1 or 0 indicating success or failure.
Requires a working resource to be set before being called. See open
.
Note: if either 'URL'
or 'DEST'
are locked by this dav client, then the lock headers will be taken care of automatically. If the either of the two URL's are locked by someone else, the server should reject the request.
copy examples:
$d->open(-url=>"host.org/dav_dir/");
Recursively copy dir1/ to dir2/
$d->copy(-url=>"dir1/", -dest=>"dir2/");
Non-recursively and non-forcefully copy dir1/ to dir2/
$d->copy(-url=>"dir1/", -dest=>"dir2/",-overwrite=>0,-depth=>0);
Create a copy of dir1/file.txt as dir2/file.txt
$d->cwd(-url=>"dir1/"); $d->copy("file.txt","../dir2");
Create a copy of file.txt as dir2/new_file.txt
$d->copy("file.txt","/dav_dir/dir2/new_file.txt")
This is synonymous to open except that the URL can be relative and may contain a glob
(the first match in a glob will be used).
$d->open("host.org/dav_dir/dir1/"); $d->cwd("../dir2"); $d->cwd(-url=>"../dir1");
The return value is always 1 or 0 indicating success or failure.
Requires a working resource to be set before being called. See open
.
You can not cwd to files, only collections (directories).
$d->open("host.org/dav_dir/"); $d->delete("index.html"); $d->delete("./dir1"); $d->delete(-url=>"/dav_dir/dir2/file*",-callback=>\&mycallback);
-url
resource(s)
you'd like to delete. It can be a file, directory or glob
.
-callback
is a reference to a callback function which will be called everytime a file is deleted. This is mainly useful when used in conjunction with GLOBS deletes. See callbacksRequires a working resource to be set before being called. See open
.
This command will recursively delete directories. BE CAREFUL of uninitialised file variables in situation like this: $d->delete(``$dir/$file''). This will trash your $dir if $file is not set.
URL
to the local location indicated by TO
.
-url
-to
- a B<filename> indicating where to save the contents.
- a B<FileHandle reference>.
- a reference to a B<scalar object> into which the contents will be saved.
If the -url
matches multiple files (via a glob or a directory download), then the get
routine will return an error if you try to use a FileHandle reference or a scalar reference.
-callback
The return value of get is always 1 or 0 indicating whether the entire get sequence was a success or if there was ANY failures. For instance, in a recursive get, if the server couldn't open 1 of the 10 remote files, for whatever reason, then the return value will be 0. This is so that you can have your script call the errors()
routine to handle error conditions.
Previous versions of HTTP::DAV allowed the return value to be the file contents if no -to attribute was supplied. This functionality is deprecated.
Requires a working resource to be set before being called. See open
.
get examples:
$d->open("host.org/dav_dir/");
Recursively get remote my_dir/ to .
$d->get("my_dir/",".");
Recursively get remote my_dir/ to /tmp/my_dir/ calling &mycallback($success,$mesg) everytime a file operation is completed.
$d->get("my_dir","/tmp",\&mycallback);
Get remote my_dir/index.html to /tmp/index.html
$d->get(-url=>"/dav_dir/my_dir/index.html",-to=>"/tmp");
Get remote index.html to /tmp/index1.html
$d->get("index.html","/tmp/index1.html");
Get remote index.html to a filehandle
my $fh = new FileHandle; $fh->open(">/tmp/index1.html"); $d->get("index.html",\$fh);
Get remote index.html as a scalar (into the string $file_contents):
my $file_contents; $d->get("index.html",\$file_contents);
Get all of the files matching the globs file1* and file2*:
$d->get("file[12]*","/tmp");
Get all of the files matching the glob file?.html:
$d->get("file?.html","/tmp"); # downloads file1.html and file2.html but not file3.html or file1.txt
Invalid glob:
$d->get("/dav_dir/*/index.html","/tmp"); # Can not glob like this.
$d->lock( -url => "index.html", -owner => "Patrick Collins", -depth => "infinity", -scope => "exclusive", -type => "write", -timeout => "10h" )
See HTTP::DAV::Resource
lock()
for details of the above parameters.
The return value is always 1 or 0 indicating success or failure.
Requires a working resource to be set before being called. See open
.
When you lock a resource, the lock is held against the current HTTP::DAV object. In fact, the locks are held in a HTTP::DAV::ResourceList
object. You can operate against all of the locks that you have created as follows:
## Print and unlock all locks that we own. my $rl_obj = $d->get_lockedresourcelist(); foreach $resource ( $rl_obj->get_resources() ) { @locks = $resource->get_locks(-owned=>1); foreach $lock ( @locks ) { print $resource->get_uri . "\n"; print $lock->as_string . "\n"; } ## Unlock them? $resource->unlock; }
Typically, a simple $d->unlock($uri)
will suffice.
lock example
$d->lock($uri, -timeout=>"1d"); ... $d->put("/tmp/index.html",$uri); $d->unlock($uri);
The return value is always 1 or 0 indicating success or failure.
Requires a working resource to be set before being called. See open
.
$d->open("host.org/dav_dir/"); $d->mkcol("new_dir"); # Should succeed $d->mkcol("/dav_dir/new_dir"); # Should succeed $d->mkcol("/dav_dir/new_dir/xxx/yyy"); # Should fail
-url
-dest
-overwrite
Requires a working resource to be set before being called. See open
.
The return value is always 1 or 0 indicating success or failure.
Note: if either 'URL'
or 'DEST'
are locked by this dav client, then the lock headers will be taken care of automatically. If either of the two URL's are locked by someone else, the server should reject the request.
move examples:
$d->open(-url=>"host.org/dav_dir/");
move dir1/ to dir2/
$d->move(-url=>"dir1/", -dest=>"dir2/");
non-forcefully move dir1/ to dir2/
$d->move(-url=>"dir1/", -dest=>"dir2/",-overwrite=>0);
Move dir1/file.txt to dir2/file.txt
$d->cwd(-url=>"dir1/"); $d->move("file.txt","../dir2");
move file.txt to dir2/new_file.txt
$d->move("file.txt","/dav_dir/dir2/new_file.txt")
open will perform a propfind against URL. If the server does not understand the request then the open will fail.
Similarly, if the server indicates that the resource at URL is NOT a collection, the open command will fail.
Requires a working resource to be set before being called. See open
.
The return value is a string of comma separated OPTIONS that the server states are legal for URL or undef otherwise.
A fully compliant DAV server may offer as many methods as: OPTIONS, TRACE, GET, HEAD, DELETE, PUT, COPY, MOVE, PROPFIND, PROPPATCH, LOCK, UNLOCK
Note: IIS5 does not support PROPPATCH or LOCK on collections.
Example:
$options = $d->options($url); print $options . "\n"; if ($options=~ /\bPROPPATCH\b/) { print "OK to proppatch\n"; }
Or, put more simply:
if ( $d->options($url) =~ /\bPROPPATCH\b/ ) { print "OK to proppatch\n"; }
-depth
can be used to specify how deep the propfind goes. ``0'' is collection only. ``1'' is collection and it's immediate members (This is the default value). ``infinity'' is the entire directory tree. Note that most DAV compliant servers deny ``infinity'' depth propfinds for security reasons.
Requires a working resource to be set before being called. See open
.
The return value is an HTTP::DAV::Resource
object on success or 0 on failure.
The Resource object can be used for interrogating properties or performing other operations.
## Print collection or content length if ( $r=$d->propfind( -url=>"/my_dir", -depth=>1) ) { if ( $r->is_collection ) { print "Collection\n" print $r->get_resourcelist->as_string . "\n" } else { print $r->get_property("getcontentlength") ."\n"; } }
Please note that although you may set a different namespace for a property of a resource during a set_prop, HTTP::DAV currently ignores all XML namespaces so you will get clashes if two properties have the same name but in different namespaces. Currently this is unavoidable but I'm working on the solution.
-action
equals ``set'' then we set a property named -propname
to -propvalue
in the namespace -namespace
for -url
.
If -action
equals ``remove'' then we unset a property named -propname
in the namespace -namespace
for -url
.
If no action is supplied then the default action is ``set''.
The return value is an HTTP::DAV::Resource
object on success or 0 on failure.
The Resource object can be used for interrogating properties or performing other operations.
To explicitly set a namespace in which to set the propname then you can use the -namespace
and -nsabbr
(namespace abbreviation) parameters. But you're welcome to play around with DAV namespaces.
Requires a working resource to be set before being called. See open
.
It is recommended that you use set_prop
and unset_prop
instead of proppatch for readability.
set_prop
simply calls proppatch(-action=
set)> and unset_prop
calls proppatch(-action=
``remove'')>
See set_prop
and unset_prop
for examples.
-local points to a file, directory or series of files or directories (indicated by a glob).
If the filename contains any of the characters `*', `?' or `[' it is a candidate for filename substitution, also known as ``globbing''. This word is then regarded as a pattern (``glob-pattern''), and replaced with an alphabetically sorted list of file names which match the pattern.
One can upload/put a string by passing a reference to a scalar in the -local parameter. See example below.
put requires a working resource to be set before being called. See open
.
The return value is always 1 or 0 indicating success or failure.
See get() for a description of what the optional callback parameter does.
put examples:
Put a string to the server:
my $myfile = "This is the contents of a file to be uploaded\n"; $d->put(-local=>\$myfile,-url=>"http://www.host.org/dav_dir/file.txt");
Put a local file to the server:
$d->put(-local=>"/tmp/index.html,-url=>"http://www.host.org/dav_dir/");
Put a series of local files to the server:
In these examples, /tmp contains file1.html, file1, file2.html, file2.txt, file3.html, file2/
$d->put(-local=>"/tmp/file[12]*",-url=>"http://www.host.org/dav_dir/");
uploads file1.html, file1, file2.html, file2.txt and the directory file2/ to dav_dir/.
-propname
to -propvalue
in the namespace -namespace
for -url
.
Requires a working resource to be set before being called. See open
.
The return value is an HTTP::DAV::Resource
object on success or 0 on failure.
The Resource object can be used for interrogating properties or performing other operations.
Example:
if ( $r = $d->set_prop(-url=>$url, -namespace=>"dave", -propname=>"author", -propvalue=>"Patrick Collins" ) ) { print "Author property set\n"; } else { print "set_prop failed:" . $d->message . "\n"; }
See the note in propfind about namespace support in HTTP::DAV. They're settable, but not readable.
steal will perform a propfind against URL and then, any locks that are found will be unlocked one by one regardless of whether we own them or not.
Requires a working resource to be set before being called. See open
.
The return value is always 1 or 0 indicating success or failure. If multiple locks are found and unlocking one of them fails then the operation will be aborted.
if ($d->steal()) { print "Steal succeeded\n"; } else { print "Steal failed: ". $d->message() . "\n"; }
Requires a working resource to be set before being called. See open
.
The return value is always 1 or 0 indicating success or failure.
if ($d->unlock()) { print "Unlock succeeded\n"; } else { print "Unlock failed: ". $d->message() . "\n"; }
-propname
in the namespace -namespace
for -url
.
Requires a working resource to be set before being called. See open
.
The return value is an HTTP::DAV::Resource
object on success or 0 on failure.
The Resource object can be used for interrogating properties or performing other operations.
Example:
if ( $r = $d->unset_prop(-url=>$url, -namespace=>"dave", -propname=>"author", ) ) { print "Author property was unset\n"; } else { print "set_prop failed:" . $d->message . "\n"; }
See the note in propfind about namespace support in HTTP::DAV. They're settable, but not readable.
HTTP::DAV::UserAgent
object.
You may want to interact with the HTTP::DAV::UserAgent
object
to modify request headers or provide advanced authentication
procedures. See dave for an advanced authentication procedure.
HTTP::Request
object.
You would only use this to inspect a request that has already occurred.
If you would like to modify the HTTP::Request
BEFORE the HTTP request takes place (for instance to add another header), you will need to get the HTTP::DAV::UserAgent
using get_user_agent
and interact with that.
HTTP::DAV::Resource
).
The working resource is changed whenever you open a url or use the cwd command.
e.g. $r = $d->get_workingresource print ``pwd: '' . $r->get_uri . ``\n'';
URL
.
The working resource is changed whenever you open a url or use the cwd command.
print "pwd: " . $d->get_workingurl . "\n";
HTTP::DAV::ResourceList
object that represents all of the locks we've created using THIS dav client.
print "pwd: " . $d->get_workingurl . "\n";
BASE_URI
and REL_URI
and returns a new URI.
If BASE_URI
is not supplied then the current working resource (as indicated by get_workingurl) is used. If BASE_URI
is not set and there is no current working resource the REL_URI
will be returned.
For instance: $d->open(``http://host.org/webdav/dir1/'');
# Returns "http://host.org/webdav/dir2/" $d->get_absolute_uri(-rel_uri=>"../dir2");
# Returns "http://x.org/dav/dir2/file.txt" $d->get_absolute_uri(-rel_uri =>"dir2/file.txt", ->base_uri=>"http://x.org/dav/");
Note that it subtly takes care of trailing slashes.
message
gets the last success or error message.
The return value is always a scalar (string) and will change everytime a dav operation is invoked (lock, cwd, put, etc).
See also errors
for operations which contain multiple error messages.
Some of HTTP::DAV
's operations perform multiple request to the server. At the time of writing only put and get are considered multi-request since they can operate recursively requiring many HTTP requests.
In these situations you should check the errors array if to determine if any of the requests failed.
The errors
function is used for multi-request operations and not to be confused with a multi-status server response. A multi-status server response is when the server responds with multiple error messages for a SINGLE request. To deal with multi-status responses, see HTTP::DAV::Response
.
# Recursive put if (!$d->put( "/tmp/my_dir", $url ) ) { # Get the overall message print $d->message; # Get the individual messages foreach $err ( $d->errors ) { print " Error:$err\n" } }
This value will always be the same as the value returned from an HTTP::DAV::method. For instance:
# This will always evaluate to true ($d->lock($url) eq $d->is_success) ?
You may want to use the is_success method if you didn't capture the return value immediately. But in most circumstances you're better off just evaluating as follows:
if($d->lock($url))
{ ... }
HTTP::DAV::Response
object.
You may want to use this if you have just called a propfind and need the individual error messages returned in a MultiStatus.
If you find that you're using get_last_response()
method a lot, you may be better off using the more advanced HTTP::DAV
interface and interacting with the HTTP::DAV::* interfaces directly as discussed in the intro. For instance, if you find that you're always wanting a detailed understanding of the server's response headers or messages, then you're probably better off using the HTTP::DAV::Resource
methods and interpreting the HTTP::DAV::Response
directly.
To perform detailed analysis of the server's response (if for instance you got back a multistatus response) you can call get_lastresponse which will return to you the most recent response object (always the result of the last operation, PUT, PROPFIND, etc). With the returned HTTP::DAV::Response object you can handle multi-status responses.
For example:
# Print all of the messages in a multistatus response if (! $d->unlock($url) ) { $response = $d->get_lastresponse(); if ($response->is_multistatus() ) { foreach $num ( 0 .. $response->response_count() ) { ($err_code,$mesg,$url,$desc) = $response->response_bynum($num); print "$mesg ($err_code) for $url\n"; } } }
$dav->new_resource( -uri => "http://..." );
You shouldn't need this method. Call open or cwd to set the working resource.
You CAN call set_workingresource but you will need to perform a propfind immediately following it to ensure that the working resource is valid.
Please see the primary HTTP::DAV webpage at (http://www.webdav.org/perldav/http-dav/) or the README file in this library.
You'll want to also read:
HTTP::DAV::Response
, HTTP::DAV::Resource
, dave
and maybe if you're more inquisitive:
LWP::UserAgent
,HTTP::Request
, HTTP::DAV::Comms
,HTTP::DAV::Lock
, HTTP::DAV::ResourceList
, HTTP::DAV::Utils
This module is Copyright (C) 2001 by
Patrick Collins G03 Gloucester Place, Kensington Sydney, Australia
Email: pcollins@cpan.org Phone: +61 2 9663 4916
All rights reserved.
You may distribute this module under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.