net_dav 0.3.3 → 0.4.0
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/Rakefile +3 -1
- data/VERSION +1 -1
- data/bin/dav +8 -5
- data/lib/net/dav/item.rb +21 -11
- data/lib/net/dav.rb +314 -209
- data/net_dav.gemspec +12 -6
- data/spec/fixtures/file.html +1 -0
- data/spec/integration/net_dav_spec.rb +96 -0
- data/spec/integration/webdav_server.rb +87 -0
- data/spec/spec_helper.rb +10 -1
- metadata +18 -4
- data/spec/net_dav_spec.rb +0 -7
data/Rakefile
CHANGED
@@ -9,9 +9,11 @@ begin
|
|
9
9
|
gem.description = %Q{WebDAV client library in the style of Net::HTTP, using Net::HTTP and libcurl, if installed}
|
10
10
|
gem.email = "c1.github@niftybox.net"
|
11
11
|
gem.homepage = "http://github.com/devrandom/net_dav"
|
12
|
-
gem.authors = ["Miron Cuperman"]
|
12
|
+
gem.authors = ["Miron Cuperman","Thomas Flemming"]
|
13
|
+
gem.executables = ["dav"]
|
13
14
|
gem.add_dependency "nokogiri", ">= 1.3.0"
|
14
15
|
gem.add_development_dependency "rspec", ">= 1.2.0"
|
16
|
+
gem.add_development_dependency "webrick-webdav", ">= 1.0"
|
15
17
|
|
16
18
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
19
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/dav
CHANGED
@@ -21,6 +21,7 @@ def print_usage
|
|
21
21
|
puts " put Put file from FILE to URL"
|
22
22
|
puts " mkdir Create directory at URL"
|
23
23
|
puts " gsub Replace content at URL from REGEXP to VALUE"
|
24
|
+
puts " props Display xml properties for file or directory at URL"
|
24
25
|
exit
|
25
26
|
end
|
26
27
|
|
@@ -62,13 +63,13 @@ res = dav.start { |dav|
|
|
62
63
|
when 'get'
|
63
64
|
if file.nil?
|
64
65
|
dav.get(url.path) do |str|
|
65
|
-
|
66
|
+
$stdout.print str
|
66
67
|
end
|
67
68
|
else
|
68
69
|
File.open(file, "w") do |stream|
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
dav.get(url.path) do |str|
|
71
|
+
stream.print str
|
72
|
+
end
|
72
73
|
end
|
73
74
|
end
|
74
75
|
when 'lsr'
|
@@ -86,9 +87,11 @@ res = dav.start { |dav|
|
|
86
87
|
val = $*[3]
|
87
88
|
dav.find(url.path) do |item|
|
88
89
|
if (item.type == :file)
|
89
|
-
|
90
|
+
item.content = item.content.gsub(re, val)
|
90
91
|
end
|
91
92
|
end
|
93
|
+
when 'props'
|
94
|
+
puts dav.propfind(url.path).to_s
|
92
95
|
else
|
93
96
|
print_usage
|
94
97
|
end
|
data/lib/net/dav/item.rb
CHANGED
@@ -13,34 +13,44 @@ module Net
|
|
13
13
|
|
14
14
|
# Synonym for uri
|
15
15
|
def url
|
16
|
-
|
16
|
+
@uri
|
17
17
|
end
|
18
18
|
|
19
19
|
def initialize(dav, uri, type, size) #:nodoc:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
@uri = uri
|
21
|
+
@size = size.to_i rescue nil
|
22
|
+
@type = type
|
23
|
+
@dav = dav
|
24
24
|
end
|
25
25
|
|
26
26
|
# Get content from server if needed and return as string
|
27
27
|
def content
|
28
|
-
|
29
|
-
|
28
|
+
return @content unless @content.nil?
|
29
|
+
@content = @dav.get(@uri.path)
|
30
30
|
end
|
31
31
|
|
32
32
|
# Put content to server
|
33
33
|
def content=(str)
|
34
|
-
|
35
|
-
|
34
|
+
@dav.put_string(@uri.path, str)
|
35
|
+
@content = str
|
36
|
+
end
|
37
|
+
|
38
|
+
# Proppatch item
|
39
|
+
def proppatch(xml_snippet)
|
40
|
+
@dav.proppatch(@uri.path,xml_snippet)
|
41
|
+
end
|
42
|
+
|
43
|
+
#Properties for this item
|
44
|
+
def propfind
|
45
|
+
return @dav.propfind(@uri.path)
|
36
46
|
end
|
37
47
|
|
38
48
|
def to_s #:nodoc:
|
39
|
-
|
49
|
+
"#<Net::DAV::Item URL:#{@uri.to_s} type:#{@type}>"
|
40
50
|
end
|
41
51
|
|
42
52
|
def inspect #:nodoc:
|
43
|
-
|
53
|
+
"#<Net::DAV::Item URL:#{@uri.to_s} type:#{@type}>"
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
data/lib/net/dav.rb
CHANGED
@@ -19,252 +19,261 @@ module Net #:nodoc:
|
|
19
19
|
attr_accessor :disable_basic_auth
|
20
20
|
|
21
21
|
def verify_callback=(callback)
|
22
|
-
|
22
|
+
@http.verify_callback = callback
|
23
23
|
end
|
24
24
|
|
25
25
|
def verify_server=(value)
|
26
|
-
|
26
|
+
@http.verify_mode = value ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
27
27
|
end
|
28
28
|
|
29
29
|
def initialize(uri)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
@disable_basic_auth = false
|
31
|
+
@uri = uri
|
32
|
+
case @uri.scheme
|
33
|
+
when "http"
|
34
|
+
@http = Net::HTTP.new(@uri.host, @uri.port)
|
35
|
+
when "https"
|
36
|
+
@http = Net::HTTP.new(@uri.host, @uri.port)
|
37
|
+
@http.use_ssl = true
|
38
|
+
self.verify_server = true
|
39
|
+
else
|
40
|
+
raise "unknown uri scheme"
|
41
|
+
end
|
42
42
|
end
|
43
43
|
|
44
44
|
def start(&block)
|
45
|
-
|
45
|
+
@http.start(&block)
|
46
46
|
end
|
47
47
|
|
48
48
|
def read_timeout
|
49
|
-
|
49
|
+
@http.read_timeout
|
50
50
|
end
|
51
51
|
|
52
52
|
def read_timeout=(sec)
|
53
|
-
|
53
|
+
@http.read_timeout = sec
|
54
54
|
end
|
55
55
|
|
56
56
|
def open_timeout
|
57
|
-
|
57
|
+
@http.read_timeout
|
58
58
|
end
|
59
59
|
|
60
60
|
def open_timeout=(sec)
|
61
|
-
|
61
|
+
@http.read_timeout = sec
|
62
62
|
end
|
63
63
|
|
64
64
|
def request_sending_stream(verb, path, stream, length, headers)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
65
|
+
req =
|
66
|
+
case verb
|
67
|
+
when :put
|
68
|
+
Net::HTTP::Put.new(path)
|
69
|
+
else
|
70
|
+
raise "unkown sending_stream verb #{verb}"
|
71
|
+
end
|
72
|
+
req.body_stream = stream
|
73
|
+
req.content_length = length
|
74
|
+
headers.each_pair { |key, value| req[key] = value } if headers
|
75
|
+
req.content_type = 'text/xml; charset="utf-8"'
|
76
|
+
res = handle_request(req, headers)
|
77
|
+
res
|
78
78
|
end
|
79
79
|
|
80
80
|
def request_sending_body(verb, path, body, headers)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
81
|
+
req =
|
82
|
+
case verb
|
83
|
+
when :put
|
84
|
+
Net::HTTP::Put.new(path)
|
85
|
+
else
|
86
|
+
raise "unkown sending_body verb #{verb}"
|
87
|
+
end
|
88
|
+
req.body = body
|
89
|
+
headers.each_pair { |key, value| req[key] = value } if headers
|
90
|
+
req.content_type = 'text/xml; charset="utf-8"'
|
91
|
+
res = handle_request(req, headers)
|
92
|
+
res
|
93
93
|
end
|
94
94
|
|
95
95
|
def request_returning_body(verb, path, headers, &block)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
96
|
+
req =
|
97
|
+
case verb
|
98
|
+
when :get
|
99
|
+
Net::HTTP::Get.new(path)
|
100
|
+
else
|
101
|
+
raise "unkown returning_body verb #{verb}"
|
102
|
+
end
|
103
|
+
headers.each_pair { |key, value| req[key] = value } if headers
|
104
|
+
res = handle_request(req, headers, MAX_REDIRECTS, &block)
|
105
|
+
res.body
|
106
106
|
end
|
107
107
|
|
108
108
|
def request(verb, path, body, headers)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
109
|
+
req =
|
110
|
+
case verb
|
111
|
+
when :propfind
|
112
|
+
Net::HTTP::Propfind.new(path)
|
113
|
+
when :mkcol
|
114
|
+
Net::HTTP::Mkcol.new(path)
|
115
|
+
when :delete
|
116
|
+
Net::HTTP::Delete.new(path)
|
117
|
+
when :move
|
118
|
+
Net::HTTP::Move.new(path)
|
119
|
+
when :copy
|
120
|
+
Net::HTTP::Copy.new(path)
|
121
|
+
when :proppatch
|
122
|
+
Net::HTTP::Proppatch.new(path)
|
123
|
+
else
|
124
|
+
raise "unkown verb #{verb}"
|
125
|
+
end
|
126
|
+
req.body = body
|
127
|
+
headers.each_pair { |key, value| req[key] = value } if headers
|
128
|
+
req.content_type = 'text/xml; charset="utf-8"'
|
129
|
+
res = handle_request(req, headers)
|
130
|
+
res
|
123
131
|
end
|
124
132
|
|
125
133
|
def handle_request(req, headers, limit = MAX_REDIRECTS, &block)
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
134
|
+
# You should choose better exception.
|
135
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
136
|
+
|
137
|
+
response = nil
|
138
|
+
if block
|
139
|
+
@http.request(req) {|res|
|
140
|
+
# Only start returning a body if we will not retry
|
141
|
+
res.read_body nil, &block if !res.is_a?(Net::HTTPUnauthorized) && !res.is_a?(Net::HTTPRedirection)
|
142
|
+
response = res
|
143
|
+
}
|
144
|
+
else
|
145
|
+
response = @http.request(req)
|
146
|
+
end
|
147
|
+
case response
|
148
|
+
when Net::HTTPSuccess then
|
149
|
+
return response
|
150
|
+
when Net::HTTPUnauthorized then
|
151
|
+
response.error! unless @user
|
152
|
+
response.error! if req['authorization']
|
153
|
+
new_req = clone_req(req.path, req, headers)
|
154
|
+
if response['www-authenticate'] =~ /^Basic/
|
155
|
+
if disable_basic_auth
|
156
|
+
raise "server requested basic auth, but that is disabled"
|
157
|
+
end
|
158
|
+
new_req.basic_auth @user, @pass
|
159
|
+
else
|
160
|
+
digest_auth(new_req, @user, @pass, response)
|
161
|
+
end
|
162
|
+
return handle_request(new_req, headers, limit - 1, &block)
|
163
|
+
when Net::HTTPRedirection then
|
164
|
+
location = URI.parse(response['location'])
|
165
|
+
if (@uri.scheme != location.scheme ||
|
166
|
+
@uri.host != location.host ||
|
167
|
+
@uri.port != location.port)
|
168
|
+
raise ArgumentError, "cannot redirect to a different host #{@uri} => #{location}"
|
169
|
+
end
|
170
|
+
new_req = clone_req(location.path, req, headers)
|
171
|
+
return handle_request(new_req, headers, limit - 1, &block)
|
172
|
+
else
|
173
|
+
response.error!
|
174
|
+
end
|
167
175
|
end
|
176
|
+
|
168
177
|
def clone_req(path, req, headers)
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
178
|
+
new_req = req.class.new(path)
|
179
|
+
new_req.body = req.body if req.body
|
180
|
+
new_req.body_stream = req.body_stream if req.body_stream
|
181
|
+
headers.each_pair { |key, value| new_req[key] = value } if headers
|
182
|
+
return new_req
|
174
183
|
end
|
175
184
|
|
176
185
|
CNONCE = Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))).slice(0, 8)
|
177
186
|
|
178
187
|
def digest_auth(request, user, password, response)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
188
|
+
# based on http://segment7.net/projects/ruby/snippets/digest_auth.rb
|
189
|
+
@nonce_count = 0 if @nonce_count.nil?
|
190
|
+
@nonce_count += 1
|
191
|
+
|
192
|
+
raise "bad www-authenticate header" unless (response['www-authenticate'] =~ /^(\w+) (.*)/)
|
193
|
+
|
194
|
+
params = {}
|
195
|
+
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
196
|
+
|
197
|
+
a_1 = "#{user}:#{params['realm']}:#{password}"
|
198
|
+
a_2 = "#{request.method}:#{request.path}"
|
199
|
+
request_digest = ''
|
200
|
+
request_digest << Digest::MD5.hexdigest(a_1)
|
201
|
+
request_digest << ':' << params['nonce']
|
202
|
+
request_digest << ':' << ('%08x' % @nonce_count)
|
203
|
+
request_digest << ':' << CNONCE
|
204
|
+
request_digest << ':' << params['qop']
|
205
|
+
request_digest << ':' << Digest::MD5.hexdigest(a_2)
|
206
|
+
|
207
|
+
header = []
|
208
|
+
header << "Digest username=\"#{user}\""
|
209
|
+
header << "realm=\"#{params['realm']}\""
|
210
|
+
header << "nonce=\"#{params['nonce']}\""
|
211
|
+
header << "uri=\"#{request.path}\""
|
212
|
+
header << "cnonce=\"#{CNONCE}\""
|
213
|
+
header << "nc=#{'%08x' % @nonce_count}"
|
214
|
+
header << "qop=#{params['qop']}"
|
215
|
+
header << "response=\"#{Digest::MD5.hexdigest(request_digest)}\""
|
216
|
+
header << "algorithm=\"MD5\""
|
217
|
+
|
218
|
+
header = header.join(', ')
|
219
|
+
request['Authorization'] = header
|
211
220
|
end
|
212
221
|
end
|
213
222
|
|
214
223
|
|
215
224
|
class CurlHandler < NetHttpHandler
|
216
225
|
def verify_callback=(callback)
|
217
|
-
|
218
|
-
|
219
|
-
|
226
|
+
super
|
227
|
+
curl = make_curl
|
228
|
+
$stderr.puts "verify_callback not implemented in Curl::Easy"
|
220
229
|
end
|
221
230
|
|
222
231
|
def verify_server=(value)
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
232
|
+
super
|
233
|
+
curl = make_curl
|
234
|
+
curl.ssl_verify_peer = value
|
235
|
+
curl.ssl_verify_host = value
|
227
236
|
end
|
228
237
|
|
229
238
|
def make_curl
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
239
|
+
unless @curl
|
240
|
+
@curl = Curl::Easy.new
|
241
|
+
@curl.timeout = @http.read_timeout
|
242
|
+
@curl.follow_location = true
|
243
|
+
@curl.max_redirects = MAX_REDIRECTS
|
244
|
+
if disable_basic_auth
|
245
|
+
@curl.http_auth_types = Curl::CURLAUTH_DIGEST
|
246
|
+
end
|
247
|
+
end
|
248
|
+
@curl
|
240
249
|
end
|
241
250
|
|
242
251
|
def request_returning_body(verb, path, headers)
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
252
|
+
raise "unkown returning_body verb #{verb}" unless verb == :get
|
253
|
+
url = @uri.merge(path)
|
254
|
+
curl = make_curl
|
255
|
+
curl.url = url.to_s
|
256
|
+
headers.each_pair { |key, value| curl.headers[key] = value } if headers
|
257
|
+
if (@user)
|
258
|
+
curl.userpwd = "#{@user}:#{@pass}"
|
259
|
+
else
|
260
|
+
curl.userpwd = nil
|
261
|
+
end
|
262
|
+
res = nil
|
263
|
+
if block_given?
|
264
|
+
curl.on_body do |frag|
|
265
|
+
yield frag
|
266
|
+
frag.length
|
267
|
+
end
|
268
|
+
end
|
269
|
+
curl.perform
|
270
|
+
unless curl.response_code >= 200 && curl.response_code < 300
|
271
|
+
header_block = curl.header_str.split(/\r?\n\r?\n/)[-1]
|
272
|
+
msg = header_block.split(/\r?\n/)[0]
|
273
|
+
msg.gsub!(/^HTTP\/\d+.\d+ /, '')
|
274
|
+
raise Net::HTTPError.new(msg, nil)
|
275
|
+
end
|
276
|
+
curl.body_str
|
268
277
|
end
|
269
278
|
|
270
279
|
end
|
@@ -325,7 +334,7 @@ module Net #:nodoc:
|
|
325
334
|
def initialize(uri, options = nil)
|
326
335
|
@have_curl = Curl rescue nil
|
327
336
|
if options && options.has_key?(:curl) && !options[:curl]
|
328
|
-
|
337
|
+
@have_curl = false
|
329
338
|
end
|
330
339
|
@uri = uri
|
331
340
|
@uri = URI.parse(@uri) if @uri.is_a? String
|
@@ -343,7 +352,7 @@ module Net #:nodoc:
|
|
343
352
|
# end
|
344
353
|
def start(&block) # :yield: dav
|
345
354
|
@handler.start do
|
346
|
-
|
355
|
+
return yield(self)
|
347
356
|
end
|
348
357
|
end
|
349
358
|
|
@@ -362,6 +371,16 @@ module Net #:nodoc:
|
|
362
371
|
|
363
372
|
# Find files and directories, yields Net::DAV::Item
|
364
373
|
#
|
374
|
+
# The :filename option can be a regexp or string, and is used
|
375
|
+
# to filter the yielded items.
|
376
|
+
#
|
377
|
+
# If :suppress_errors is passed, exceptions that occurs when
|
378
|
+
# reading directory information is ignored, and a warning is
|
379
|
+
# printed out stderr instead.
|
380
|
+
#
|
381
|
+
# The default is to not traverse recursively, unless the :recursive
|
382
|
+
# options is passed.
|
383
|
+
#
|
365
384
|
# Examples:
|
366
385
|
#
|
367
386
|
# res = Net::DAV.start(url) do |dav|
|
@@ -370,29 +389,63 @@ module Net #:nodoc:
|
|
370
389
|
# puts item.content
|
371
390
|
# end
|
372
391
|
# end
|
392
|
+
#
|
393
|
+
# dav = Net::DAV.new(url)
|
394
|
+
# dav.find(url.path, :filename => /\.html/, :suppress_errors => true)
|
395
|
+
# puts item.url.to_s
|
396
|
+
# end
|
373
397
|
def find(path, options = {})
|
374
398
|
path = @uri.merge(path).path
|
375
399
|
namespaces = {'x' => "DAV:"}
|
376
|
-
|
400
|
+
begin
|
401
|
+
doc = propfind(path)
|
402
|
+
rescue Net::HTTPServerException => e
|
403
|
+
msg = e.to_s + ": " + path.to_s
|
404
|
+
if(options[:suppress_errors])then
|
405
|
+
# Ignore dir if propfind returns an error
|
406
|
+
warn("Warning: " + msg)
|
407
|
+
return nil
|
408
|
+
else
|
409
|
+
raise Net::HTTPServerException.new(msg, nil)
|
410
|
+
end
|
411
|
+
end
|
377
412
|
path.sub!(/\/$/, '')
|
378
413
|
doc./('.//x:response', namespaces).each do |item|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
414
|
+
uri = @uri.merge(item.xpath("x:href", namespaces).inner_text)
|
415
|
+
size = item.%(".//x:getcontentlength", namespaces).inner_text rescue nil
|
416
|
+
type = item.%(".//x:collection", namespaces) ? :directory : :file
|
417
|
+
res = Item.new(self, uri, type, size)
|
418
|
+
if type == :file then
|
419
|
+
|
420
|
+
if(options[:filename])then
|
421
|
+
search_term = options[:filename]
|
422
|
+
filename = File.basename(uri.path)
|
423
|
+
if(search_term.class == Regexp and search_term.match(filename))then
|
424
|
+
yield res
|
425
|
+
elsif(search_term.class == String and search_term == filename)then
|
426
|
+
yield res
|
427
|
+
end
|
428
|
+
else
|
429
|
+
yield res
|
430
|
+
end
|
431
|
+
|
432
|
+
elsif uri.path == path || uri.path == path + "/"
|
433
|
+
# This is the top-level dir, skip it
|
434
|
+
elsif options[:recursive] && type == :directory
|
435
|
+
|
436
|
+
if(!options[:filename])then
|
437
|
+
yield res
|
438
|
+
end
|
439
|
+
|
440
|
+
# This is a subdir, recurse
|
441
|
+
find(uri.path, options) do |sub_res|
|
442
|
+
yield sub_res
|
443
|
+
end
|
444
|
+
else
|
445
|
+
if(!options[:filename])then
|
446
|
+
yield res
|
447
|
+
end
|
448
|
+
end
|
396
449
|
end
|
397
450
|
end
|
398
451
|
|
@@ -400,7 +453,7 @@ module Net #:nodoc:
|
|
400
453
|
def cd(url)
|
401
454
|
new_uri = @uri.merge(url)
|
402
455
|
if new_uri.host != @uri.host || new_uri.port != @uri.port || new_uri.scheme != @uri.scheme
|
403
|
-
|
456
|
+
raise Exception , "uri must have same scheme, host and port"
|
404
457
|
end
|
405
458
|
@uri = new_uri
|
406
459
|
end
|
@@ -441,6 +494,58 @@ module Net #:nodoc:
|
|
441
494
|
res.body
|
442
495
|
end
|
443
496
|
|
497
|
+
# Delete request
|
498
|
+
#
|
499
|
+
# Example:
|
500
|
+
# dav.delete(uri.path)
|
501
|
+
def delete(path)
|
502
|
+
path = @uri.merge(path).path
|
503
|
+
res = @handler.request(:delete, path, nil, nil)
|
504
|
+
res.body
|
505
|
+
end
|
506
|
+
|
507
|
+
# Send a move request to the server.
|
508
|
+
#
|
509
|
+
# Example:
|
510
|
+
# dav.move(original_path, new_path)
|
511
|
+
def move(path,destination)
|
512
|
+
path = @uri.merge(path).path
|
513
|
+
destination = @uri.merge(destination).to_s
|
514
|
+
headers = {'Destination' => destination}
|
515
|
+
res = @handler.request(:move, path, nil, headers)
|
516
|
+
res.body
|
517
|
+
end
|
518
|
+
|
519
|
+
# Send a copy request to the server.
|
520
|
+
#
|
521
|
+
# Example:
|
522
|
+
# dav.copy(original_path, destination)
|
523
|
+
def copy(path,destination)
|
524
|
+
path = @uri.merge(path).path
|
525
|
+
destination = @uri.merge(destination).to_s
|
526
|
+
headers = {'Destination' => destination}
|
527
|
+
res = @handler.request(:copy, path, nil, headers)
|
528
|
+
res.body
|
529
|
+
end
|
530
|
+
|
531
|
+
# Do a proppatch request to the server to
|
532
|
+
# update properties on resources or collections.
|
533
|
+
#
|
534
|
+
# Example:
|
535
|
+
# dav.proppatch(uri.path,"<d:creationdate>#{new_date}</d:creationdate>")
|
536
|
+
def proppatch(path, xml_snippet)
|
537
|
+
headers = {'Depth' => '1'}
|
538
|
+
body = '<?xml version="1.0"?>' +
|
539
|
+
'<d:propertyupdate xmlns:d="DAV:">' +
|
540
|
+
'<d:set>' +
|
541
|
+
'<d:prop>' +
|
542
|
+
xml_snippet +
|
543
|
+
'</d:prop>' +
|
544
|
+
'</d:set>' +
|
545
|
+
'</d:propertyupdate>'
|
546
|
+
res = @handler.request(:proppatch, path, body, headers)
|
547
|
+
Nokogiri::XML.parse(res.body)
|
548
|
+
end
|
444
549
|
|
445
550
|
# Makes a new directory (collection)
|
446
551
|
def mkdir(path)
|
data/net_dav.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{net_dav}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Miron Cuperman"]
|
12
|
-
s.date = %q{2009-
|
11
|
+
s.authors = ["Miron Cuperman", "Thomas Flemming"]
|
12
|
+
s.date = %q{2009-12-10}
|
13
13
|
s.default_executable = %q{dav}
|
14
14
|
s.description = %q{WebDAV client library in the style of Net::HTTP, using Net::HTTP and libcurl, if installed}
|
15
15
|
s.email = %q{c1.github@niftybox.net}
|
@@ -30,7 +30,9 @@ Gem::Specification.new do |s|
|
|
30
30
|
"lib/net/dav/item.rb",
|
31
31
|
"net_dav.gemspec",
|
32
32
|
"script/multi-test",
|
33
|
-
"spec/
|
33
|
+
"spec/fixtures/file.html",
|
34
|
+
"spec/integration/net_dav_spec.rb",
|
35
|
+
"spec/integration/webdav_server.rb",
|
34
36
|
"spec/spec.opts",
|
35
37
|
"spec/spec_helper.rb",
|
36
38
|
"tmp/.gitignore"
|
@@ -41,8 +43,9 @@ Gem::Specification.new do |s|
|
|
41
43
|
s.rubygems_version = %q{1.3.5}
|
42
44
|
s.summary = %q{WebDAV client library in the style of Net::HTTP}
|
43
45
|
s.test_files = [
|
44
|
-
"spec/
|
45
|
-
"spec/
|
46
|
+
"spec/spec_helper.rb",
|
47
|
+
"spec/integration/webdav_server.rb",
|
48
|
+
"spec/integration/net_dav_spec.rb"
|
46
49
|
]
|
47
50
|
|
48
51
|
if s.respond_to? :specification_version then
|
@@ -52,13 +55,16 @@ Gem::Specification.new do |s|
|
|
52
55
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
56
|
s.add_runtime_dependency(%q<nokogiri>, [">= 1.3.0"])
|
54
57
|
s.add_development_dependency(%q<rspec>, [">= 1.2.0"])
|
58
|
+
s.add_development_dependency(%q<webrick-webdav>, [">= 1.0"])
|
55
59
|
else
|
56
60
|
s.add_dependency(%q<nokogiri>, [">= 1.3.0"])
|
57
61
|
s.add_dependency(%q<rspec>, [">= 1.2.0"])
|
62
|
+
s.add_dependency(%q<webrick-webdav>, [">= 1.0"])
|
58
63
|
end
|
59
64
|
else
|
60
65
|
s.add_dependency(%q<nokogiri>, [">= 1.3.0"])
|
61
66
|
s.add_dependency(%q<rspec>, [">= 1.2.0"])
|
67
|
+
s.add_dependency(%q<webrick-webdav>, [">= 1.0"])
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
@@ -0,0 +1 @@
|
|
1
|
+
Content
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/webdav_server')
|
3
|
+
|
4
|
+
describe "Net::Dav" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
# Start webdav server in subprocess
|
8
|
+
@pid = fork do
|
9
|
+
webdav_server(:port => 10080,:authentication => false)
|
10
|
+
end
|
11
|
+
# Wait for webdavserver to start
|
12
|
+
sleep(10)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should create a Net::Dav object" do
|
16
|
+
Net::DAV.new("http://localhost.localdomain/").should_not be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should read properties from webdav server" do
|
20
|
+
dav = Net::DAV.new("http://localhost:10080/")
|
21
|
+
@props = dav.propfind("/").to_s
|
22
|
+
@props.should match(/200 OK/)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should write files to webdav server" do
|
26
|
+
dav = Net::DAV.new("http://localhost:10080/")
|
27
|
+
@props = find_props_or_error(dav, "/new_file.html")
|
28
|
+
@props.should match(/404.*Not found/i)
|
29
|
+
|
30
|
+
dav.put_string("/new_file.html","File contents")
|
31
|
+
|
32
|
+
@props = find_props_or_error(dav, "/new_file.html")
|
33
|
+
@props.should match(/200 OK/i)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should delete files from webdav server" do
|
37
|
+
dav = Net::DAV.new("http://localhost:10080/")
|
38
|
+
|
39
|
+
@props = find_props_or_error(dav, "/new_file.html")
|
40
|
+
@props.should match(/200 OK/i)
|
41
|
+
|
42
|
+
dav.delete("/new_file.html")
|
43
|
+
@props = find_props_or_error(dav, "/new_file.html")
|
44
|
+
@props.should match(/404.*Not found/i)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should copy files on webdav server" do
|
48
|
+
dav = Net::DAV.new("http://localhost:10080/")
|
49
|
+
|
50
|
+
@props = find_props_or_error(dav, "/file.html")
|
51
|
+
@props.should match(/200 OK/i)
|
52
|
+
|
53
|
+
dav.copy("/file.html","/copied_file.html")
|
54
|
+
@props = find_props_or_error(dav, "/copied_file.html")
|
55
|
+
@props.should match(/200 OK/i)
|
56
|
+
|
57
|
+
dav.delete("/copied_file.html")
|
58
|
+
|
59
|
+
@props = find_props_or_error(dav, "/copied_file.html")
|
60
|
+
@props.should match(/404.*Not found/i)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should move files on webdav server" do
|
64
|
+
dav = Net::DAV.new("http://localhost:10080/")
|
65
|
+
|
66
|
+
@props = find_props_or_error(dav, "/file.html")
|
67
|
+
@props.should match(/200 OK/i)
|
68
|
+
|
69
|
+
dav.move("/file.html","/moved_file.html")
|
70
|
+
@props = find_props_or_error(dav, "/moved_file.html")
|
71
|
+
@props.should match(/200 OK/i)
|
72
|
+
|
73
|
+
@props = find_props_or_error(dav, "/file.html")
|
74
|
+
@props.should match(/404.*Not found/i)
|
75
|
+
|
76
|
+
dav.move("/moved_file.html","/file.html")
|
77
|
+
@props = find_props_or_error(dav, "/file.html")
|
78
|
+
@props.should match(/200 OK/i)
|
79
|
+
end
|
80
|
+
|
81
|
+
# proppatch seems to work, but our simple webdav server don't update properties
|
82
|
+
# it "should alter properties on resources on webdav server" do
|
83
|
+
# dav = Net::DAV.new("http://localhost:10080/")
|
84
|
+
# @props = find_props_or_error(dav, "/file.html")
|
85
|
+
# puts @props
|
86
|
+
# dav.proppatch("/file.html", "<d:resourcetype>static-file</d:resourcetype>")
|
87
|
+
# @props = find_props_or_error(dav, "/file.html")
|
88
|
+
# puts @props
|
89
|
+
# end
|
90
|
+
|
91
|
+
after(:all) do
|
92
|
+
# Shut down webdav server
|
93
|
+
Process.kill('SIGKILL', @pid) rescue nil
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'webrick'
|
5
|
+
require 'webrick/httpservlet/webdavhandler'
|
6
|
+
|
7
|
+
# Web server with WebDAV extensions
|
8
|
+
#
|
9
|
+
# Usage: ruby webdav_server.rb
|
10
|
+
|
11
|
+
# Code based on:
|
12
|
+
# http://github.com/aslakhellesoy/webdavjs/blob/master/spec/webdav_server.rb
|
13
|
+
|
14
|
+
|
15
|
+
# Monkey patch REXML to always nil-indent. The indentation is broken in REXML
|
16
|
+
# on Ruby 1.8.6 and even when fixed it confuses OS-X.
|
17
|
+
module REXML
|
18
|
+
module Node
|
19
|
+
alias old_to_s to_s
|
20
|
+
def to_s(indent=nil)
|
21
|
+
old_to_s(nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/223386
|
27
|
+
# http://gmarrone.objectblues.net/cgi-bin/wiki/WebDAV_-_Linux_server%2c_Mac_OS_X_client
|
28
|
+
module WEBrick
|
29
|
+
module HTTPServlet
|
30
|
+
class WebDAVHandlerVersion2 < WebDAVHandler
|
31
|
+
|
32
|
+
def do_OPTIONS(req, res)
|
33
|
+
super
|
34
|
+
res["DAV"] = "1,2"
|
35
|
+
end
|
36
|
+
|
37
|
+
def do_LOCK(req, res)
|
38
|
+
res.body << "<XXX-#{Time.now.to_s}/>"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class WebDAVHandlerVersion3 < WebDAVHandlerVersion2
|
44
|
+
|
45
|
+
# Enable authentication
|
46
|
+
$REALM = "WebDav share"
|
47
|
+
$USER = "myuser"
|
48
|
+
$PASS = "mypass"
|
49
|
+
|
50
|
+
def service(req, res)
|
51
|
+
HTTPAuth.basic_auth(req, res, $REALM) {|user, pass|
|
52
|
+
# this block returns true if
|
53
|
+
# authentication token is valid
|
54
|
+
user == $USER && pass == $PASS
|
55
|
+
}
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def webdav_server(*options)
|
65
|
+
port = 10080
|
66
|
+
if(options and options[0][:port])
|
67
|
+
port = options[0][:port]
|
68
|
+
end
|
69
|
+
log = WEBrick::Log.new
|
70
|
+
log.level = WEBrick::Log::DEBUG if $DEBUG
|
71
|
+
serv = WEBrick::HTTPServer.new({:Port => port, :Logger => log})
|
72
|
+
|
73
|
+
dir = File.expand_path(File.dirname(__FILE__)) + '/../fixtures'
|
74
|
+
if(options and options[0][:authentication])
|
75
|
+
serv.mount("/", WEBrick::HTTPServlet::WebDAVHandlerVersion3, dir)
|
76
|
+
else
|
77
|
+
serv.mount("/", WEBrick::HTTPServlet::WebDAVHandlerVersion2, dir)
|
78
|
+
end
|
79
|
+
|
80
|
+
trap(:INT){ serv.shutdown }
|
81
|
+
serv.start
|
82
|
+
end
|
83
|
+
|
84
|
+
if($0 == __FILE__)
|
85
|
+
|
86
|
+
webdav_server(:port => 10080,:authentication => false)
|
87
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,5 +6,14 @@ require 'spec'
|
|
6
6
|
require 'spec/autorun'
|
7
7
|
|
8
8
|
Spec::Runner.configure do |config|
|
9
|
-
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
# Profind helper. Returns properties or error
|
13
|
+
def find_props_or_error(dav, path)
|
14
|
+
begin
|
15
|
+
return dav.propfind(path).to_s
|
16
|
+
rescue Net::HTTPServerException => e
|
17
|
+
return e.to_s
|
18
|
+
end
|
10
19
|
end
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net_dav
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miron Cuperman
|
8
|
+
- Thomas Flemming
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date: 2009-
|
13
|
+
date: 2009-12-10 00:00:00 -08:00
|
13
14
|
default_executable: dav
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
@@ -32,6 +33,16 @@ dependencies:
|
|
32
33
|
- !ruby/object:Gem::Version
|
33
34
|
version: 1.2.0
|
34
35
|
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: webrick-webdav
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "1.0"
|
45
|
+
version:
|
35
46
|
description: WebDAV client library in the style of Net::HTTP, using Net::HTTP and libcurl, if installed
|
36
47
|
email: c1.github@niftybox.net
|
37
48
|
executables:
|
@@ -53,7 +64,9 @@ files:
|
|
53
64
|
- lib/net/dav/item.rb
|
54
65
|
- net_dav.gemspec
|
55
66
|
- script/multi-test
|
56
|
-
- spec/
|
67
|
+
- spec/fixtures/file.html
|
68
|
+
- spec/integration/net_dav_spec.rb
|
69
|
+
- spec/integration/webdav_server.rb
|
57
70
|
- spec/spec.opts
|
58
71
|
- spec/spec_helper.rb
|
59
72
|
- tmp/.gitignore
|
@@ -86,5 +99,6 @@ signing_key:
|
|
86
99
|
specification_version: 3
|
87
100
|
summary: WebDAV client library in the style of Net::HTTP
|
88
101
|
test_files:
|
89
|
-
- spec/net_dav_spec.rb
|
90
102
|
- spec/spec_helper.rb
|
103
|
+
- spec/integration/webdav_server.rb
|
104
|
+
- spec/integration/net_dav_spec.rb
|