Created
January 1, 2009 19:12
-
-
Save krobertson/42329 to your computer and use it in GitHub Desktop.
rackdav
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# rackdav (prototype) by Ken Robertson | |
# | |
# rackdav is a work in progress of a Rack middleware implementation of WebDAV | |
# | |
# This code is barely functional and ugly as hell, but meant to just work on figuring out compliance. | |
# It is built by working through the test suite provided by litmus (http://www.webdav.org/neon/litmus) | |
# Once it has compliance on the commands, it will be greatly cleaned up and refactored into an actual | |
# library instead of a gist. | |
# | |
# TODO: Mostly property handling, custom property storage, and locking (in memory for dev, memcached perhaps for production) | |
# | |
require 'rubygems' | |
require 'cgi' | |
require 'rack' | |
require 'hpricot' | |
require 'tempfile' | |
require 'libxml' | |
require 'activesupport' | |
class Dav | |
def call(env) | |
path = File.join('dav', CGI.unescape(env['REQUEST_PATH'])) | |
path = path.chop if path =~ /\/$/ | |
dest = File.join('dav', CGI.unescape(env['HTTP_DESTINATION'].gsub('http://localhost:5000/', ''))) if env.has_key?('HTTP_DESTINATION') | |
dest = dest.chop if dest =~ /\/$/ | |
puts "==================== #{env['HTTP_X_LITMUS']}: #{env['REQUEST_METHOD']} #{path}" | |
puts "#{Time.now}: #{env.inspect}" | |
puts | |
case env['REQUEST_METHOD'].downcase | |
when 'options' | |
[200, {'DAV' => '1'}, ''] | |
when 'get' | |
unless File.exist?(path) | |
[404, {}, 'Not found'] | |
else | |
f = File.new(path) | |
[200, {"Content-Type"=>"text/plain", 'Content-Length' => File.size(path).to_s}, f] | |
end | |
when 'put' | |
f = File.new(path, 'w') | |
b = env['rack.input'].read(env['rack.input'].size) | |
f.write b | |
f.close | |
[201, {"Content-Type"=>"text/plain"}, ['Hello']] | |
when 'post' | |
f = File.new(path, 'w') | |
b = env['rack.input'].read(env['rack.input'].size) | |
f.write b | |
f.close | |
[201, {"Content-Type"=>"text/plain"}, ['Hello']] | |
when 'delete' | |
if File.exist?(path) | |
FileUtils.rm_r(path) | |
[200, {}, ''] | |
else | |
[404, {}, ''] | |
end | |
when 'mkcol' | |
return [415, {}, ''] if env['rack.input'].size > 0 | |
parent = path.split('/') | |
parent = parent[0,parent.size-1].join('/') | |
return [409, {}, ''] unless File.directory?(parent) | |
unless File.exist?(path) | |
FileUtils.mkdir(path) | |
[200, {"Content-Type"=>"text/plain"}, ["Hello world!"]] | |
else | |
[405, {"Content-Type"=>"text/plain"}, ["Resource already exists"]] | |
end | |
when 'copy' | |
overwrite = env['HTTP_OVERWRITE'] == 'T' | |
exists = File.exist?(dest) | |
return [412, {}, ''] if exists and !overwrite | |
parent = dest.split('/') | |
parent = parent[0,parent.size-1].join('/') | |
return [409, {}, ''] unless File.directory?(parent) | |
FileUtils.cp_r(path, dest) | |
[exists && overwrite ? 204 : 201, {}, ''] | |
when 'move' | |
overwrite = env['HTTP_OVERWRITE'] == 'T' | |
exists = File.exist?(dest) | |
return [412, {}, ''] if exists and !overwrite | |
parent = dest.split('/') | |
parent = parent[0,parent.size-1].join('/') | |
return [409, {}, ''] unless File.directory?(parent) | |
FileUtils.rm_r(dest) if exists && overwrite | |
FileUtils.move(path, dest) | |
[exists && overwrite ? 204 : 201, {}, ''] | |
when 'propfind' | |
lines = env['rack.input'].readlines.join("\n") | |
puts "......................" | |
puts lines | |
puts "......................" | |
query = xmlize(lines) | |
return [400, {}, ''] unless query.root.name == 'propfind' | |
[550, {"Content-Type"=>"text/plain"}, ["Hello world!"]] | |
when 'proppatch' | |
lines = env['rack.input'].readlines.join("\n") | |
puts "......................" | |
puts lines | |
puts "......................" | |
query = xmlize(lines) | |
return [400, {}, ''] unless query.root.name == 'propertyupdate' | |
properties = [] | |
query.find("//D:prop").each do |prop| | |
puts "------------------ .#{prop.children.first.name}." | |
properties << prop.children.first.name | |
end | |
x = Builder::XmlMarkup.new(:indent => 2) | |
x.instruct! | |
response = x.D(:multistatus, {'xmlns:D' => "DAV:"}) do | |
x.D(:response) do | |
x.D(:href, 'http://localhost:5000' + env['REQUEST_URI']) | |
properties.each do |p| | |
x.D(:propstat) do | |
x.D(:prop) { x.D(:"#{p}") } | |
x.D(:status, 'HTTP/1.1 424 Failed Dependency') | |
end | |
end | |
end | |
end | |
[207, {"Content-Type"=>"text/xml", 'Content-Length' => response.size.to_s}, [response]] | |
else | |
[550, {"Content-Type"=>"text/plain"}, ["Hello world!"]] | |
end | |
end | |
def xmlize(env) | |
lines = env | |
# lines = env['rack.input'].readlines.join("\n") unless env.is_a?(String) | |
temp = Tempfile.new('a') | |
temp.write lines | |
temp.close | |
LibXML::XML::Document.file(temp.path) | |
end | |
end | |
Rack::Handler::Mongrel.run(Dav.new, {:Host => "0.0.0.0", :Port => 5000}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment