davclient 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +76 -0
- data/bin/dav +6 -0
- data/lib/davclient/curl_commands.rb +73 -0
- data/lib/davclient/dav-ls.rb +88 -0
- data/lib/davclient/dav-propfind.rb +92 -0
- data/lib/davclient/dav-put.rb +71 -0
- data/lib/davclient/davcli.rb +166 -0
- data/lib/davclient/hpricot_extensions.rb +118 -0
- data/lib/davclient.rb +451 -0
- metadata +83 -0
data/README.rdoc
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= DavClient
|
2
|
+
|
3
|
+
* http://davclient.rubyforge.org
|
4
|
+
* http://rubyforge.org/mailman/listinfo/davclient-general
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
WebDAV command line client written in Ruby for managing content on
|
9
|
+
webservers that support the WebDAV extensions.
|
10
|
+
|
11
|
+
== Requirements
|
12
|
+
|
13
|
+
The command line utility curl installed and available in your unix path.
|
14
|
+
|
15
|
+
== LIRBRARY SYNOPSIS:
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'davclient'
|
19
|
+
|
20
|
+
# Print url of all files in webdav folder recursively
|
21
|
+
# with basic tree walking
|
22
|
+
|
23
|
+
url = 'http://test.webdav.org/dav/'
|
24
|
+
WebDAV.find(url, :recursive => true) do |item|
|
25
|
+
puts item.href
|
26
|
+
end
|
27
|
+
|
28
|
+
== COMMAND LINE UTILITIES:
|
29
|
+
|
30
|
+
DavClient includes the command line utility 'dav'. It is inspired by git and should be familiar to unix users.
|
31
|
+
The command 'dav cd url' sets current working url, 'dav ls' list files and 'dav pwd'
|
32
|
+
prints current working url. , users can access files, folders and their properties on webdav servers.
|
33
|
+
|
34
|
+
The only authentication method supported at the moment, is by reading usernames and passwords from
|
35
|
+
a file named ~/.netrc. If username is missing, the 'dav' command will print out detailed instructions
|
36
|
+
on how to add username etc. to the '~/.netrc' file.
|
37
|
+
|
38
|
+
== COMMAND LINE SYNOPSIS:
|
39
|
+
|
40
|
+
>dav cd http://test.webdav.org/dav/
|
41
|
+
http://test.webdav.org/dav/
|
42
|
+
>dav ls
|
43
|
+
images/
|
44
|
+
index.html
|
45
|
+
>dav pwd
|
46
|
+
http://test.webdav.org/dav/
|
47
|
+
|
48
|
+
== INSTALL:
|
49
|
+
|
50
|
+
[sudo] gem install davclient
|
51
|
+
|
52
|
+
or
|
53
|
+
|
54
|
+
git clone git://github.com/thomasfl/davclient.git
|
55
|
+
cd davclient
|
56
|
+
gem build Rakefile
|
57
|
+
sudo gem install davclient-x.x.x.gem
|
58
|
+
|
59
|
+
Curl comes preinstalled on Mac OS X. It can be downloaded from
|
60
|
+
http://curl.haxx.se/ . If you are using debian or ubuntu, it
|
61
|
+
can be installed by:
|
62
|
+
|
63
|
+
sudo apt-get install curl
|
64
|
+
|
65
|
+
== Background:
|
66
|
+
|
67
|
+
There has been posted a few examples on the web of how to make a WebDAV client.
|
68
|
+
The problem is that they all seem to support only one type of username and password
|
69
|
+
authentication. DavClient instead uses the command line tool 'curl' to do all the
|
70
|
+
authentication and networking. To avoid handling authentication all togheter, curl
|
71
|
+
are told to look up all usernames and passwords are in a file named ~/.netrc.
|
72
|
+
|
73
|
+
The command 'dav' is non-interactive and inspired by git, making it suitable for
|
74
|
+
use in scripts. If you can script in Ruby, the examples folder includes sample
|
75
|
+
scripts using the davclient Ruby library.
|
76
|
+
|
data/bin/dav
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
def remove_newlines(string)
|
4
|
+
string.gsub("\n","").gsub(/ +/," ") + " "
|
5
|
+
end
|
6
|
+
|
7
|
+
# Templates for curl commands:
|
8
|
+
curl_propfind_cmd = <<EOF
|
9
|
+
#{$curl}
|
10
|
+
--request PROPFIND
|
11
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
12
|
+
--header "Depth: 1"
|
13
|
+
--data-ascii '<?xml version="1.0" encoding="utf-8"?>
|
14
|
+
<DAV:propfind xmlns:DAV="DAV:"><DAV:allprop/></DAV:propfind>'
|
15
|
+
--netrc
|
16
|
+
EOF
|
17
|
+
CURL_PROPFIND = remove_newlines(curl_propfind_cmd)
|
18
|
+
|
19
|
+
curl_proppatch_cmd = <<EOF
|
20
|
+
#{$curl}
|
21
|
+
--request PROPPATCH
|
22
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
23
|
+
--header "Depth: 1"
|
24
|
+
--data-ascii '<?xml version="1.0"?>
|
25
|
+
<d:propertyupdate xmlns:d="DAV:" xmlns:v="vrtx">
|
26
|
+
<d:set>
|
27
|
+
<d:prop>
|
28
|
+
<!--property-and-value-->
|
29
|
+
</d:prop>
|
30
|
+
</d:set>
|
31
|
+
</d:propertyupdate>'
|
32
|
+
--netrc
|
33
|
+
EOF
|
34
|
+
CURL_PROPPATCH = remove_newlines(curl_proppatch_cmd)
|
35
|
+
|
36
|
+
curl_delete_cmd = <<EOF
|
37
|
+
#{$curl}
|
38
|
+
--request DELETE
|
39
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
40
|
+
--netrc
|
41
|
+
EOF
|
42
|
+
CURL_DELETE = remove_newlines(curl_delete_cmd)
|
43
|
+
|
44
|
+
curl_mkcol_cmd = <<EOF
|
45
|
+
#{$curl}
|
46
|
+
--request MKCOL
|
47
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
48
|
+
--netrc
|
49
|
+
EOF
|
50
|
+
CURL_MKCOL = remove_newlines(curl_mkcol_cmd)
|
51
|
+
|
52
|
+
CURL_OPTIONS = "#{$curl} -i -X OPTIONS --netrc "
|
53
|
+
|
54
|
+
curl_copy = <<EOF
|
55
|
+
#{$curl}
|
56
|
+
--request COPY
|
57
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
58
|
+
--header 'Destination: <!--destination-->'
|
59
|
+
--netrc
|
60
|
+
EOF
|
61
|
+
|
62
|
+
CURL_COPY = remove_newlines(curl_copy)
|
63
|
+
|
64
|
+
|
65
|
+
curl_move = <<EOF
|
66
|
+
#{$curl}
|
67
|
+
--request MOVE
|
68
|
+
--header 'Content-Type: text/xml; charset="utf-8"'
|
69
|
+
--header 'Destination: <!--destination-->'
|
70
|
+
--netrc
|
71
|
+
EOF
|
72
|
+
|
73
|
+
CURL_MOVE = remove_newlines(curl_move)
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# WebDav ls command line utility
|
2
|
+
# Synopsis:
|
3
|
+
# dav ls [options][url]
|
4
|
+
#
|
5
|
+
# or standalone:
|
6
|
+
#
|
7
|
+
# ruby dav-ls [options][url]
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'davclient'
|
11
|
+
require 'optparse'
|
12
|
+
|
13
|
+
class LsCLI
|
14
|
+
|
15
|
+
def self.ls(args)
|
16
|
+
options = read_options(args)
|
17
|
+
url = args[0]
|
18
|
+
tmp_cwurl = WebDAV.CWURL
|
19
|
+
if(not url)then
|
20
|
+
url = WebDAV.CWURL
|
21
|
+
if(not url)then
|
22
|
+
puts "#{$0} ls: no current working url"
|
23
|
+
puts "Usage: Use '#{$0} cd [url|dir] to set current working url"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
else
|
27
|
+
WebDAV.cd(url)
|
28
|
+
end
|
29
|
+
|
30
|
+
url = WebDAV.CWURL
|
31
|
+
WebDAV.find(url, :recursive => false ) do |item|
|
32
|
+
if(options[:showUrl])then
|
33
|
+
puts item.href
|
34
|
+
elsif(options[:longFormat])
|
35
|
+
|
36
|
+
else
|
37
|
+
print item.basename
|
38
|
+
print "/" if item.isCollection?
|
39
|
+
puts
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Restore CWURL
|
44
|
+
WebDAV.cd(tmp_cwurl)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def self.read_options(args)
|
50
|
+
options = {}
|
51
|
+
|
52
|
+
optparse = OptionParser.new do|opts|
|
53
|
+
opts.banner = "Usage: #{$0} ls [options] url"
|
54
|
+
|
55
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
options[:longFormat] = false
|
61
|
+
opts.on( '-l', "List in long format" ) do
|
62
|
+
options[:longFormat] = true
|
63
|
+
end
|
64
|
+
|
65
|
+
options[:showUrl] = false
|
66
|
+
opts.on('-a', "Include full url in names.") do
|
67
|
+
options[:showUrl] = true
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
begin
|
73
|
+
optparse.parse! args
|
74
|
+
rescue
|
75
|
+
puts "Error: " + $!
|
76
|
+
puts optparse
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
return options
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# Make this file an executable script
|
86
|
+
if $0 == __FILE__
|
87
|
+
LsCLI.ls(ARGV)
|
88
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# Handle 'dav propfind [URL]' command
|
2
|
+
require 'rubygems'
|
3
|
+
require 'davclient'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
class PropfindCLI
|
7
|
+
|
8
|
+
def self.propfind(args)
|
9
|
+
options = read_options(args)
|
10
|
+
|
11
|
+
url = args[0]
|
12
|
+
if(not(url)) then
|
13
|
+
url = WebDAV.CWURL
|
14
|
+
end
|
15
|
+
|
16
|
+
if(not(url)) then
|
17
|
+
puts "Error: Missing mandatory url"
|
18
|
+
puts optparse
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
if(options[:xml])then
|
23
|
+
puts WebDAV.propfind(url, :xml => true)
|
24
|
+
else
|
25
|
+
|
26
|
+
# TODO This is experimental code in desperat need
|
27
|
+
# of love and attention
|
28
|
+
item = WebDAV.propfind(url)
|
29
|
+
puts item.collection
|
30
|
+
|
31
|
+
prev_url = nil
|
32
|
+
WebDAV.find(url, :children => options[:children]) do | url, item |
|
33
|
+
if(prev_url != url) then
|
34
|
+
puts
|
35
|
+
puts "url = " + url.to_s
|
36
|
+
prev_url = url
|
37
|
+
end
|
38
|
+
|
39
|
+
name = item.prefix
|
40
|
+
if(item.namespace)then
|
41
|
+
name = name + "(" + item.namespace + ")"
|
42
|
+
end
|
43
|
+
name = name + item.name
|
44
|
+
puts name.ljust(40) + " = '" + item.text.to_s + "'"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.read_options(args)
|
54
|
+
options = {}
|
55
|
+
|
56
|
+
title = nil
|
57
|
+
optparse = OptionParser.new do|opts|
|
58
|
+
opts.banner = "Usage: #{$0} propfind [options] url"
|
59
|
+
|
60
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
61
|
+
puts opts
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
|
65
|
+
options[:xml] = true
|
66
|
+
opts.on( '-p', '--pretty', "Pretty print output instead of returning xml" ) do
|
67
|
+
options[:xml] = false
|
68
|
+
end
|
69
|
+
|
70
|
+
options[:children] = false
|
71
|
+
opts.on('-c', '--children', "Show children if viewing collection (folder)") do
|
72
|
+
options[:children] = true
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
begin
|
78
|
+
optparse.parse!
|
79
|
+
rescue
|
80
|
+
puts "Error: " + $!
|
81
|
+
puts optparse
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
|
85
|
+
return options
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
if $0 == __FILE__
|
91
|
+
PropfindCLI.propfind(ARGV)
|
92
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# WebDav put command line utility
|
2
|
+
# Synopsis:
|
3
|
+
# dav put [options][url]
|
4
|
+
#
|
5
|
+
# or standalone:
|
6
|
+
#
|
7
|
+
# ruby dav-ls [options][url]
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'davclient'
|
11
|
+
require 'optparse'
|
12
|
+
|
13
|
+
class PutCLI
|
14
|
+
|
15
|
+
def self.put(args)
|
16
|
+
options = read_options(args)
|
17
|
+
url = args[0]
|
18
|
+
if(options[:string])then
|
19
|
+
begin
|
20
|
+
WebDAV.put_string(url,options[:string])
|
21
|
+
rescue
|
22
|
+
puts $0 + ": " + $!
|
23
|
+
end
|
24
|
+
puts "Published content to " + url
|
25
|
+
else
|
26
|
+
puts "PUT file not implemented"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.usage()
|
34
|
+
puts "Usage: #{$0} put --string \"<html>..</html>\" url"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.read_options(args)
|
38
|
+
options = {}
|
39
|
+
|
40
|
+
optparse = OptionParser.new do|opts|
|
41
|
+
opts.banner = "Usage: #{$0} put [options] url"
|
42
|
+
|
43
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
44
|
+
puts opts
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
|
48
|
+
options[:string] = false
|
49
|
+
opts.on( '-s', '--string', "Put contents of string" ) do |str |
|
50
|
+
options[:string] = str
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
optparse.parse! args
|
57
|
+
rescue
|
58
|
+
puts "Error: " + $!
|
59
|
+
puts optparse
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
return options
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# Make this file an executable script
|
69
|
+
if $0 == __FILE__
|
70
|
+
PutCLI.put(ARGV)
|
71
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'davclient/dav-ls'
|
2
|
+
require 'davclient/dav-put'
|
3
|
+
require 'davclient/dav-propfind'
|
4
|
+
|
5
|
+
# Handle the 'dav' command line commands
|
6
|
+
|
7
|
+
class DavCLI
|
8
|
+
|
9
|
+
def self.cat(args)
|
10
|
+
if(args.size == 1)
|
11
|
+
url = args[0]
|
12
|
+
puts WebDAV.get(url)
|
13
|
+
else
|
14
|
+
puts "Illegal arguments: " + args[1..100].join(" ")
|
15
|
+
puts "#{$0}: usage '#{$0} cat [url|filename]"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.pwd(args)
|
20
|
+
cwurl = WebDAV.CWURL
|
21
|
+
if(cwurl)
|
22
|
+
puts cwurl
|
23
|
+
else
|
24
|
+
puts "#{$0}: No working url set. Use 'dav cd url' to set url"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.cd(args)
|
30
|
+
url = args[0]
|
31
|
+
if(url == nil)then
|
32
|
+
puts "#{$0} cd: Missing mandatory url."
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
begin
|
36
|
+
WebDAV.cd(url)
|
37
|
+
puts "Changing WebDAV URL to: " + WebDAV.CWURL
|
38
|
+
rescue Exception => exception
|
39
|
+
puts exception
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.mkcol(args)
|
44
|
+
if(args.size == 1 )
|
45
|
+
WebDAV.mkcol(args[0])
|
46
|
+
else
|
47
|
+
puts "#{$0}: usage '#{$0} mkcol url|path"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.delete(args)
|
52
|
+
if(args.size == 1)
|
53
|
+
url = WebDAV.delete(args[0])
|
54
|
+
puts "#{$0} delete: Deleted '#{url}'"
|
55
|
+
else
|
56
|
+
puts "#{$0}: usage '#{$0} delete url|path"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.options(args)
|
61
|
+
if(args.size == 0 or args.size == 1)
|
62
|
+
puts WebDAV.options(args[0])
|
63
|
+
else
|
64
|
+
puts "#{$0}: usage '#{$0} options [url]"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.propfind(args)
|
69
|
+
PropfindCLI.propfind(args)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.ls(args)
|
73
|
+
LsCLI.ls(args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.cp(args)
|
77
|
+
if(args.size == 2 )
|
78
|
+
WebDAV.cp(args[0], args[1])
|
79
|
+
else
|
80
|
+
puts "#{$0}: usage '#{$0} cp src dest"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def self.mv(args)
|
86
|
+
if(args.size == 2 )
|
87
|
+
WebDAV.mv(args[0], args[1])
|
88
|
+
else
|
89
|
+
puts "#{$0}: usage '#{$0} copy mv dest"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.print_dav_usage
|
94
|
+
puts "usage: #{$0} COMMAND [ARGS]"
|
95
|
+
puts ""
|
96
|
+
puts "Available #{$0} commands:"
|
97
|
+
puts " ls List files on webdav server"
|
98
|
+
puts " pwd Print current working url"
|
99
|
+
puts " cd Change current working url"
|
100
|
+
puts " cp Copy resource"
|
101
|
+
puts " mv Move resource"
|
102
|
+
puts " rm Remove resource"
|
103
|
+
puts " cat Print content of resource"
|
104
|
+
puts " propfind Print webdav properties for url"
|
105
|
+
puts " mkcol Make collection"
|
106
|
+
puts " options Display webservers WebDAV options"
|
107
|
+
puts ""
|
108
|
+
puts "See '#{$0} COMMAND -h' for more information on a specific command."
|
109
|
+
exit
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.dav(args)
|
113
|
+
command = args[0]
|
114
|
+
|
115
|
+
if(command == "-h" or command =~ /help/ or command =~ /\?/) then
|
116
|
+
print_dav_usage
|
117
|
+
end
|
118
|
+
|
119
|
+
if(command == "-v" or command =~ /version/ ) then
|
120
|
+
puts "#{$0} version " + WebDAV.version
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
|
124
|
+
args = args[1..100]
|
125
|
+
case command
|
126
|
+
when "cat" then
|
127
|
+
cat(args)
|
128
|
+
when "ls" then
|
129
|
+
LsCLI.ls(args)
|
130
|
+
when "pwd"
|
131
|
+
pwd(args)
|
132
|
+
when "cd"
|
133
|
+
cd(args)
|
134
|
+
when "cp"
|
135
|
+
cp(args)
|
136
|
+
when "copy"
|
137
|
+
cp(args)
|
138
|
+
when "mv"
|
139
|
+
mv(args)
|
140
|
+
when "move"
|
141
|
+
mv(args)
|
142
|
+
when "mkcol"
|
143
|
+
mkcol(args)
|
144
|
+
when "mkdir"
|
145
|
+
mkcol(args)
|
146
|
+
when "put"
|
147
|
+
PutCLI.put(args)
|
148
|
+
when "delete"
|
149
|
+
delete(args)
|
150
|
+
when "del"
|
151
|
+
delete(args)
|
152
|
+
when "rm"
|
153
|
+
delete(args)
|
154
|
+
when "propfind"
|
155
|
+
propfind(args)
|
156
|
+
when "props"
|
157
|
+
propfind(args)
|
158
|
+
when "options"
|
159
|
+
options(args)
|
160
|
+
else
|
161
|
+
puts "Unknown command :'" + command + "'"
|
162
|
+
print_dav_usage
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hpricot'
|
5
|
+
|
6
|
+
# Extensions to the Hpricot XML parser.
|
7
|
+
module Hpricot
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
class Elem
|
12
|
+
|
13
|
+
# Makes properties available as simple method calls.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# print item.creationdate()
|
17
|
+
def method_missing(method_name, *args)
|
18
|
+
if(args.size == 0) then
|
19
|
+
return property(method_name.to_s)
|
20
|
+
end
|
21
|
+
raise "Method missing"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Resource url
|
25
|
+
def href()
|
26
|
+
self.at("d:href").innerText
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns true of resource is a collection, i.e. a folder and not a file.
|
30
|
+
def isCollection?()
|
31
|
+
self.at("d:collection") != nil
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO Not used. Delete???
|
37
|
+
def type_convert_value(value)
|
38
|
+
if(returnValue == "true")then
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
if(returnValue == "false")then
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
# Number format???
|
45
|
+
## Dato format
|
46
|
+
return returnValue
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: Make list of recognized namespace prefixes configurable
|
50
|
+
# Get property.
|
51
|
+
# Example:
|
52
|
+
# page = WebDAV.find(url)
|
53
|
+
# print page.property("published-date")
|
54
|
+
def property(name)
|
55
|
+
|
56
|
+
property = property = self.at(name)
|
57
|
+
if(property)then
|
58
|
+
returnValue = property.innerText
|
59
|
+
return returnValue
|
60
|
+
end
|
61
|
+
|
62
|
+
property = property = self.at(name.downcase)
|
63
|
+
if(property)then
|
64
|
+
return property.innerText
|
65
|
+
end
|
66
|
+
|
67
|
+
vrtx_property = self.at("v:" + name)
|
68
|
+
if(vrtx_property)then
|
69
|
+
return vrtx_property.innerText
|
70
|
+
end
|
71
|
+
|
72
|
+
vrtx_property = self.at("v:" + name.downcase)
|
73
|
+
if(vrtx_property)then
|
74
|
+
return vrtx_property.innerText
|
75
|
+
end
|
76
|
+
|
77
|
+
dav_property = self.at("d:" +name)
|
78
|
+
if( dav_property)then
|
79
|
+
return dav_property.innerText
|
80
|
+
end
|
81
|
+
|
82
|
+
dav_property = self.at("d:" +name.downcase)
|
83
|
+
if( dav_property)then
|
84
|
+
return dav_property.innerText
|
85
|
+
end
|
86
|
+
|
87
|
+
return nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def basename
|
91
|
+
File.basename(self.at("d:href").innerText)
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: Move to vortex_lib.rb
|
95
|
+
def dateProperty(name)
|
96
|
+
date = self.property(name)
|
97
|
+
if(date =~ /\dZ$/)then
|
98
|
+
# Fix for bug in vortex:
|
99
|
+
#
|
100
|
+
# Some date properties are in xmlshcema datetime format, but
|
101
|
+
# all tough the time seems to be localtime the timezone is
|
102
|
+
# specified as Z not CEST. Fix is to set timezone and add
|
103
|
+
# 2 hours.
|
104
|
+
date = date.gsub(/\dZ$/," CEST")
|
105
|
+
time = Time.parse(date)
|
106
|
+
time = time + (60 * 60 * 2)
|
107
|
+
return time
|
108
|
+
end
|
109
|
+
time = Time.parse(date)
|
110
|
+
return time
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set the items WebDAV properties. Properties must be a string with XML.
|
114
|
+
def proppatch(properties)
|
115
|
+
WebDAV.proppatch(href, properties)
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/lib/davclient.rb
ADDED
@@ -0,0 +1,451 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'hpricot'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'open3'
|
6
|
+
require 'pathname'
|
7
|
+
require 'davclient/hpricot_extensions'
|
8
|
+
|
9
|
+
# :stopdoc:
|
10
|
+
|
11
|
+
# Path to curl executable:
|
12
|
+
$curl = "curl"
|
13
|
+
|
14
|
+
require 'davclient/curl_commands'
|
15
|
+
|
16
|
+
# :startdoc:
|
17
|
+
|
18
|
+
# WebDAV client
|
19
|
+
module WebDAV
|
20
|
+
|
21
|
+
# :stopdoc:
|
22
|
+
VERSION = '0.0.5'
|
23
|
+
# :startdoc:
|
24
|
+
|
25
|
+
# Returns the version string for the library.
|
26
|
+
#
|
27
|
+
def self.version
|
28
|
+
VERSION
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns current working url. Used by command line utilites
|
32
|
+
def self.CWURL
|
33
|
+
return $CWURL if($CWURL) # Used by tests
|
34
|
+
cwurl = nil
|
35
|
+
filename = cwurl_filename
|
36
|
+
if(File.exists?(filename))
|
37
|
+
File.open(filename, 'r') {|f| cwurl = f.read() }
|
38
|
+
end
|
39
|
+
return cwurl
|
40
|
+
end
|
41
|
+
|
42
|
+
# Make relative url absolute. Returns error if no current working
|
43
|
+
# url has been set.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
#
|
47
|
+
# WebDAV.cd("https://example.org/subfolder")
|
48
|
+
# print WebDAV.absoluteUrl("..") => "https://example.org/"
|
49
|
+
def self.absoluteUrl(url)
|
50
|
+
if(not(url =~ /^http.?:\/\//))then
|
51
|
+
cwurl = Pathname.new(self.CWURL)
|
52
|
+
cwurl = cwurl + url
|
53
|
+
url = cwurl.to_s
|
54
|
+
# url = url + "/" if(not(url =~ /\/$/))
|
55
|
+
|
56
|
+
if(not(url =~ /^http.?:\/\//))then
|
57
|
+
warn "#{$0}: Error: illegal url: " + url
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return url
|
62
|
+
end
|
63
|
+
|
64
|
+
# Change current working url. Takes relative pathnames.
|
65
|
+
#
|
66
|
+
# Examples:
|
67
|
+
#
|
68
|
+
# WebDAV.cd("http://www.example.org")
|
69
|
+
#
|
70
|
+
# WebDAV.cd("../folder")
|
71
|
+
def self.cd(url)
|
72
|
+
url = absoluteUrl(url)
|
73
|
+
url = url + "/" if(not(url =~ /\/$/))
|
74
|
+
|
75
|
+
resource = WebDAV.propfind(url)
|
76
|
+
if(resource and resource.isCollection?)then
|
77
|
+
WebDAV.CWURL = url
|
78
|
+
else
|
79
|
+
# TODO Make proper exception
|
80
|
+
raise Exception, "cd: URL '#{cwurl} is not a WebDAV collection."
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Sets current working url by storing url in a tempfile with parent process pid
|
85
|
+
# as part of the filename.
|
86
|
+
def self.CWURL=(url)
|
87
|
+
$CWURL = url # Used by tests
|
88
|
+
File.open(cwurl_filename, 'w') {|f| f.write(url) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get content of resource as string
|
92
|
+
#
|
93
|
+
# Example:
|
94
|
+
#
|
95
|
+
# html = WebDAV.get(url)
|
96
|
+
#
|
97
|
+
# html = WebDAV.get("file_in_current_working_folder.html")
|
98
|
+
def self.get(url)
|
99
|
+
url = absoluteUrl(url)
|
100
|
+
|
101
|
+
curl_command = "#{$curl} --netrc " + url
|
102
|
+
return exec_curl(curl_command)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Set WebDAV properties for url as xml.
|
106
|
+
#
|
107
|
+
# Example:
|
108
|
+
#
|
109
|
+
# WebDAV.proppatch("https://dav.webdav.org/folder","<contentLastModified>2007-12-12 12:00:00 GMT</contentLastModified>
|
110
|
+
def self.proppatch(href, property)
|
111
|
+
curl_command = CURL_PROPPATCH + " \""+href+"\""
|
112
|
+
curl_command = curl_command.gsub("<!--property-and-value-->",property)
|
113
|
+
response = exec_curl(curl_command)
|
114
|
+
if(not(response =~ /200 OK/)) then
|
115
|
+
puts "Error:\nRequest:\n" + curl_command + "\n\nResponse: " + response
|
116
|
+
exit(0)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get WebDAV properties
|
121
|
+
#
|
122
|
+
# Examples:
|
123
|
+
# item = propfind(url) - Returns a Hpricot::Elem object
|
124
|
+
#
|
125
|
+
# xml = propfind(url, :xml => true) - Returns xml for debugging.
|
126
|
+
def self.propfind(*args)
|
127
|
+
url = args[0]
|
128
|
+
url = absoluteUrl(url)
|
129
|
+
options = args[1]
|
130
|
+
|
131
|
+
curl_command = CURL_PROPFIND + " \"" + url + "\""
|
132
|
+
response = exec_curl(curl_command)
|
133
|
+
|
134
|
+
if(response == "")then
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
|
138
|
+
if(not(response =~ /200 OK/)) then
|
139
|
+
puts "Error:\nRequest:\n" + curl_command + "\n\nResponse: " + response
|
140
|
+
exit(0)
|
141
|
+
end
|
142
|
+
|
143
|
+
if(options and options[:xml])then
|
144
|
+
return response
|
145
|
+
end
|
146
|
+
doc = Hpricot( response )
|
147
|
+
items_filtered = Array.new()
|
148
|
+
items = doc.search("//d:response").reverse
|
149
|
+
items.each do |item|
|
150
|
+
|
151
|
+
# Only return root item if folder
|
152
|
+
if(item.href == url or item.href == url + "/" ) then
|
153
|
+
return item
|
154
|
+
end
|
155
|
+
end
|
156
|
+
return nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# Find files and folders.
|
160
|
+
#
|
161
|
+
# Examples:
|
162
|
+
#
|
163
|
+
# result = find( url )
|
164
|
+
#
|
165
|
+
# result = find( url, :type => "collection" ,:recursive => true)
|
166
|
+
#
|
167
|
+
# You can also pass a block of code:
|
168
|
+
#
|
169
|
+
# find( url, :type => "collection" ,:recursive => true) do |folder|
|
170
|
+
# puts folder.href
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
def self.find(*args, &block)
|
174
|
+
href = args[0]
|
175
|
+
options = args[1]
|
176
|
+
type = nil
|
177
|
+
recursive = false
|
178
|
+
if(options)then
|
179
|
+
|
180
|
+
if(options[:type])then
|
181
|
+
type = options[:type]
|
182
|
+
end
|
183
|
+
if(options[:recursive])then
|
184
|
+
recursive = options[:recursive]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
dav_xml_output = propfind(href, :xml => true)
|
188
|
+
if(not(dav_xml_output))then
|
189
|
+
return nil
|
190
|
+
end
|
191
|
+
|
192
|
+
doc = Hpricot( dav_xml_output )
|
193
|
+
items_filtered = Array.new()
|
194
|
+
items = doc.search("//d:response").reverse
|
195
|
+
|
196
|
+
# filter items
|
197
|
+
items.each do |item|
|
198
|
+
|
199
|
+
# Ignore info about root item (file or folder)
|
200
|
+
if(item.href != href) then
|
201
|
+
|
202
|
+
if(type == nil)then
|
203
|
+
# No filters
|
204
|
+
items_filtered.push(item)
|
205
|
+
if(block) then
|
206
|
+
yield item
|
207
|
+
end
|
208
|
+
|
209
|
+
else
|
210
|
+
# Filter result set
|
211
|
+
if((type == "collection" or type == "folder") and item.collection )then
|
212
|
+
items_filtered.push(item)
|
213
|
+
if(block) then
|
214
|
+
yield item
|
215
|
+
end
|
216
|
+
end
|
217
|
+
if(type == "file" and item.collection == false )then
|
218
|
+
items_filtered.push(item)
|
219
|
+
if(block) then
|
220
|
+
yield item
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
if(recursive)then
|
229
|
+
items_filtered.each do |item|
|
230
|
+
if(item.collection && item.href != args[0])then
|
231
|
+
result = find(item.href, args[1], &block)
|
232
|
+
if(result != nil)
|
233
|
+
items_filtered.concat( result)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
return items_filtered
|
240
|
+
end
|
241
|
+
|
242
|
+
# Make collection
|
243
|
+
# Accepts relative url's
|
244
|
+
def self.mkcol(*args) # url, props)
|
245
|
+
url = args[0]
|
246
|
+
props = args[3]
|
247
|
+
url = absoluteUrl(url)
|
248
|
+
curl_command = CURL_MKCOL + " " + url
|
249
|
+
response = exec_curl(curl_command)
|
250
|
+
|
251
|
+
if(props)then
|
252
|
+
proppatch(url,props)
|
253
|
+
end
|
254
|
+
if(response =~ />Created</)then
|
255
|
+
return true
|
256
|
+
end
|
257
|
+
return response
|
258
|
+
end
|
259
|
+
|
260
|
+
# Returns true if resource exists
|
261
|
+
def self.exists?(url)
|
262
|
+
url = absoluteUrl(url)
|
263
|
+
props = WebDAV.propfind(url)
|
264
|
+
if(props.to_s.size == 0)then
|
265
|
+
return false
|
266
|
+
else
|
267
|
+
return true
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Copy resources
|
272
|
+
#
|
273
|
+
# Examples:
|
274
|
+
#
|
275
|
+
# WebDAV.cp("src.html","https://example.org/destination/destination.html"
|
276
|
+
def self.cp(src,dest)
|
277
|
+
srcUrl = absoluteUrl(src)
|
278
|
+
destUrl = absoluteUrl(dest)
|
279
|
+
|
280
|
+
# puts "DEBUG: " + srcUrl + " => " + destUrl
|
281
|
+
curl_command = CURL_COPY.sub("<!--destination-->", destUrl) + " " + srcUrl
|
282
|
+
response = exec_curl(curl_command)
|
283
|
+
|
284
|
+
if(response == "")then
|
285
|
+
return destUrl
|
286
|
+
end
|
287
|
+
return false
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
# Move resources
|
292
|
+
#
|
293
|
+
# Examples:
|
294
|
+
#
|
295
|
+
# WebDAV.mv("src.html","https://example.org/destination/destination.html"
|
296
|
+
def self.mv(src,dest)
|
297
|
+
srcUrl = absoluteUrl(src)
|
298
|
+
destUrl = absoluteUrl(dest)
|
299
|
+
|
300
|
+
# puts "DEBUG: " + srcUrl + " => " + destUrl
|
301
|
+
curl_command = CURL_MOVE.sub("<!--destination-->", destUrl) + " " + srcUrl
|
302
|
+
response = exec_curl(curl_command)
|
303
|
+
|
304
|
+
if(response == "")then
|
305
|
+
return destUrl
|
306
|
+
end
|
307
|
+
return false
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
# Delete resource
|
312
|
+
#
|
313
|
+
# Examples:
|
314
|
+
#
|
315
|
+
# WebDAV.cd("https://example.org/folder")
|
316
|
+
# WebDAV.mkcol("subfolder")
|
317
|
+
# WebDAV.delete("subfolder")
|
318
|
+
def self.delete(url)
|
319
|
+
|
320
|
+
url = absoluteUrl(url)
|
321
|
+
|
322
|
+
curl_command = CURL_DELETE + url
|
323
|
+
response = exec_curl(curl_command)
|
324
|
+
|
325
|
+
if(response == "")then
|
326
|
+
return url
|
327
|
+
end
|
328
|
+
if(not(response =~ /200 OK/)) then
|
329
|
+
puts "Error:\nRequest:\n" + curl_command + "\n\nResponse: " + response
|
330
|
+
return false
|
331
|
+
end
|
332
|
+
return url
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
# Low level WebDAV publish
|
337
|
+
#
|
338
|
+
# Example:
|
339
|
+
#
|
340
|
+
# WebDAV.publish("https://dav.example.org/index.html","<h1>Hello</h1>",nil)
|
341
|
+
def self.publish(url, string, props)
|
342
|
+
self.put_string(url, string)
|
343
|
+
if(props)then
|
344
|
+
self.proppatch(url,props)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
# Puts content of string to file on server with url
|
350
|
+
#
|
351
|
+
# Example:
|
352
|
+
#
|
353
|
+
# WebDAV.put("https://dav.webdav.org/file.html", "<html><h1>Test</h1></html>"
|
354
|
+
def self.put_string(url, html)
|
355
|
+
url = absoluteUrl(url)
|
356
|
+
|
357
|
+
if(url =~ /\/$/)then
|
358
|
+
raise "Error: WebDAV.put_html: url can not be a collection (folder)."
|
359
|
+
end
|
360
|
+
|
361
|
+
tmp_dir = "/tmp/" + rand.to_s[2..10] + "/"
|
362
|
+
FileUtils.mkdir_p tmp_dir
|
363
|
+
tmp_file = tmp_dir + "webdav.tmp"
|
364
|
+
File.open(tmp_file, 'w') {|f| f.write(html) }
|
365
|
+
|
366
|
+
curl_command = "#{$curl} --netrc --silent --upload-file #{tmp_file} #{url}"
|
367
|
+
response = exec_curl(curl_command)
|
368
|
+
if(response != "" and not(response =~ /200 OK/)) then
|
369
|
+
raise "Error:\n WebDAV.put: WebDAV Request:\n" + curl_command + "\n\nResponse: " + response
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Returns a string with the webservers WebDAV options (PUT, PROPFIND, etc.)
|
374
|
+
def self.options(url)
|
375
|
+
if(not(url))
|
376
|
+
url = self.CWURL
|
377
|
+
end
|
378
|
+
return self.exec_curl(CURL_OPTIONS + url )
|
379
|
+
end
|
380
|
+
|
381
|
+
# :stopdoc:
|
382
|
+
|
383
|
+
# TODO put file utility
|
384
|
+
# TESTME
|
385
|
+
def put_file(filename, href)
|
386
|
+
# TODO Detect if href is a collection or not??
|
387
|
+
curl_command = "#{$curl} --netrc --request PUT #{filename} #{href}"
|
388
|
+
return exec_curl(curl_command)
|
389
|
+
# return execute_curl_cmd(curl_put_cmd)
|
390
|
+
end
|
391
|
+
# :startdoc:
|
392
|
+
|
393
|
+
private
|
394
|
+
|
395
|
+
# Returns filename /tmp/cwurl.#pid that holds the current working directory
|
396
|
+
# for the shell's pid
|
397
|
+
def self.cwurl_filename
|
398
|
+
tmp_file = Tempfile.new("dummy").path
|
399
|
+
basename = File.basename(tmp_file)
|
400
|
+
tmp_folder = tmp_file.gsub(basename, "")
|
401
|
+
return tmp_folder + "cwurl." + Process.ppid.to_s
|
402
|
+
end
|
403
|
+
|
404
|
+
# Display instructions for adding credentials to .netrc file
|
405
|
+
def self.display_unauthorized_message(href)
|
406
|
+
puts "Error: 401 Unauthorized: " + href
|
407
|
+
href.match(/^http.*\/\/([^\/]*)/)
|
408
|
+
puts "\nTry adding the following to your ~/.netrc file:"
|
409
|
+
puts ""
|
410
|
+
puts "machine #{$1}"
|
411
|
+
puts " login " + ENV['USER']
|
412
|
+
puts " password ********"
|
413
|
+
puts ""
|
414
|
+
end
|
415
|
+
|
416
|
+
|
417
|
+
# Run 'curl' as a subprocess
|
418
|
+
def self.exec_curl(curl_command)
|
419
|
+
response = ""
|
420
|
+
|
421
|
+
puts curl_command if($DEBUG)
|
422
|
+
|
423
|
+
Open3.popen3(curl_command) do |stdin, stdout, stderr|
|
424
|
+
|
425
|
+
response = stdout.readlines.join("")
|
426
|
+
|
427
|
+
if(response == "")
|
428
|
+
stderr = stderr.readlines.join("").sub(/^\W/,"")
|
429
|
+
if(stderr =~ /command/)
|
430
|
+
puts "Error: " + stderr
|
431
|
+
exit
|
432
|
+
end
|
433
|
+
if(stderr =~ /^curl:/)
|
434
|
+
puts "Error: " + stderr
|
435
|
+
puts
|
436
|
+
puts curl_command
|
437
|
+
puts
|
438
|
+
exit
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
if(response =~ /401 Unauthorized/)then
|
443
|
+
href = curl_command.match( /"(http[^\"]*)"$/ )[0].gsub(/"/,"")
|
444
|
+
self.display_unauthorized_message(href)
|
445
|
+
exit
|
446
|
+
end
|
447
|
+
return response
|
448
|
+
end
|
449
|
+
|
450
|
+
|
451
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: davclient
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Flemming
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-10 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0.6"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: zentest
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "3.5"
|
34
|
+
version:
|
35
|
+
description: WebDAV command line client written in Ruby for managing content on webservers that support the WebDAV extensions.
|
36
|
+
email: thomasfl@usit.uio.no
|
37
|
+
executables:
|
38
|
+
- dav
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- lib/davclient.rb
|
45
|
+
- lib/davclient/hpricot_extensions.rb
|
46
|
+
- lib/davclient/curl_commands.rb
|
47
|
+
- bin/dav
|
48
|
+
- lib/davclient/davcli.rb
|
49
|
+
- lib/davclient/dav-put.rb
|
50
|
+
- lib/davclient/dav-ls.rb
|
51
|
+
- lib/davclient/dav-propfind.rb
|
52
|
+
- README.rdoc
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://folk.uio.no/thomasfl
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements:
|
75
|
+
- cURL command line tool available from http://curl.haxx.se/
|
76
|
+
- Servername, username and password must be supplied in ~/.netrc file.
|
77
|
+
rubyforge_project: davclient
|
78
|
+
rubygems_version: 1.3.5
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Command line WebDAV client and Ruby library.
|
82
|
+
test_files: []
|
83
|
+
|