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 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,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'davclient'
4
+ require 'davclient/davcli'
5
+
6
+ DavCLI.dav(ARGV)
@@ -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
+