"Fossies" - the Fresh Open Source Software Archive

Member "HTTP-DAV-0.49/bin/dave" (28 Nov 2018, 27853 Bytes) of package /linux/www/HTTP-DAV-0.49.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "dave": 0.48_vs_0.49.

    1 #!/usr/bin/perl
    2 
    3 =pod
    4 
    5 =head1 NAME
    6 
    7 dave - DAV Explorer
    8 
    9 =head1 SYNOPSIS
   10 
   11     dave [OPTIONS] URL
   12 
   13 E.g. 
   14 
   15    $ dave -u pcollins -p mypass www.host.org/dav_dir/
   16    ...
   17    dave> get file.txt
   18 
   19 Use C<dave -h> to get help on options.
   20 
   21 Use C<perldoc dave> for the whole manpage.
   22 
   23 =cut
   24 
   25 use strict;
   26 use vars '$AUTOLOAD';
   27 
   28 use AutoLoader;
   29 use Cwd;
   30 use Getopt::Long;
   31 use HTTP::DAV;
   32 use Pod::Usage;
   33 use Term::ReadLine;
   34 use Text::ParseWords;
   35 use Time::Local;
   36 use URI::file;
   37 
   38 my $VERSION    = "2.01";
   39 my $TMP_DIR    = "/tmp";
   40 my $LOCK_OWNER = "dave/v$VERSION (pid $$)";
   41 my $USER_AGENT = "dave/v$VERSION (perldav)";
   42 
   43 ## Setup/parse options
   44 ## Parse options and print usage if there is a syntax error.
   45 ## Note pod2usage comes from Pod::Usage
   46 my $opt_h=0;
   47 my $opt_man=0;
   48 my $user='';
   49 my $pass='';
   50 my $debug=0;
   51 GetOptions(
   52    'help|?'         => \$opt_h, 
   53    'man|?'          => \$opt_man, 
   54    'debug:i'        => \$debug, 
   55    'u|username=s'   => \$user,
   56    'p|password=s'   => \$pass,
   57    'tmpdir:s'       => \$TMP_DIR,
   58 ) or Pod::Usage::pod2usage(2);
   59 
   60 # Basic sanity check for /tmp dir
   61 if (! $TMP_DIR or ! -d $TMP_DIR or ! -w $TMP_DIR) {
   62     die "Can't write into temp dir '$TMP_DIR': $!\n";
   63 }
   64 
   65 ## Setup the Terminal
   66 my $prompt = "dave> ";
   67 my $term = Term::ReadLine->new('DAV Terminal');
   68 $term->ornaments(0);
   69 # Catch ^C
   70 my $OUT = $term->OUT || *STDOUT;
   71 $SIG{INT} = sub { print $OUT "\nType q, bye or quit\n$prompt"; };
   72 
   73 ## Setup the HTTP::DAV client
   74 my $useragent = DAVE::UserAgent->new();
   75 $useragent->agent($USER_AGENT);
   76 my $gdc = HTTP::DAV->new(-useragent=>$useragent);
   77 HTTP::DAV::DebugLevel($debug);
   78 
   79 ## Setup the help system 
   80 my $help = Pod2HelpParser->new();
   81 $help->parse_from_filehandle(\*DATA);
   82 
   83 ## Setup the valid commands and synonyms
   84 my @valid_commands = qw( cd cat copy delete edit get help
   85    lcd lls ls lock mkcol move open options propfind put pwd
   86    quit set sh showlocks steal unlock unset );
   87 
   88 my %command_synonyms = qw(
   89    !     sh
   90    ?     help
   91    q     quit
   92    bye   quit
   93    h     help
   94    dir   ls
   95    mkdir mkcol
   96    rmdir delete
   97    rm    delete
   98    cp    copy
   99    mv    move
  100 );
  101 
  102 # Make a full populated hash from those given above.
  103 # %commands = (
  104 #  quit => quit,
  105 #  q    => quit,
  106 #  help => help,
  107 #  ?    => help,
  108 my %commands;
  109 foreach my $command (@valid_commands) {
  110    $commands{$command} = $command;
  111 }
  112 
  113 # Invert the command_synonyms for easy lookup.
  114 foreach my $synonym (keys %command_synonyms ) {
  115    $commands{$synonym} = $command_synonyms{$synonym};
  116 }
  117 
  118 ###########################################################################
  119 # MAIN
  120 
  121 pod2usage(-verbose => 1)  if ($opt_h);
  122 pod2usage(-verbose => 2)  if ($opt_man);
  123 
  124 # Barf if more than one URL
  125 if (@ARGV > 1) {
  126    pod2usage( -message => "$0: You must only specify one URL.\n")
  127 }
  128 
  129 print $OUT <<END;
  130 
  131 dave -- DAV Explorer (v$VERSION)
  132 Try "help", or "open http://host.com/dav_enabled_dir/"
  133 
  134 END
  135 
  136 # Put the credentials into HTTP::DAV for $url
  137 my $url=shift @ARGV;
  138 if ($user && $url) {
  139    $gdc->credentials( -user=>$user, -pass=>$pass, -url=>$url );
  140 }
  141 &command_open($url) if ($url );
  142 
  143 ######################################################################
  144 # WHILE dave> command
  145 my $line;
  146 while ( defined ($line = $term->readline($prompt)) ) {
  147 
  148    # Hack. Put a space between the ! shellout command and the next arg
  149    $line =~ s/^([!])/$1 /g;
  150 
  151    # Parse the user's typed command and return a parsed list of words.
  152    my @args = &shellwords($line); 
  153 
  154    # Remove empty elements from the list
  155    @args = grep( ! /^\s*$/,@args);
  156 
  157    # If the user has entered nothing then back to while 
  158    # loop and throw another command prompt.
  159    next if ( ! @args );
  160 
  161    # Get the first argument. It should be the command.
  162    my $command = shift @args;
  163 
  164    # Check the validity of the command in our lookup.
  165    if ( &is_valid_command($command) ) {
  166 
  167       # This is so we can do the ref'ed function call 
  168       no strict 'refs'; 
  169 
  170       $command = &get_command($command);
  171      
  172       print $OUT "Valid Command: \"$command\"\n" if $HTTP::DAV::DEBUG>2;
  173 
  174       # Call the command. e.g. &command_put(@args)
  175       my $function_name = "command_" . $command;
  176       my $return_code = &$function_name(@args);
  177 
  178    } else {
  179       print $OUT "Unrecognised command. Try 'help or h' for a list of commands.\n";
  180    }
  181 }
  182 
  183 ######################################################################
  184 # Command implementations
  185 
  186 # This is a simple "print message" (pm) routine. 
  187 # Keeps things neat.
  188 sub pm { print $OUT "** $_[0] **\n"; }
  189 
  190 sub command_cd     { $gdc->cwd    (@_); pm($gdc->message()) }
  191 sub command_copy   { $gdc->copy   (@_); pm($gdc->message()) }
  192 
  193 sub command_delete { 
  194    $gdc->delete(-url=> $_[0], -callback=>\&cb);
  195    pm($gdc->message());
  196 }
  197 
  198 sub command_mkcol  { $gdc->mkcol  (@_); pm($gdc->message()) }
  199 sub command_move   { $gdc->move   (@_); pm($gdc->message()) }
  200 sub command_open   { $gdc->open   (@_); pm($gdc->message()) }
  201 sub command_steal  { $gdc->steal  (@_); pm($gdc->message()) }
  202 sub command_unlock { $gdc->unlock (@_); pm($gdc->message()) }
  203 
  204 sub command_set   { 
  205    my ($url,$pn,$pv,$ns) = @_;
  206    $gdc->set_prop (-url=>$url,     -namespace=>$ns, 
  207                    -propname=>$pn, -propvalue=>$pv); 
  208    pm($gdc->message()) 
  209 }
  210 
  211 sub command_unset   { 
  212    my ($url,$pn,$ns) = @_;
  213    $gdc->unset_prop (-url=>$url, -namespace=>$ns, -propname=>$pn); 
  214    pm($gdc->message()) 
  215 }
  216 
  217 sub command_lock   { 
  218    my ($url,$timeout,$depth ) = @_;
  219    $gdc->lock (-url=>$url,
  220                -timeout=>$timeout,
  221                -depth=>$depth,
  222                -owner=>$LOCK_OWNER); 
  223    pm($gdc->message()) 
  224 }
  225 
  226 sub command_showlocks { 
  227    my $rl = $gdc->get_lockedresourcelist();
  228    if ($rl) {
  229       my $l = $rl->showlocks;
  230       print $OUT ($l ne "") ? $l : "No locks\n";
  231    }
  232 }
  233 
  234 sub command_cat { 
  235    my ($url) = @_;
  236    $gdc->get(-url=>$url, -callback=>\&cat_callback,-chunk=>128);
  237 }
  238 
  239 sub command_edit { 
  240    my $remote_file = shift;
  241 
  242    my $EDITOR= $ENV{DAV_EDITOR} || $ENV{EDITOR} || 'vi';
  243    my $local_file = HTTP::DAV::_tempfile('dave', $TMP_DIR);
  244 
  245    my $resource = $gdc->propfind($remote_file);
  246    if ( $resource && $resource->is_collection() ) {
  247       pm("Can't edit collections");
  248       return;
  249    }
  250 
  251    my $locked=0;
  252    # Try a lock with a 10h timeout first. 
  253    # If that doesn't work then try it without a timeout.
  254    if ( $gdc->lock(-url=>$remote_file, -timeout=>"10h",-owner=>$LOCK_OWNER ) ) {
  255       $locked=1;
  256       pm( "Locked with 10 hour timeout" );
  257    } elsif ( $gdc->lock(-url=>$remote_file, -owner=>$LOCK_OWNER) ) {
  258       $locked=1;
  259       pm( "Locked with infinite timeout" );
  260    } else {
  261       my $resp = $gdc->get_last_response();
  262       if ($resp->messages =~ /Locked/ ) {
  263          pm("$remote_file is locked. Can't edit.");
  264          return;
  265       } else {
  266          pm("Couldn't lock $remote_file for editing (" . $gdc->message . ")" );
  267       }
  268    }
  269 
  270 
  271    if ( $gdc->get($remote_file,$local_file) ) {
  272       pm( $gdc->message());
  273 
  274       pm("Using $EDITOR to edit $local_file");
  275       my $file_date_at_start = (stat($local_file))[9];
  276 
  277       sleep 2;
  278       system("$EDITOR $local_file") == 0 ||
  279          pm("$EDITOR $local_file failed: $!");
  280 
  281       my $file_date_at_end   = (stat($local_file))[9];
  282 
  283       if ( $file_date_at_start eq $file_date_at_end ) {
  284          pm ("File unchanged");
  285       } else {
  286          $gdc->put($local_file,$remote_file);
  287          pm( $gdc->message());
  288       }
  289 
  290       unlink $local_file || pm("Couldn't remove $local_file: $!");
  291    }
  292    else {
  293       pm( $gdc->message());
  294    }
  295 
  296    if ($locked) {
  297       $gdc->unlock($remote_file);
  298       pm( $gdc->message());
  299    }
  300 }
  301 
  302 
  303 
  304 sub command_get { 
  305    my ($remote,$local) = @_;
  306    $local ||= ".";
  307    $gdc->get(-url=>$remote,
  308              -to =>$local,
  309              -callback=>\&cb
  310             );
  311 }
  312 
  313 sub command_put { 
  314    my ($local,$remote) = @_;
  315    $remote ||= ".";
  316    $gdc->put(-local=>$local,
  317              -url  =>$remote,
  318              -callback=>\&cb
  319             );
  320 }
  321 
  322 # PWD
  323 sub command_pwd { 
  324    my $uri = $gdc->get_workingurl(); 
  325    if ($uri) {
  326       print $OUT "$uri\n";
  327       return 1;
  328    } else {
  329       pm("Not connected. Type \"help open\"");
  330       return 0;
  331    }
  332 }
  333 
  334 # OPTIONS
  335 sub command_options{ 
  336    my $options = $gdc->options(@_); 
  337    print $OUT "$options\n" if ($options);
  338    pm($gdc->message);
  339    return $options;
  340 }
  341 
  342 # HELP
  343 sub command_help {
  344    my (@args) = @_;
  345 
  346    # If they just typed help with no arguments then give them generic help
  347    if ( $#args < 0 ) {
  348       print $OUT "Type \"help <command>\" for more detail:\n";
  349       foreach my $i (sort @valid_commands ) {
  350          my($sect,$title,$first_line,$body) = $help->get_help($i);
  351          if ($sect) {
  352             printf $OUT (" %-10s%s\n",$i,$first_line);
  353          } else {
  354             printf $OUT (" %-10s%s\n",$i,"no help");
  355          }
  356       }
  357       print $OUT "\n";
  358    }
  359 
  360    # If they type "help <topic>" then look for help on <topic>.
  361    else {
  362       my $help_topic = shift @args;
  363       my $format =     shift @args || "verbose";
  364 
  365       my ($sect,$title,$first_line,$help_body) = 
  366          $help->get_help( &get_command($help_topic) || $help_topic );
  367       my $help_text;
  368       if ( defined $sect && $sect ne "" ) {
  369          if ($format eq "verbose" ) {
  370             $help_body =~ s/\n/\n      /gs;
  371             $help_text = "\n   $title\n      $help_body\n";
  372          } else {
  373             $help_text = "\n   $sect - $first_line\n";
  374          }
  375       } else {
  376             $help_text = "\nNo help for \"$help_topic\"\n";
  377       }
  378       print $OUT $help_text;
  379    }
  380 }
  381 
  382 # LCD
  383 sub command_lcd {
  384    my (@args) = @_;
  385 
  386    my $args = join(' ', @args) || "";
  387    chdir($args);
  388 }
  389 
  390 # LLS
  391 sub command_lls {
  392    my (@args) = @_;
  393 
  394    my $args = join(' ', @args) || "";
  395    system("ls @args");
  396 }
  397 
  398 # LS (client)
  399 sub command_ls {
  400    my $resource = $gdc->propfind(@_);
  401    if ($resource) {
  402       if ($resource->is_collection) {
  403          print $OUT $resource->get_property('short_ls');
  404       } else {
  405          print $OUT $resource->get_property('long_ls');
  406       }
  407    } else {
  408       pm($gdc->message);
  409    }
  410 }
  411 
  412 sub command_propfind {
  413    my $resource = $gdc->propfind(@_);
  414    if ($resource) {
  415       print $OUT $resource->get_property('long_ls');
  416    } else {
  417       pm($gdc->message);
  418    }
  419 }
  420 
  421 # QUIT
  422 sub command_quit {
  423    my (@args) = @_;
  424    print $OUT "Bye\n";
  425    exit;
  426 }
  427 
  428 # SH (!)
  429 sub command_sh {
  430    my (@args) = @_;
  431 
  432    my $args = join(' ', @args) || "";
  433    system("$args");
  434 }
  435 
  436 sub AUTOLOAD {
  437    my $sub = $AUTOLOAD || "";
  438    #my @args = @{$_[0]} || ();
  439    die "Fatal Error. No function defined $sub ?!\n";
  440 }
  441 
  442 ######################################################################
  443 # CALLBACKS for cat,get and put
  444 {
  445    my $in_transfer=0;
  446    
  447    sub cat_callback {
  448       my($status,$mesg,$url,$so_far,$length,$data) = @_;
  449       $|=1;
  450       if ($status == 1) {
  451          print "\n" if ($in_transfer);
  452          print "**$mesg (success)\n";
  453          $in_transfer=0;
  454       }
  455       if ($status == 0) {
  456          print "**$mesg\n";
  457          $in_transfer=0;
  458       }
  459       if ($status == -1) {
  460          print $OUT $data;
  461          $in_transfer++;
  462       }
  463    }
  464 
  465    # Used for get and put. put doesn't support chunking though.
  466    sub cb {
  467       my($status,$mesg,$url,$so_far,$length,$data) = @_;
  468       $|=1;
  469       if ($status == 1) {
  470          print "\n" if ($in_transfer);
  471          print "  $mesg (success)\n";
  472          $in_transfer=0;
  473       }
  474       if ($status == 0) {
  475          print "**$mesg\n";
  476          $in_transfer=0;
  477       }
  478       if ($status == -1) {
  479          if (!$in_transfer++) {
  480             print "  Transferring $url ($length bytes):\n";
  481          }
  482          my $width = 60;
  483          if ($length>0) {
  484             my $num = int($so_far/$length * $width);
  485             my $space = $width-$num;
  486             print "  [" . "#"x$num . " "x$space . "]";
  487          }
  488          print " $so_far bytes\r";
  489       }
  490    }
  491 }
  492 
  493 ###########################################################################
  494 sub is_valid_command {
  495    my ($command) = @_;
  496    $command = lc($command);
  497    return 1 if defined $commands{$command};
  498 }
  499 
  500 sub get_command {
  501    my ($command) = @_;
  502    $command = lc($command);
  503    return $commands{$command};
  504 }
  505 
  506 BEGIN {
  507 # We make our own specialization of HTTP::DAV::UserAgent (which in turn is already a specialisation of LWP::UserAgent).
  508 # This user agent is able to:
  509 #  - interact with the user on the command line to get user/pass's
  510 #  - allow the user to try 3 times before failing.
  511 {
  512     package DAVE::UserAgent;
  513     use vars qw(@ISA);
  514     @ISA = qw(HTTP::DAV::UserAgent);
  515 
  516     sub new {
  517        my $self = HTTP::DAV::UserAgent::new(@_);
  518        #$self->agent("DAVE/v$VERSION");
  519        $self->{_failed_logins} = ();
  520        return $self;
  521     }
  522 
  523     sub request {
  524        my($self) = shift;
  525        my $resp = $self->SUPER::request(@_);
  526 
  527        # Only if we did not get a 401 back from the server
  528        # do we go and 
  529        # commit the user's details to memory.
  530        $self->_commit_credentials() if ($resp->code() != 401);
  531        return $resp;
  532     }
  533 
  534     sub _set_credentials {shift->{_temp_credentials} = \@_; }
  535     sub _commit_credentials {
  536        my ($self)=@_;
  537        if (defined $self->{_temp_credentials} ) {
  538           $self->credentials(@{$self->{_temp_credentials}});
  539           $self->{_temp_credentials} = undef;
  540        }
  541     }
  542 
  543     sub get_basic_credentials {
  544        my($self, $realm, $url) = @_;
  545        my $userpass;
  546 
  547        # First, try to get the details from our memory.
  548        my @mem_userpass = $self->SUPER::get_basic_credentials($realm,$url);
  549        return @mem_userpass if @mem_userpass;
  550 
  551        if (-t) {
  552 
  553           my $netloc = $url->host_port;
  554           if ($self->{_failed_logins}->{$realm . $netloc}++ > 3) {
  555              return (undef,undef) 
  556           }
  557 
  558           print "\nEnter username for $realm at $netloc: ";
  559           my $user = <STDIN>;
  560           chomp($user);
  561           return (undef, undef) unless length $user;
  562           print "Password: ";
  563           system("stty -echo");
  564           my $password = <STDIN>;
  565           system("stty echo");
  566           print "\n";  # because we disabled echo
  567           chomp($password);
  568 
  569           $self->_set_credentials($netloc, $realm,$user,$password);
  570           #print "Returning $user, $password\n";
  571           return ($user, $password);
  572       } else {
  573           return (undef, undef)
  574       }
  575    }
  576 }
  577 
  578 ######################################################################
  579 # Setup our help system with this nifty Pod::Parser from the 
  580 # Pod at the end of this script.
  581 #
  582 {
  583    package Pod2HelpParser;
  584    
  585    use vars qw(@ISA);
  586    use Pod::Parser;
  587    @ISA = qw(Pod::Parser);
  588    
  589    ######
  590    # Pod2HelpParser - public help access methods.
  591    # 
  592    # get_help() will return from the pod any items 
  593    # that start with $command as help
  594    #
  595    # For instance:
  596    #    my($sect,$title,$terse,$long) = $parser->get_help("open");
  597    sub get_help { 
  598       my ($self,$command) = @_;
  599       foreach my $sect (keys %{$self->{_help_text}} ) {
  600          if ( $sect =~ /^$command\b/i ) {
  601             my $title       = $self->{_title}     {$sect} ||"";
  602             my $first_line  = $self->{_first_line}{$sect} ||"";
  603             my $help_text   = $self->{_help_text} {$sect} ||"";
  604             $help_text=~ s/\n*$//gs;
  605             return ($sect,$title,$first_line,$help_text);
  606          }
  607       }
  608       return ();
  609    }
  610 
  611    sub get_help_list {
  612       my ($self) = @_;
  613       my @return;
  614       foreach my $sect (keys %{$self->{_help_text}} ) {
  615          next if $sect eq "OTHER";
  616          push (@return,$sect);
  617       }
  618       return @return;
  619    }
  620    
  621    ######
  622    # INIT
  623    # These methods are all overriden from Pod::Parser.
  624    # They are effectively call-backs to handle pod.
  625    # Specifically, we're building a hash to provide convenient
  626    # access to the pod data as help information.
  627    sub command { 
  628        my ($parser, $command, $paragraph, $line_num) = @_;
  629        my $title = $parser->interpolate($paragraph, $line_num);
  630    
  631        # Remove extraneous whitespace
  632        $title =~ s/^[\s\n]*(.*?)[\s\n]*$/$1/gis;
  633        
  634        my $section = $title;
  635        $section =~ s/^\s*(.+?)\s.*$/$1/;
  636        $section= ($command eq "item") ? $section : "OTHER";
  637        $parser->{_last_section} = $section;
  638        $parser->{_help_text}{$section} = "";
  639        $parser->{_title}{$section} = $title; 
  640    }
  641    
  642    # Overrriden from Pod::Parser - interprets section text
  643    sub verbatim { 
  644        my ($parser, $paragraph, $line_num) = @_;
  645        my $expansion = $parser->interpolate($paragraph, $line_num);
  646   
  647        # Get the first line
  648        my $first_line = $expansion;
  649        $first_line =~ s/^(.*?)\n.*/$1/gis;
  650    
  651        my $section_head = $parser->_get_last_section;
  652        $parser->{_help_text} {$section_head} .= $expansion;
  653        if (!defined $parser->{_first_line}{$section_head} ) {
  654           $parser->{_first_line}{$section_head} = $first_line;
  655        }
  656    }
  657    
  658    # Overrriden from Pod::Parser - do nothing with normal body text
  659    sub textblock { shift->verbatim(@_); }
  660    sub interior_sequence { return $_[2]; }
  661    sub _get_last_section { $_[0]->{_last_section}; }
  662 }
  663 
  664 } # END BEGIN
  665 
  666 ######################################################################
  667 # PERLDOC
  668 __END__
  669 
  670 =head1 DESCRIPTION
  671 
  672 dave is a powerful command-line program for interacting with WebDAV-enabled webservers. With dave you can:
  673 
  674 =over 4
  675 
  676 =item * get and put files
  677 
  678 =item * make directories on a remote webserver
  679 
  680 =item * remove files and directories from a remote webserver
  681 
  682 =item * edit a file on the webserver with a single command
  683 
  684 =item * recursively get a remote directory
  685 
  686 =item * recursively put a local directory
  687 
  688 =item * lock and unlock remote files and directories
  689 
  690 =item * securely transfer over https
  691 
  692 =item * authenticate using the safer Digest authentication
  693 
  694 =back
  695 
  696 Dave is a part of the PerlDAV project (http://www.webdav.org/perldav/) and is built on top of the HTTP::DAV perl API.
  697 
  698 If you would like to script webdav interactions in Perl checkout the HTTP::DAV API as it's commands are the basis for dave's.
  699 
  700 =head1 OPTIONS
  701 
  702 =over 4
  703 
  704 =item C<-debug N>
  705 
  706 Sets the debug level to N. 0=none. 3=noisy.
  707 
  708 =item C<-h>
  709 
  710 Prints basic help and options.
  711 
  712 =item C<-man>
  713 
  714 Prints the full manual (equivalent to perldoc dave).
  715 
  716 You will need to use a pager like C<more> or C<less>. e.g.
  717 
  718     dave -man |less
  719 
  720 =item C<< -p <password> >>
  721 
  722 Sets the password to be used for the URL.
  723 You must also supply a user. See C<-u>.
  724 
  725 =item C<< -u <username> >>
  726 
  727 Sets the username to be used for the URL.
  728 You must also supply a pass. See C<-p>.
  729 
  730 =item C<-tmpdir /some/path>
  731 
  732 Create temporary files in C</some/path> instead of the default C</tmp>.
  733 
  734 =back
  735 
  736 =head1 COMMANDS
  737 
  738 =over 4
  739 
  740 =item B<cd URL>
  741 
  742 changes directories
  743 
  744    dave> open host.org/dav_dir/
  745    dave> cd dir1
  746    dave> cd ../dir2
  747 
  748 =item B<cat URL>
  749 
  750 shows the contents of a remote file
  751 
  752    dave> open host.org/dav_dir/
  753    dave> cat index.html
  754 
  755 Note: you cannot cat a directory (collection).
  756 
  757 =item B<cp>
  758 
  759 =item B<copy SOURCE_URL DEST_URL>
  760 
  761 copies one remote resource to another
  762 
  763   dave> open host.org/dav_dir/
  764 
  765 Create a copy of dir1/ as dav2/
  766 
  767   dave> cp dir1 dav2
  768 
  769 Create a copy of dir1/file.txt as dav2/file.txt
  770 
  771   dave> cd dir1
  772   dave> copy file.txt ../dav2
  773 
  774 Create a copy of file.txt as ../dav2/new_file.txt
  775 
  776   dave> copy file.txt dav2/new_file.txt
  777 
  778 Aliases: cp
  779 
  780 =item B<rmdir URL>
  781 
  782 =item B<rm URL>
  783 
  784 =item B<delete URL>
  785 
  786 deletes a remote resource
  787 
  788    dave> open host.org/dav_dir/
  789    dave> delete index.html
  790    dave> rmdir ./dir1
  791    dave> delete /dav_dir/dir2/
  792    dave> delete /dav_dir/*.html
  793 
  794 This command recursively deletes directories. BE CAREFUL :)
  795 
  796 This command supported wildcards (globbing). See get.
  797 
  798 Aliases: rm, rmdir
  799 
  800 =item B<edit URL>
  801 
  802 edits the contents of a remote file
  803 
  804    dave> open host.org/dav_dir/
  805    dave> edit index.html
  806 
  807 Edit is equivalent to the following sequence of commands:
  808 
  809    LOCK index.html (if allowed)
  810    GET index.html /tmp/dave.perldav.421341234124
  811    sh $EDITOR /tmp/dave.perldav.421341234124
  812    PUT index.html (if modified)
  813    UNLOCK index.html (if locked)
  814 
  815 Where $EDITOR is determined from the environment variables 
  816 DAV_EDITOR or EDITOR. 
  817 
  818 If DAV_EDITOR is set, it will use that, otherwise it will 
  819 use EDITOR. If neither variables are set, then "vi" will 
  820 be used. 
  821 
  822 Notes: 
  823 
  824 The lock only lasts for 10 hours.
  825 
  826 You cannot edit a directory (collection).
  827 
  828 The temporary save directory is editable by 
  829 editing dave and changing TMP_DIR
  830 
  831 =item B<get URL [FILE]>
  832 
  833 downloads the file or directory at URL
  834 
  835 If FILE is not specified it will be saved to your 
  836 current working directory using the same name as 
  837 the remote name.
  838 
  839   dave> open host.org/dav_dir/
  840 
  841 Recursively get remote my_dir/ to .
  842 
  843   dave> get my_dir/  
  844 
  845 Recursively get remote my_dir/ to /tmp/my_dir/
  846 
  847   dave> get my_dir /tmp
  848 
  849 Get remote my_dir/index.html to /tmp/index.html
  850 
  851   dave> get /dav_dir/my_dir/index.html /tmp
  852 
  853 Get remote index.html to /tmp/index1.html
  854 
  855   dave> get index.html /tmp/index1.html
  856 
  857 Use globs and save to /tmp
  858 
  859   dave> get index* /tmp                   # Gets index*.html, index*.txt, etc.
  860   dave> get index*.html /tmp/index1.html  # Gets index*.html
  861   dave> get index[12].htm?                # Gets file1 and file2, .htm and .html
  862 
  863 =item B<? [CMD]>
  864 
  865 =item B<h [CMD]>
  866 
  867 =item B<help [CMD]>
  868 
  869 prints list of commands or help for CMD
  870 
  871   dave> ?
  872 
  873   dave> help get
  874 
  875 Aliases: ?, h
  876 
  877 =item B<lcd [DIR]>
  878 
  879 changes local directory
  880 
  881   dave> lcd /tmp
  882 
  883 =item B<lls [DIR]>
  884 
  885 lists local directory contents
  886 
  887   dave> lcd /tmp
  888   dave> lls
  889   dave> lls /home
  890 
  891 This command simply execs the local ls command and is 
  892 equivalent to "!ls"
  893 
  894 =item B<dir [URL]>
  895 
  896 =item B<ls [URL]>
  897 
  898 lists remote directory contents or file props
  899 
  900   dave> ls
  901   Listing of http://host.org/dav_dir/
  902                   ./          Aug 29 02:26  <dir>
  903      mtx_0.04.tar.gz   52640  Aug 11 11:45
  904           index.html    4580  Aug 11 11:45
  905       index0.04.html    4936  Nov 11  2000
  906               mydir/          Aug 19 21:14  <dir>,<locked>
  907 
  908   dave> ls index.html
  909   URL: http://www.webdav.org/perldav/index.html
  910   Content-type: text/html
  911   Creation date: Sun Aug 12 21:58:02 2001
  912   Last modified:
  913   Size: 4580 bytes
  914   Locks supported: write/exclusive write/shared
  915   Locks:
  916 
  917 Use propfind to get a similar printout of a 
  918 collection (directory).
  919 
  920 Aliases: dir
  921 
  922 =item B<lock [URL [TIMEOUT] [DEPTH]]>
  923 
  924 locks a resource 
  925 
  926 Without a URL you will lock the current remote 
  927 collection.
  928 
  929 TIMEOUT can be any of the following formats:
  930              30s          30 seconds from now
  931              10m          ten minutes from now
  932              1h           one hour from now
  933              1d           tomorrow
  934              3M           in three months
  935              10y          in ten years time
  936              2000-02-31 00:40:33
  937              2000-02-31
  938 
  939 Default is an infinite timeout
  940 
  941 See perldoc C<HTTP::DAV::Resource> for more information 
  942 about timeouts.
  943 
  944 DEPTH can be either "0" or "infinity" (default)
  945 
  946 Seeting the lock Scope and Type is not currently 
  947 implemented. Let me know if you need it as it shouldn't 
  948 be too much effort.
  949 
  950 =item B<mkdir URL>
  951 
  952 =item B<mkcol URL>
  953 
  954 make a remote collection (directory)
  955 
  956   dave> open host.org/dav_dir/
  957   dave> mkcol new_dir
  958   dave> mkdir /dav_dir/new_dir
  959 
  960 Aliases: mkdir
  961 
  962 =item B<mv>
  963 
  964 =item B<move SOURCE_URL DEST_URL>
  965 
  966 moves a remote resource to another
  967 
  968   dave> open host.org/dav_dir/
  969 
  970 Move dir1/ to dav2/
  971 
  972   dave> move dir1 dav2
  973 
  974 Move file dir2/file.txt to ../file.txt
  975 
  976   dave> cd dir2
  977   dave> move file.txt ..
  978 
  979 Move file.txt to dav2/new_file.txt
  980 
  981   dave> move file.txt dav2/new_file.txt
  982 
  983 Aliases: mv
  984 
  985 =item B<open URL>
  986 
  987 connects to the WebDAV-enabled server at URL
  988 
  989    dave> open host.org/dav_dir/
  990 
  991 Note that if authorization details are required you will 
  992 be prompted for them.
  993 
  994 https and Digest authorization are not currently 
  995 supported. Please let me know if you need it.
  996 
  997 
  998 =item B<options [URL]>
  999 
 1000 show the HTTP methods allowed for a URL
 1001 
 1002    dave> options index.html
 1003    OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPFIND, 
 1004    PROPPATCH, COPY, MOVE, LOCK, UNLOCK
 1005 
 1006 Note that Microsoft's IIS does not support LOCK on 
 1007 collections (directories). Nor does it support PROPPATCH.
 1008 
 1009 =item B<propfind [URL]>
 1010 
 1011 show the properties of a resource
 1012 
 1013   dave> propfind test
 1014   URL: http://host.org/dav_dir/test/
 1015   Content-type: httpd/unix-directory
 1016   Creation date: Wed Aug 29 00:36:42 2001
 1017   Last modified:
 1018   Size:  bytes
 1019   Locks supported: write/exclusive write/shared
 1020   Locks:
 1021 
 1022 Using ls will get you the same printout if you ls 
 1023 a file. But ls'ing a collection will show you the 
 1024 collections contents.
 1025 
 1026 =item B<put FILE [URL]>
 1027 
 1028 uploads a local file or directory to URL
 1029 or the currently opened location.
 1030 
 1031 If URL is an existing collection then the dir/file will
 1032 be copied INTO that collection.
 1033 
 1034   dave> open host.org/dav_dir/
 1035 
 1036 Recursively put local my_dir/ to host.org/dav_dir/my_dir/:
 1037 
 1038   dave> put my_dir/  
 1039 
 1040 Put local index.html to host.org/dav_dir/index1.html:
 1041 
 1042   dave> put /tmp/index.html index1.html
 1043 
 1044 Put * to remote directory
 1045 
 1046   dave> put *
 1047 
 1048 Put index[12].htm? to remote directory /dav_dir (/dav_dir must exist)
 1049 
 1050   dave> put index[12].htm? /dav_dir
 1051 
 1052 Put index[1234].htm? to remote directory /dav_dir (/dav_dir must exist)
 1053 
 1054   dave> put index[1-4].htm? /dav_dir
 1055 
 1056 Glob types supported are, * (matches any characters), ? (matches any one character), [...] (matches any characters in the set ...). 
 1057 
 1058 =item B<pwd>
 1059 
 1060 prints the currently opened URL (working directory)
 1061 
 1062  dave> open host.org/dav_dir/
 1063  dave> cd new_dir/
 1064  dave> pwd
 1065  http://host.org/dav_dir/new_dir/
 1066 
 1067 =item B<q>
 1068 
 1069 =item B<bye>
 1070 
 1071 =item B<quit>
 1072 
 1073 exits dave
 1074 
 1075 Note that dave does not unlock any locks created 
 1076 during your session.
 1077 
 1078 Aliases: q, quit
 1079 
 1080 =item B<set URL PROPERTY VALUE [NAMESPACE]>
 1081 
 1082 sets a custom property on a resource
 1083 
 1084    dave> set file.txt author "Patrick Collins"
 1085    dave> set file.txt author "Patrick Collins" "mynamespace"
 1086 
 1087 The NAMESPACE by default is "DAV:".
 1088 
 1089 =item B<!>
 1090 
 1091 =item B<sh>
 1092 
 1093 executes a local command (alias !)
 1094 
 1095    dave> sh cat localfile
 1096    dave> !gzip localfile.gz
 1097    dave> ! "cat localfile | less"
 1098 
 1099 Aliases: !
 1100 
 1101 =item B<showlocks>
 1102 
 1103 show my locks on a resource
 1104 
 1105 Shows any locked resources that you've locked in this 
 1106 session.
 1107 
 1108 See C<propfind> if you'd like to see anyone's locks held 
 1109 against a particular resource.
 1110 
 1111 =item B<steal [URL]>
 1112 
 1113 remove ANY locks on a resource
 1114 
 1115 Useful if you accidentally forgot to unlock a resource 
 1116 from a previous session or if you think that somebody 
 1117 has forgotten to unlock a resource. 
 1118 
 1119 =item B<unlock [URL]>
 1120 
 1121 unlocks a resource
 1122 
 1123 Note that unlock will only unlock locks that you have 
 1124 created. Use steal if you want to forcefully unlock 
 1125 somebody else's lock.
 1126 
 1127 =item B<unset URL PROPERTY [NAMESPACE]>
 1128 
 1129 unsets a property from a resource
 1130 
 1131    dave> unset file.txt author
 1132    dave> unset file.txt author "mynamespace"
 1133 
 1134 The NAMESPACE by default is "DAV:".
 1135 
 1136 =back
 1137 
 1138 =head1 GETTING HELP
 1139 
 1140 The perldav mailing list
 1141 There is a mailing list for PerlDAV and dave for use by Developers and Users.
 1142 Please see http://mailman.webdav.org/mailman/listinfo/perldav
 1143 
 1144 =head1 INSTALLATION
 1145 
 1146 dave is installed to /usr/local/bin by default when you 
 1147 install the PerlDAV library. See 
 1148 http://www.webdav.org/perldav/ for installation details 
 1149 of PerlDAV.
 1150 
 1151 =head1 WHAT PLATFORMS WILL IT WORK ON?
 1152 
 1153 dave is pure perl so only needs Perl5.003 (or later) and 
 1154 the C<PerlDAV> library to be installed. 
 1155 
 1156 I have not ported dave to Windows but would like somebody 
 1157 to have a shot at it.
 1158 
 1159 =head1 SEE ALSO
 1160 
 1161 The C<PerlDAV> perl API at http://www.webdav.org/perldav/
 1162 or by typing "perldoc HTTP::DAV" on your command line.
 1163 
 1164 =head1 AUTHOR AND COPYRIGHT
 1165 
 1166 This module is Copyright (C) 2001 by
 1167 
 1168     Patrick Collins
 1169     G03 Gloucester Place, Kensington
 1170     Sydney, Australia
 1171 
 1172     Email: pcollins@cpan.org
 1173     Phone: +61 2 9663 4916
 1174 
 1175 All rights reserved.
 1176 
 1177 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.
 1178 
 1179 =head1 MAINTAINER
 1180 
 1181 Cosimo Streppone, E<lt>cosimo@cpan.orgE<gt>
 1182 
 1183 =cut