calligraphy 0.2.1 → 0.3.1
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 +5 -5
- data/README.md +1 -1
- data/lib/calligraphy.rb +23 -17
- data/lib/calligraphy/rails/mapper.rb +150 -142
- data/lib/calligraphy/rails/web_dav_methods.rb +67 -0
- data/lib/calligraphy/rails/web_dav_preconditions.rb +114 -0
- data/lib/calligraphy/rails/web_dav_requests_controller.rb +72 -180
- data/lib/calligraphy/resource/file_resource.rb +377 -194
- data/lib/calligraphy/resource/resource.rb +192 -32
- data/lib/calligraphy/utils.rb +23 -6
- data/lib/calligraphy/version.rb +3 -1
- data/lib/calligraphy/{copy.rb → web_dav_request/copy.rb} +13 -8
- data/lib/calligraphy/{delete.rb → web_dav_request/delete.rb} +6 -2
- data/lib/calligraphy/web_dav_request/get.rb +18 -0
- data/lib/calligraphy/web_dav_request/lock.rb +89 -0
- data/lib/calligraphy/{mkcol.rb → web_dav_request/mkcol.rb} +7 -2
- data/lib/calligraphy/web_dav_request/move.rb +56 -0
- data/lib/calligraphy/web_dav_request/propfind.rb +24 -0
- data/lib/calligraphy/web_dav_request/proppatch.rb +29 -0
- data/lib/calligraphy/web_dav_request/put.rb +16 -0
- data/lib/calligraphy/{unlock.rb → web_dav_request/unlock.rb} +6 -1
- data/lib/calligraphy/web_dav_request/web_dav_request.rb +43 -0
- data/lib/calligraphy/xml/builder.rb +83 -117
- data/lib/calligraphy/xml/namespace.rb +12 -6
- data/lib/calligraphy/xml/node.rb +24 -10
- data/lib/calligraphy/xml/utils.rb +22 -11
- data/lib/calligraphy/xml/web_dav_elements.rb +92 -0
- data/lib/generators/calligraphy/install_generator.rb +4 -0
- data/lib/generators/templates/calligraphy.rb +2 -0
- metadata +109 -22
- data/lib/calligraphy/get.rb +0 -12
- data/lib/calligraphy/lock.rb +0 -52
- data/lib/calligraphy/move.rb +0 -31
- data/lib/calligraphy/propfind.rb +0 -18
- data/lib/calligraphy/proppatch.rb +0 -20
- data/lib/calligraphy/put.rb +0 -12
- data/lib/calligraphy/web_dav_request.rb +0 -31
- data/spec/spec_helper.rb +0 -46
@@ -1,6 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Calligraphy
|
4
|
+
# Responsible for creating a new collection resource at the location
|
5
|
+
# specified by the request.
|
2
6
|
class Mkcol < WebDavRequest
|
3
|
-
|
7
|
+
# Executes the WebDAV request for a particular resource.
|
8
|
+
def execute
|
4
9
|
return :method_not_allowed if @resource.exists?
|
5
10
|
return :conflict unless @resource.ancestor_exist?
|
6
11
|
return :unsupported_media_type unless @resource.request_body.blank?
|
@@ -8,7 +13,7 @@ module Calligraphy
|
|
8
13
|
@resource.create_collection
|
9
14
|
set_content_location_header
|
10
15
|
|
11
|
-
|
16
|
+
:created
|
12
17
|
end
|
13
18
|
|
14
19
|
private
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calligraphy
|
4
|
+
# Responsible for copying a resource then deleting the original source.
|
5
|
+
class Move < Copy
|
6
|
+
# Executes the WebDAV request for a particular resource.
|
7
|
+
def execute
|
8
|
+
return :locked if @resource.locked_to_user? @headers
|
9
|
+
|
10
|
+
if @resource.true? options[:overwrite]
|
11
|
+
previous_resource_existed = overwrite_destination
|
12
|
+
end
|
13
|
+
|
14
|
+
status = super
|
15
|
+
return status if %i[precondition_failed conflict].include? status
|
16
|
+
|
17
|
+
@resource.delete_collection
|
18
|
+
|
19
|
+
response_status status, previous_resource_existed
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def options
|
25
|
+
copy_move_options
|
26
|
+
end
|
27
|
+
|
28
|
+
def overwrite_destination
|
29
|
+
to_path = options[:destination].tap { |s| s.slice! @resource.mount_point }
|
30
|
+
to_resource = destination_resource to_path
|
31
|
+
|
32
|
+
if to_resource.exists?
|
33
|
+
to_resource.delete_collection
|
34
|
+
previous_resource_existed = true
|
35
|
+
end
|
36
|
+
|
37
|
+
previous_resource_existed
|
38
|
+
end
|
39
|
+
|
40
|
+
def destination_resource(to_path)
|
41
|
+
@resource.class.new(
|
42
|
+
resource: to_path,
|
43
|
+
req: @request,
|
44
|
+
root_dir: @resource.root_dir
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def response_status(status, previous_resource)
|
49
|
+
return :no_content if status == :created && previous_resource
|
50
|
+
|
51
|
+
response.headers['Location'] = options[:destination] if status == :created
|
52
|
+
|
53
|
+
status
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calligraphy
|
4
|
+
# Responsible for retrieving properties defined on the resource.
|
5
|
+
class Propfind < WebDavRequest
|
6
|
+
include Calligraphy::XML::Utils
|
7
|
+
|
8
|
+
# Executes the WebDAV request for a particular resource.
|
9
|
+
def execute
|
10
|
+
xml = xml_for body: body, node: 'propfind'
|
11
|
+
return :bad_request if xml == :bad_request
|
12
|
+
|
13
|
+
properties = @resource.propfind xml
|
14
|
+
|
15
|
+
builder = xml_builder
|
16
|
+
xml_res = builder.propfind_response(@resource.full_request_path,
|
17
|
+
properties)
|
18
|
+
|
19
|
+
set_xml_content_type
|
20
|
+
|
21
|
+
[:multi_status, xml_res]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calligraphy
|
4
|
+
# Responsible for processing instructions specified in the request body
|
5
|
+
# to set and/or remove properties defined on the resource.
|
6
|
+
class Proppatch < WebDavRequest
|
7
|
+
include Calligraphy::XML::Utils
|
8
|
+
|
9
|
+
# Executes the WebDAV request for a particular resource.
|
10
|
+
def execute
|
11
|
+
return :locked if @resource.locked_to_user? @headers
|
12
|
+
|
13
|
+
# The `propertyupdate` tag contains the request to alter properties
|
14
|
+
# on a resource.
|
15
|
+
xml = xml_for body: body, node: 'propertyupdate'
|
16
|
+
return :bad_request if xml == :bad_request
|
17
|
+
|
18
|
+
actions = @resource.proppatch xml
|
19
|
+
|
20
|
+
builder = xml_builder
|
21
|
+
xml_res = builder.proppatch_response(@resource.full_request_path,
|
22
|
+
actions)
|
23
|
+
|
24
|
+
set_xml_content_type
|
25
|
+
|
26
|
+
[:multi_status, xml_res]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calligraphy
|
4
|
+
# Responsible for replacing the `Get` response entity of the resource.
|
5
|
+
class Put < WebDavRequest
|
6
|
+
# Executes the WebDAV request for a particular resource.
|
7
|
+
def execute
|
8
|
+
return :locked if @resource.locked_to_user? @headers
|
9
|
+
return :method_not_allowed if @resource.collection?
|
10
|
+
|
11
|
+
@resource.write
|
12
|
+
|
13
|
+
[:created, @resource.contents]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Calligraphy
|
4
|
+
# Responsible for removing the lock identified by the lock token in the
|
5
|
+
# request header.
|
2
6
|
class Unlock < WebDavRequest
|
3
|
-
|
7
|
+
# Executes the WebDAV request for a particular resource.
|
8
|
+
def execute
|
4
9
|
return :bad_request if @headers['Lock-Token'].nil?
|
5
10
|
|
6
11
|
@resource.unlock lock_token_header
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calligraphy
|
4
|
+
# Base class for WebDavRequests.
|
5
|
+
#
|
6
|
+
# `WebDavRequest` exposes the `request` method, which is used by the
|
7
|
+
# `WebDavRequestsController` to execute the WebDAV request for a particular
|
8
|
+
# resource. This class also provides private methods for accessing the
|
9
|
+
# request body, setting response headers, and creating XML responses.
|
10
|
+
class WebDavRequest
|
11
|
+
attr_accessor :resource, :response
|
12
|
+
attr_reader :headers, :request
|
13
|
+
|
14
|
+
#:nodoc:
|
15
|
+
def initialize(headers:, request:, response:, resource:)
|
16
|
+
@headers = headers
|
17
|
+
@request = request
|
18
|
+
@response = response
|
19
|
+
@resource = resource
|
20
|
+
end
|
21
|
+
|
22
|
+
# Executes the WebDAV request for a particular resource.
|
23
|
+
def execute
|
24
|
+
raise NotImplemented
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def body
|
30
|
+
@resource.request_body
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_xml_content_type
|
34
|
+
@response.content_type = 'application/xml'
|
35
|
+
end
|
36
|
+
|
37
|
+
def xml_builder
|
38
|
+
protocol = @request.env['SERVER_PROTOCOL']
|
39
|
+
|
40
|
+
Calligraphy::XML::Builder.new server_protocol: protocol
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,152 +1,118 @@
|
|
1
|
-
|
2
|
-
class Builder
|
3
|
-
SUPPORTED_NS_TAGS = %w(
|
4
|
-
creationdate displayname exclusive getcontentlanguage getcontentlength
|
5
|
-
getcontenttype getetag getlastmodified href lockdiscovery lockscope
|
6
|
-
locktype owner write
|
7
|
-
)
|
8
|
-
DAV_PROPERTIES = %w(
|
9
|
-
creationdate displayname getcontentlanguage getcontentlength
|
10
|
-
getcontenttype getetag getlastmodified lockdiscovery
|
11
|
-
resourcetype supportedlock
|
12
|
-
)
|
13
|
-
|
14
|
-
attr_reader :dav_ns, :default_ns, :server_protocol
|
15
|
-
|
16
|
-
def initialize(dav_ns: 'D', server_protocol: 'HTTP/1.1')
|
17
|
-
@dav_ns = dav_ns
|
18
|
-
@default_ns = { "xmlns:#{@dav_ns}" => 'DAV:' }
|
19
|
-
@server_protocol = server_protocol
|
20
|
-
end
|
1
|
+
# frozen_string_literal: true
|
21
2
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
3
|
+
module Calligraphy
|
4
|
+
module XML
|
5
|
+
# Responsible for building XML responses for WebDAV requests.
|
6
|
+
class Builder
|
7
|
+
include Calligraphy::XML::WebDavElements
|
8
|
+
|
9
|
+
attr_reader :dav_ns, :default_ns, :server_protocol
|
31
10
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
11
|
+
#:nodoc:
|
12
|
+
def initialize(dav_ns: 'D', server_protocol: 'HTTP/1.1')
|
13
|
+
@dav_ns = dav_ns
|
14
|
+
@default_ns = { "xmlns:#{@dav_ns}" => 'DAV:' }
|
15
|
+
@server_protocol = server_protocol
|
37
16
|
end
|
38
|
-
end
|
39
17
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
18
|
+
private
|
19
|
+
|
20
|
+
def build(tag)
|
21
|
+
Nokogiri::XML::Builder.new do |xml|
|
22
|
+
xml[@dav_ns].send(tag, @default_ns) { yield xml }
|
23
|
+
end.to_xml
|
45
24
|
end
|
46
|
-
end
|
47
25
|
|
48
|
-
|
26
|
+
def multistatus
|
27
|
+
build :multistatus do |xml|
|
28
|
+
xml.response { yield xml }
|
29
|
+
end
|
30
|
+
end
|
49
31
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
32
|
+
def property_drilldown(xml, property)
|
33
|
+
if property.is_a? Array
|
34
|
+
iterate_and_drilldown xml, property
|
35
|
+
elsif DAV_NS_TAGS.include? property.name
|
36
|
+
supported_ns_tag xml, property
|
37
|
+
elsif property.namespace&.href
|
38
|
+
non_supported_ns_tag xml, property
|
39
|
+
else
|
40
|
+
nil_ns_tag xml, property
|
54
41
|
end
|
55
|
-
end
|
56
|
-
end
|
42
|
+
end
|
57
43
|
|
58
|
-
|
59
|
-
xml.activelock do
|
44
|
+
def iterate_and_drilldown(xml, property_set)
|
60
45
|
property_set.each do |property|
|
61
46
|
property_drilldown xml, property
|
62
47
|
end
|
63
48
|
end
|
64
|
-
end
|
65
49
|
|
66
|
-
|
67
|
-
|
68
|
-
|
50
|
+
def supported_ns_tag(xml, property)
|
51
|
+
if DAV_NS_METHODS.include? property.name
|
52
|
+
return send property.name, xml, property
|
53
|
+
end
|
69
54
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
55
|
+
xml[@dav_ns].send property.name do
|
56
|
+
if property.children
|
57
|
+
iterate_and_drilldown xml, property.children
|
58
|
+
else
|
59
|
+
xml.text property.text
|
60
|
+
end
|
74
61
|
end
|
75
62
|
end
|
76
|
-
end
|
77
63
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
64
|
+
def non_supported_ns_tag(xml, property)
|
65
|
+
xml.send property.name, xmlns: property.namespace.href do
|
66
|
+
if property.children
|
67
|
+
iterate_and_drilldown xml, property.children
|
68
|
+
else
|
69
|
+
xml.text property.text
|
70
|
+
end
|
82
71
|
end
|
83
72
|
end
|
84
|
-
end
|
85
73
|
|
86
|
-
|
87
|
-
|
74
|
+
def nil_ns_tag(xml, property)
|
75
|
+
xml.send property.name, property.text do
|
76
|
+
xml.parent.namespace = nil
|
77
|
+
end
|
78
|
+
end
|
88
79
|
|
89
|
-
xml
|
90
|
-
|
91
|
-
|
80
|
+
def self_closing_tag(xml, text)
|
81
|
+
xml.send text
|
82
|
+
end
|
83
|
+
|
84
|
+
def href(xml, path)
|
85
|
+
xml[@dav_ns].href path
|
92
86
|
end
|
93
|
-
end
|
94
87
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
xml.send 'collection'
|
88
|
+
def prop(xml, property_set)
|
89
|
+
xml[@dav_ns].prop do
|
90
|
+
iterate_and_drilldown xml, property_set
|
99
91
|
end
|
100
|
-
else
|
101
|
-
xml[@dav_ns].resourcetype
|
102
92
|
end
|
103
|
-
end
|
104
93
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
94
|
+
def propstat(xml, property_set, status = :ok)
|
95
|
+
return if property_set.empty?
|
108
96
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
xml.text property.text
|
97
|
+
xml[@dav_ns].propstat do
|
98
|
+
prop xml, property_set
|
99
|
+
status xml, status
|
100
|
+
end
|
114
101
|
end
|
115
|
-
end
|
116
102
|
|
117
|
-
|
118
|
-
|
119
|
-
property.each do |prop|
|
120
|
-
property_drilldown xml, prop
|
121
|
-
end
|
122
|
-
elsif property.children && property.text.nil?
|
123
|
-
xml.send property.name do
|
124
|
-
property.children.each do |child|
|
125
|
-
property_drilldown xml, child
|
126
|
-
end
|
127
|
-
end
|
128
|
-
elsif property.name == 'resourcetype'
|
129
|
-
resourcetype xml, property
|
130
|
-
elsif property.name == 'timeout'
|
131
|
-
timeout xml, property
|
132
|
-
elsif SUPPORTED_NS_TAGS.include? property.name
|
133
|
-
xml[@dav_ns].send property.name do
|
134
|
-
xml.text property.text
|
135
|
-
end
|
136
|
-
elsif property.namespace && property.namespace.href
|
137
|
-
xml.send property.name, xmlns: property.namespace.href do
|
138
|
-
xml.text property.text
|
139
|
-
end
|
140
|
-
else
|
141
|
-
xml.send property.name, property.text do
|
142
|
-
xml.parent.namespace = nil
|
143
|
-
end
|
103
|
+
def status(xml, status)
|
104
|
+
xml[@dav_ns].status status_message status
|
144
105
|
end
|
145
|
-
end
|
146
106
|
|
147
|
-
|
148
|
-
|
149
|
-
|
107
|
+
def status_message(status)
|
108
|
+
status_code = Rack::Utils.status_code status
|
109
|
+
|
110
|
+
[
|
111
|
+
@server_protocol,
|
112
|
+
status_code,
|
113
|
+
Rack::Utils::HTTP_STATUS_CODES[status_code]
|
114
|
+
].join ' '
|
115
|
+
end
|
150
116
|
end
|
151
117
|
end
|
152
118
|
end
|
@@ -1,10 +1,16 @@
|
|
1
|
-
|
2
|
-
class Namespace
|
3
|
-
attr_accessor :href, :prefix
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Calligraphy
|
4
|
+
module XML
|
5
|
+
# Simple XML namespace, used to store a namespace's href and prefix values.
|
6
|
+
class Namespace
|
7
|
+
attr_accessor :href, :prefix
|
8
|
+
|
9
|
+
#:nodoc:
|
10
|
+
def initialize(namespace)
|
11
|
+
@href = namespace.href if namespace.href
|
12
|
+
@prefix = namespace.prefix if namespace.prefix
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
10
16
|
end
|