rack-webdav 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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/LICENSE +42 -0
- data/README.rdoc +336 -0
- data/bin/rack-webdav +126 -0
- data/lib/rack-webdav.rb +14 -0
- data/lib/rack-webdav/controller.rb +601 -0
- data/lib/rack-webdav/file.rb +37 -0
- data/lib/rack-webdav/file_resource_lock.rb +169 -0
- data/lib/rack-webdav/handler.rb +63 -0
- data/lib/rack-webdav/http_status.rb +108 -0
- data/lib/rack-webdav/interceptor.rb +22 -0
- data/lib/rack-webdav/interceptor_resource.rb +119 -0
- data/lib/rack-webdav/lock.rb +40 -0
- data/lib/rack-webdav/lock_store.rb +61 -0
- data/lib/rack-webdav/logger.rb +30 -0
- data/lib/rack-webdav/remote_file.rb +148 -0
- data/lib/rack-webdav/resource.rb +485 -0
- data/lib/rack-webdav/resources/file_resource.rb +379 -0
- data/lib/rack-webdav/resources/mongo_resource.rb +341 -0
- data/lib/rack-webdav/utils.rb +40 -0
- data/lib/rack-webdav/version.rb +17 -0
- data/rack-webdav.gemspec +31 -0
- metadata +206 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 56d779afdbd6da5c459d33219b508ed4b141a564
|
4
|
+
data.tar.gz: 1bba827edea7b0d09f7b22ecb013660966c23ee8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 888809b2b3deebd298ab26810d980686df9cf912ef3f8d557de2e5dfd45a3d0a3fa110dcd8562c4c3c38e6df1137901128eba54021e431139352e1cf0ba1e954
|
7
|
+
data.tar.gz: b545e17b78a6ec5c5c8ff1ae9e6d7cb4e682c152ed1d484ca8b5dd92a6be6599f0b4b68bd23bb72e84ae8013724ea2d280f9800ccc329369eea3ee5762bb2e4a
|
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
Current RackWebDAV license:
|
2
|
+
|
3
|
+
Copyright (c) 2010 Chris Roberts <chrisroberts.code@gmail.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to
|
7
|
+
deal in the Software without restriction, including without limitation the
|
8
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
9
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
18
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
|
23
|
+
Original rack_dav source license:
|
24
|
+
|
25
|
+
Copyright (c) 2009 Matthias Georgi <http://www.matthias-georgi.de>
|
26
|
+
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
28
|
+
of this software and associated documentation files (the "Software"), to
|
29
|
+
deal in the Software without restriction, including without limitation the
|
30
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
31
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
32
|
+
furnished to do so, subject to the following conditions:
|
33
|
+
|
34
|
+
The above copyright notice and this permission notice shall be included in
|
35
|
+
all copies or substantial portions of the Software.
|
36
|
+
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
40
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
41
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
42
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
= RackWebDAV - Web Authoring for Rack
|
2
|
+
|
3
|
+
RackWebDAV based by (dav4rack)[https://github.com/chrisroberts/dav4rack]
|
4
|
+
|
5
|
+
== Motivaion
|
6
|
+
|
7
|
+
* dav4rack no maintained
|
8
|
+
* I want to implment the following functions
|
9
|
+
* WebDAV RFC 3253 ( https://www.ietf.org/rfc/rfc3253.txt )
|
10
|
+
* because support Subversion (SVN)
|
11
|
+
* Rails 5 support
|
12
|
+
* Rack 2.0 support
|
13
|
+
|
14
|
+
== Current Feature
|
15
|
+
|
16
|
+
=== From DAV4Rack
|
17
|
+
* Better resource support for building fully virtualized resource structures
|
18
|
+
* Generic locking as well as Resource level specific locking
|
19
|
+
* Interceptor middleware to provide virtual mapping to resources
|
20
|
+
* Mapped resource paths
|
21
|
+
* Authentication support
|
22
|
+
* Resource callbacks
|
23
|
+
* Remote file proxying (including sendfile support for remote files)
|
24
|
+
|
25
|
+
=== RackWebDAV original
|
26
|
+
* Rack 2.0 support
|
27
|
+
* serveral bugfix
|
28
|
+
|
29
|
+
== Install
|
30
|
+
=== RubyGems
|
31
|
+
|
32
|
+
gem install rack-webdav
|
33
|
+
|
34
|
+
== Documentation
|
35
|
+
|
36
|
+
* TODO
|
37
|
+
|
38
|
+
== Quickstart
|
39
|
+
|
40
|
+
If you just want to share a folder over WebDAV, you can just start a
|
41
|
+
simple server with:
|
42
|
+
|
43
|
+
rack-webdav
|
44
|
+
|
45
|
+
This will start a Unicorn, Mongrel or WEBrick server on port 3000, which you can connect
|
46
|
+
to without authentication. Unicorn and Mongrel will be much more responsive than WEBrick,
|
47
|
+
so if you are having slowness issues, install one of them and restart the rack-webdav process.
|
48
|
+
The simple file resource allows very basic authentication which is used for an example. To enable it:
|
49
|
+
|
50
|
+
rack-webdav --username=user --password=pass
|
51
|
+
|
52
|
+
== Rack Handler
|
53
|
+
|
54
|
+
Using RackWebDAV within a rack application is pretty simple. A very slim
|
55
|
+
rackup script would look something like this:
|
56
|
+
|
57
|
+
require 'rubygems'
|
58
|
+
require 'rack-webdav'
|
59
|
+
|
60
|
+
use Rack::CommonLogger
|
61
|
+
run RackWebDAV::Handler.new(:root => '/path/to/public/fileshare')
|
62
|
+
|
63
|
+
This will use the included FileResource and set the share path. However,
|
64
|
+
RackWebDAV has some nifty little extras that can be enabled in the rackup script. First,
|
65
|
+
an example of how to use a custom resource:
|
66
|
+
|
67
|
+
run RackWebDAV::Handler.new(:resource_class => CustomResource, :custom => 'options', :passed => 'to resource')
|
68
|
+
|
69
|
+
Next, lets venture into mapping a path for our WebDAV access. In this example, we
|
70
|
+
will use default FileResource like in the first example, but instead of the WebDAV content
|
71
|
+
being available at the root directory, we will map it to a specific directory: /webdav/share/
|
72
|
+
|
73
|
+
require 'rubygems'
|
74
|
+
require 'rack-webdav'
|
75
|
+
|
76
|
+
use Rack::CommonLogger
|
77
|
+
|
78
|
+
app = Rack::Builder.new{
|
79
|
+
map '/webdav/share/' do
|
80
|
+
run RackWebDAV::Handler.new(:root => '/path/to/public/fileshare', :root_uri_path => '/webdav/share/')
|
81
|
+
end
|
82
|
+
}.to_app
|
83
|
+
run app
|
84
|
+
|
85
|
+
Aside from the Builder#map block, notice the new option passed to the Handler's initialization, :root_uri_path. When
|
86
|
+
RackWebDAV receives a request, it will automatically convert the request to the proper path and pass it to
|
87
|
+
the resource.
|
88
|
+
|
89
|
+
Another tool available when building the rackup script is the Interceptor. The Interceptor's job is to simply
|
90
|
+
intecept WebDAV requests received up the path hierarchy where no resources are currently mapped. For example,
|
91
|
+
lets continue with the last example but this time include the interceptor:
|
92
|
+
|
93
|
+
require 'rubygems'
|
94
|
+
require 'rack-webdav'
|
95
|
+
|
96
|
+
use Rack::CommonLogger
|
97
|
+
app = Rack::Builder.new{
|
98
|
+
map '/webdav/share/' do
|
99
|
+
run RackWebDAV::Handler.new(:root => '/path/to/public/fileshare', :root_uri_path => '/webdav/share/')
|
100
|
+
end
|
101
|
+
map '/webdav/share2/' do
|
102
|
+
run RackWebDAV::Handler.new(:resource_class => CustomResource, :root_uri_path => '/webdav/share2/')
|
103
|
+
end
|
104
|
+
map '/' do
|
105
|
+
use RackWebDAV::Interceptor, :mappings => {
|
106
|
+
'/webdav/share/' => {:resource_class => FileResource, :custom => 'option'},
|
107
|
+
'/webdav/share2/' => {:resource_class => CustomResource}
|
108
|
+
}
|
109
|
+
use Rails::Rack::Static
|
110
|
+
run ActionController::Dispatcher.new
|
111
|
+
end
|
112
|
+
}.to_app
|
113
|
+
run app
|
114
|
+
|
115
|
+
In this example we have two WebDAV resources restricted by path. This means those resources will handle requests to /webdav/share/*
|
116
|
+
and /webdav/share2/* but nothing above that. To allow webdav to respond, we provide the Interceptor. The Interceptor does not
|
117
|
+
provide any authentication support. It simply creates a virtual file system view to the provided mapped paths. Once the actual
|
118
|
+
resources have been reached, authentication will be enforced based on the requirements defined by the individual resource. Also
|
119
|
+
note in the root map you can see we are running a Rails application. This is how you can easily enable RackWebDAV with your Rails
|
120
|
+
application.
|
121
|
+
|
122
|
+
=== Enabling Logging
|
123
|
+
|
124
|
+
RackWebDAV provides some simple logging in a Rails style format (simply for consistency) so the output should look some what familiar.
|
125
|
+
|
126
|
+
RackWebDAV::Handler.new(:resource_class => CustomResource, :log_to => '/my/log/file')
|
127
|
+
|
128
|
+
You can even specify the level of logging:
|
129
|
+
|
130
|
+
RackWebDAV::Handler.new(:resource_class => CustomResource, :log_to => ['/my/log/file', Logger::DEBUG])
|
131
|
+
|
132
|
+
== Custom Resources
|
133
|
+
|
134
|
+
Creating your own resource is easy. Simply inherit the RackWebDAV::Resource class, and start redefining all the methods
|
135
|
+
you want to customize. The RackWebDAV::Resource class only has implementations for methods that can be provided extremely
|
136
|
+
generically. This means that most things will require at least some sort of implementation. However, because the Resource
|
137
|
+
is defined so generically, and the Controller simply passes the request on to the Resource, it is easy to create fully
|
138
|
+
virtualized resources.
|
139
|
+
|
140
|
+
== Helpers
|
141
|
+
|
142
|
+
There are some helpers worth mentioning that make things a little easier. RackWebDAV::Resource#accept_redirect? method is available to Resources.
|
143
|
+
If true, the currently connected client will accept and properly use a 302 redirect for a GET request. Most clients do not properly
|
144
|
+
support this, which can be a real pain when working with virtualized files that may be located some where else, like S3. To deal with
|
145
|
+
those clients that don't support redirects, a helper has been provided so resources don't have to deal with proxying themselves. The RackWebDAV::RemoteFile
|
146
|
+
is a modified Rack::File that can do some interesting things. First, lets look at its most basic use:
|
147
|
+
|
148
|
+
class MyResource < RackWebDAV::Resource
|
149
|
+
def setup
|
150
|
+
@item = method_to_fill_this_properly
|
151
|
+
end
|
152
|
+
|
153
|
+
def get
|
154
|
+
if(accept_redirect?)
|
155
|
+
response.redirect item[:url]
|
156
|
+
else
|
157
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type)
|
158
|
+
OK
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
This is a simple proxy. When Rack receives the RemoteFile, it will pull a chunk of data from object, which in turn pulls it from the socket, and
|
164
|
+
sends it to the user over and over again until the EOF is reached. This much the same method that Rack::File uses but instead we are pulling
|
165
|
+
from a socket rather than an actual file. Now, instead of proxying these files from a remote server every time, lets cache them:
|
166
|
+
|
167
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :cache_directory => '/tmp')
|
168
|
+
|
169
|
+
Providing the :cache_directory will let RemoteFile cache the items locally, and then search for them on subsequent requests before heading out
|
170
|
+
to the network. The cached file name is based off the SHA1 hash of the file path, size and last modified time. It is important to note that for
|
171
|
+
services like S3, the path will often change, making this cache pretty worthless. To combat this, we can provide a reference to use instead:
|
172
|
+
|
173
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :cache_directory => '/tmp', :cache_ref => item[:static_url])
|
174
|
+
|
175
|
+
These methods will work just fine, but it would be really nice to just let someone else deal with the proxying and let the process get back
|
176
|
+
to dealing with actual requests. RemoteFile will happily do that as long as the frontend server is setup correctly. Using the sendfile approach
|
177
|
+
will tell the RemoteFile to simply pass the headers on and let the server deal with doing the actual proxying. First, lets look at an implementation
|
178
|
+
using all the features, and then degrade that down to the bare minimum. These examples are NGINX specific, but are based off the Rack::Sendfile implementation
|
179
|
+
and as such should be applicable to other servers. First, a simplified NGINX server block:
|
180
|
+
|
181
|
+
server {
|
182
|
+
listen 80;
|
183
|
+
location / {
|
184
|
+
proxy_set_header X-Real-IP $remote_addr;
|
185
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
186
|
+
proxy_set_header Host $http_host;
|
187
|
+
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
|
188
|
+
proxy_set_header X-Accel-Remote-Mapping webdav_redirect
|
189
|
+
proxy_pass http://my_app_server;
|
190
|
+
}
|
191
|
+
|
192
|
+
location ~* /webdav_redirect {
|
193
|
+
internal;
|
194
|
+
resolver 127.0.0.1;
|
195
|
+
set $r_host $upstream_http_redirect_host;
|
196
|
+
set $r_url $upstream_http_redirect_url;
|
197
|
+
proxy_set_header Authorization '';
|
198
|
+
proxy_set_header Host $r_host;
|
199
|
+
proxy_max_temp_file_size 0;
|
200
|
+
proxy_pass $r_url;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
With this in place, the parameters for the RemoteFile change slightly:
|
205
|
+
|
206
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => true)
|
207
|
+
|
208
|
+
The RemoteFile will automatically take care of building out the correct path and sending the proper headers. If the X-Accel-Remote-Mapping header
|
209
|
+
is not available, you can simply pass the value:
|
210
|
+
|
211
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => true, :sendfile_prefix => 'webdav_redirect')
|
212
|
+
|
213
|
+
And if you don't have the X-Sendfile-Type header set, you can fix that by changing the value of :sendfile:
|
214
|
+
|
215
|
+
response.body = RackWebDAV::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => 'X-Accel-Redirect', :sendfile_prefix => 'webdav_redirect')
|
216
|
+
|
217
|
+
And if you have none of the above because your server hasn't been configured for sendfile support, you're out of luck until it's configured.
|
218
|
+
|
219
|
+
== Authentication
|
220
|
+
|
221
|
+
Authentication is performed on a per Resource basis. The Controller object will check the Resource for a Resource#authenticate method. If it exists,
|
222
|
+
any authentication information will be passed to the method. Depending on the result, the Controller will either continue on with the request, or send
|
223
|
+
a 401 Unauthorized response.
|
224
|
+
|
225
|
+
As a nicety, Resource#authentication_realm will be checked for existence and the returning string will be used as the Realm. Resource#authentication_error_msg
|
226
|
+
will also be checked for existence and the returning string will be passed in the response upon authentication failure.
|
227
|
+
|
228
|
+
Authentication can also be implemented using callbacks, as discussed below.
|
229
|
+
|
230
|
+
== Callbacks
|
231
|
+
|
232
|
+
Resources can make use of callbacks to easily apply permissions, authentication or any other action that needs to be performed before or after any or all
|
233
|
+
actions. Callbacks are applied to all publicly available methods. This is important for methods used internally within the resource. Methods not meant
|
234
|
+
to be called by the Controller, or anyone else, should be scoped protected or private to reduce the interaction with callbacks.
|
235
|
+
|
236
|
+
Callbacks can be called before or after a method call. For example:
|
237
|
+
|
238
|
+
class MyResource < RackWebDAV::Resource
|
239
|
+
before do |resource, method_name|
|
240
|
+
resource.send(:my_authentication_method)
|
241
|
+
end
|
242
|
+
|
243
|
+
after do |resource, method_name|
|
244
|
+
puts "#{Time.now} -> Completed: #{resource}##{method_name}"
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
def my_authentication_method
|
250
|
+
true
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
In this example MyResource#my_authentication_method will be called before any public method is called. After any method has been called a status
|
255
|
+
line will be printed to STDOUT. Running callbacks before/after every method call is a bit much in most cases, so callbacks can be applied to specific
|
256
|
+
methods:
|
257
|
+
|
258
|
+
class MyResource < RackWebDAV::Resource
|
259
|
+
before_get do |resource|
|
260
|
+
puts "#{Time.now} -> Received GET request from resource: #{resource}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
In this example, a simple status line will be printed to STDOUT before the MyResource#get method is called. The current resource object is always
|
265
|
+
provided to callbacks. The method name is only provided to the generic before/after callbacks.
|
266
|
+
|
267
|
+
Something very handy for dealing with the mess of files OS X leaves on the system:
|
268
|
+
|
269
|
+
class MyResource < RackWebDAV::Resource
|
270
|
+
after_unlock do |resource|
|
271
|
+
resource.delete if resource.name[0,1] == '.'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
Because OS X implements locking correctly, we can wait until it releases the lock on the file, and remove it if it's a hidden file.
|
276
|
+
|
277
|
+
Callbacks are called in the order they are defined, so you can easily build callbacks off each other. Like this example:
|
278
|
+
|
279
|
+
class MyResource < RackWebDAV::Resource
|
280
|
+
before do |resource, method_name|
|
281
|
+
resource.DAV_authenticate unless resource.user.is_a?(User)
|
282
|
+
raise Unauthorized unless resource.user.is_a?(User)
|
283
|
+
end
|
284
|
+
before do |resource, method_name|
|
285
|
+
resource.user.allowed?(method_name)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
In this example, the second block checking User#allowed? can count on Resource#user being defined because the blocks are called in
|
290
|
+
order, and if the Resource#user is not a User type, an exception is raised.
|
291
|
+
|
292
|
+
=== Avoiding callbacks
|
293
|
+
|
294
|
+
Something special to notice in the last example is the DAV_ prefix on authenticate. Providing the DAV_ prefix will prevent
|
295
|
+
any callbacks being applied to the given method. This allows us to provide a public method that the callback can access on the resource
|
296
|
+
without getting stuck in a loop.
|
297
|
+
|
298
|
+
== Software using RackWebDAV!
|
299
|
+
|
300
|
+
* {meishi}[https://github.com/inferiorhumanorgans/meishi] - Lightweight CardDAV implementation in Rails
|
301
|
+
* {rack-webdav_ext}[https://github.com/schmurfy/rack-webdav_ext] - CardDAV extension. (CalDAV planned)
|
302
|
+
|
303
|
+
== Issues/Bugs/Questions
|
304
|
+
|
305
|
+
=== Known Issues
|
306
|
+
|
307
|
+
* OS X Finder PUT fails when using NGINX (this is due to NGINX's lack of chunked transfer encoding)
|
308
|
+
* Windows WebDAV mini-redirector fails (this client is very broken. patches welcome.)
|
309
|
+
* Lots of unimplemented parts of the webdav spec (patches always welcome)
|
310
|
+
|
311
|
+
=== Unknown Issues
|
312
|
+
|
313
|
+
Please use the issues at github: http://github.com/chrisroberts/rack-webdav/issues
|
314
|
+
|
315
|
+
== Contributors
|
316
|
+
|
317
|
+
A big thanks to everyone contributing to help make this project better.
|
318
|
+
|
319
|
+
* {clyfe}[http://github.com/clyfe]
|
320
|
+
* {antiloopgmbh}[http://github.com/antiloopgmbh]
|
321
|
+
* {krug}[http://github.com/krug]
|
322
|
+
* {teefax}[http://github.com/teefax]
|
323
|
+
* {buffym}[https://github.com/buffym]
|
324
|
+
* {jbangert}[https://github.com/jbangert]
|
325
|
+
* {doxavore}[https://github.com/doxavore]
|
326
|
+
* {spicyj}[https://github.com/spicyj]
|
327
|
+
* {TurchenkoAlex}[https://github.com/TurchenkoAlex]
|
328
|
+
* {exabugs}[https://github.com/exabugs]
|
329
|
+
* {inferiorhumanorgans}[https://github.com/inferiorhumanorgans]
|
330
|
+
* {schmurfy}[https://github.com/schmurfy]
|
331
|
+
* {pifleo}[https://github.com/pifleo]
|
332
|
+
* {mlmorg}[https://github.com/mlmorg]
|
333
|
+
|
334
|
+
== License
|
335
|
+
|
336
|
+
Just like RackDAV before it, this software is distributed under the MIT license.
|
data/bin/rack-webdav
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rack-webdav/version'
|
5
|
+
require 'getoptlong'
|
6
|
+
|
7
|
+
def print_help_msg
|
8
|
+
print_version_info
|
9
|
+
puts "Usage: rack-webdav [opts]"
|
10
|
+
puts " --help Print help message"
|
11
|
+
puts " --version Print version information"
|
12
|
+
puts " --username name Set username"
|
13
|
+
puts " --password pass Set password"
|
14
|
+
puts " --root /share/path Set path to share directory"
|
15
|
+
puts " --log /path/to/log Set path for logging file"
|
16
|
+
puts " --verbosity opt Set logging verbosity. (Valid: debug,info,warn,fatal)"
|
17
|
+
end
|
18
|
+
|
19
|
+
def print_version_info
|
20
|
+
puts "DAV 4 Rack - Rack based WebDAV Framework - Version: #{RackWebDAV::VERSION}"
|
21
|
+
end
|
22
|
+
|
23
|
+
opts = GetoptLong.new(
|
24
|
+
['--username', '-u', GetoptLong::REQUIRED_ARGUMENT],
|
25
|
+
['--password', '-p', GetoptLong::REQUIRED_ARGUMENT],
|
26
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
27
|
+
['--version', '-v', GetoptLong::NO_ARGUMENT],
|
28
|
+
['--root', '-r', GetoptLong::REQUIRED_ARGUMENT],
|
29
|
+
['--log', '-l', GetoptLong::OPTIONAL_ARGUMENT],
|
30
|
+
['--verbosity', '-V', GetoptLong::REQUIRED_ARGUMENT],
|
31
|
+
['--pretty-xml', '-P', GetoptLong::REQUIRED_ARGUMENT]
|
32
|
+
)
|
33
|
+
|
34
|
+
credentials = {}
|
35
|
+
|
36
|
+
opts.each do |opt,arg|
|
37
|
+
case opt
|
38
|
+
when '--help'
|
39
|
+
print_help_msg
|
40
|
+
exit(0)
|
41
|
+
when '--username'
|
42
|
+
credentials[:username] = arg
|
43
|
+
when '--password'
|
44
|
+
credentials[:password] = arg
|
45
|
+
when '--root'
|
46
|
+
credentials[:root] = arg
|
47
|
+
unless(File.exists?(arg) && File.directory?(arg))
|
48
|
+
puts "ERROR: Path provided is not a valid directory (#{arg})"
|
49
|
+
exit(-1)
|
50
|
+
end
|
51
|
+
when '--pretty-xml'
|
52
|
+
credentials[:pretty_xml] = true
|
53
|
+
when '--version'
|
54
|
+
print_version_info
|
55
|
+
exit(0)
|
56
|
+
when '--log'
|
57
|
+
require 'pathname'
|
58
|
+
require 'logger'
|
59
|
+
credentials[:log_to] = [nil, Logger::FATAL] unless credentials[:log_to]
|
60
|
+
if(arg && !arg.empty?)
|
61
|
+
dirname = Pathname.new(arg).dirname
|
62
|
+
if((File.exists?(arg) && File.writable?(arg)) || (File.exists?(dirname) && File.writable?(dirname)))
|
63
|
+
credentials[:log_to][0] = arg
|
64
|
+
else
|
65
|
+
puts "ERROR: Log file is not writable: #{arg}"
|
66
|
+
exit(-1)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
credentials[:log_to][0] = $stdout
|
70
|
+
end
|
71
|
+
when '--verbosity'
|
72
|
+
require 'logger'
|
73
|
+
begin
|
74
|
+
credentials[:log_to] = [] unless credentials[:log_to]
|
75
|
+
credentials[:log_to][1] = Logger.const_get(arg.upcase)
|
76
|
+
rescue NameError
|
77
|
+
puts "ERROR: Unknown verbosity level given: #{arg}"
|
78
|
+
exit(-1)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
puts "ERROR: Unknown option provided"
|
82
|
+
exit(-1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
require 'rack-webdav'
|
87
|
+
|
88
|
+
app = Rack::Builder.new do
|
89
|
+
use Rack::ShowExceptions
|
90
|
+
use Rack::CommonLogger
|
91
|
+
use Rack::Reloader
|
92
|
+
use Rack::Lint
|
93
|
+
|
94
|
+
run RackWebDAV::Handler.new(credentials)
|
95
|
+
|
96
|
+
end.to_app
|
97
|
+
|
98
|
+
runners = []
|
99
|
+
runners << lambda do |x|
|
100
|
+
print 'Looking for unicorn... '
|
101
|
+
require 'unicorn'
|
102
|
+
puts 'OK'
|
103
|
+
if(Unicorn.respond_to?(:run))
|
104
|
+
Unicorn.run(x, :listeners => ["0.0.0.0:3000"])
|
105
|
+
else
|
106
|
+
Unicorn::HttpServer.new(x, :listeners => ["0.0.0.0:3000"]).start.join
|
107
|
+
end
|
108
|
+
end
|
109
|
+
runners << lambda do |x|
|
110
|
+
print 'Looking for mongrel... '
|
111
|
+
require 'mongrel'
|
112
|
+
puts 'OK'
|
113
|
+
Rack::Handler::Mongrel.run(x, :Port => 3000)
|
114
|
+
end
|
115
|
+
runners << lambda do |x|
|
116
|
+
puts 'Loading WEBrick'
|
117
|
+
Rack::Handler::WEBrick.run(x, :Port => 3000)
|
118
|
+
end
|
119
|
+
|
120
|
+
begin
|
121
|
+
runner = runners.shift
|
122
|
+
runner.call(app)
|
123
|
+
rescue LoadError
|
124
|
+
puts 'FAILED'
|
125
|
+
retry unless runners.empty?
|
126
|
+
end
|