tilia-dav 3.1.0.pre.alpha6 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.sabre.md +97 -1
  4. data/Gemfile +1 -16
  5. data/Gemfile.lock +44 -41
  6. data/LICENSE +1 -1
  7. data/LICENSE.sabre +1 -1
  8. data/examples/addressbookserver.rb +52 -0
  9. data/examples/calendarserver.rb +58 -0
  10. data/examples/fileserver.rb +57 -0
  11. data/examples/groupwareserver.rb +69 -0
  12. data/examples/sql/mysql.addressbook.sql +28 -0
  13. data/examples/sql/mysql.calendars.sql +64 -0
  14. data/examples/sql/mysql.locks.sql +13 -0
  15. data/examples/sql/mysql.principals.sql +21 -0
  16. data/examples/sql/mysql.propertystorage.sql +8 -0
  17. data/examples/sql/mysql.users.sql +9 -0
  18. data/examples/sql/pgsql.addressbook.sql +52 -0
  19. data/examples/sql/pgsql.calendars.sql +93 -0
  20. data/examples/sql/pgsql.locks.sql +19 -0
  21. data/examples/sql/pgsql.principals.sql +38 -0
  22. data/examples/sql/pgsql.propertystorage.sql +13 -0
  23. data/examples/sql/pgsql.users.sql +14 -0
  24. data/examples/sql/sqlite.addressbooks.sql +28 -0
  25. data/examples/sql/sqlite.calendars.sql +64 -0
  26. data/examples/sql/sqlite.locks.sql +12 -0
  27. data/examples/sql/sqlite.principals.sql +20 -0
  28. data/examples/sql/sqlite.propertystorage.sql +10 -0
  29. data/examples/sql/sqlite.users.sql +9 -0
  30. data/lib/tilia/cal_dav/ics_export_plugin.rb +1 -1
  31. data/lib/tilia/cal_dav/plugin.rb +27 -11
  32. data/lib/tilia/cal_dav/schedule/i_mip_plugin.rb +2 -2
  33. data/lib/tilia/cal_dav/schedule/plugin.rb +7 -0
  34. data/lib/tilia/dav/auth/backend.rb +1 -0
  35. data/lib/tilia/dav/auth/backend/abstract_basic.rb +3 -2
  36. data/lib/tilia/dav/auth/backend/abstract_bearer.rb +116 -0
  37. data/lib/tilia/dav/auth/backend/abstract_digest.rb +3 -2
  38. data/lib/tilia/dav/auth/backend/apache.rb +2 -1
  39. data/lib/tilia/dav/auth/backend/sequel.rb +2 -9
  40. data/lib/tilia/dav/client.rb +29 -3
  41. data/lib/tilia/dav/core_plugin.rb +1 -2
  42. data/lib/tilia/dav/server.rb +16 -4
  43. data/lib/tilia/dav/temporary_file_filter_plugin.rb +3 -0
  44. data/lib/tilia/dav/tree.rb +4 -3
  45. data/lib/tilia/dav/version.rb +1 -1
  46. data/lib/tilia/dav/xml/element/response.rb +20 -2
  47. data/lib/tilia/dav_acl/principal_backend/sequel.rb +50 -6
  48. data/test/cal_dav/ics_export_plugin_test.rb +1 -0
  49. data/test/cal_dav/plugin_test.rb +4 -4
  50. data/test/cal_dav/schedule/plugin_properties_test.rb +51 -0
  51. data/test/card_dav/backend/sequel_my_sql_test.rb +3 -3
  52. data/test/card_dav/vcf_export_test.rb +11 -1
  53. data/test/dav/auth/backend/abstract_bearer_test.rb +71 -0
  54. data/test/dav/client_test.rb +42 -4
  55. data/test/dav/core_plugin_test.rb +12 -0
  56. data/test/dav/fs_ext/server_test.rb +1 -1
  57. data/test/dav/http_copy_test.rb +185 -0
  58. data/test/dav/mock/collection.rb +6 -9
  59. data/test/dav/mock/file.rb +10 -9
  60. data/test/dav/mock/streaming_file.rb +1 -3
  61. data/test/dav/server_events_test.rb +8 -6
  62. data/test/dav/server_range_test.rb +135 -155
  63. data/test/dav/server_simple_test.rb +14 -0
  64. data/test/dav/xml/element/response_test.rb +56 -1
  65. data/test/dav/xml/property/href_test.rb +14 -0
  66. data/test/dav_acl/principal_backend/abstract_sequel_test.rb +16 -0
  67. data/test/dav_acl/principal_backend/mock.rb +1 -1
  68. data/test/dav_server_test.rb +1 -1
  69. data/tilia-dav.gemspec +2 -2
  70. metadata +38 -14
  71. data/test/dav/copy_test.rb +0 -33
  72. data/test/dav/server_copy_move_test.rb +0 -164
@@ -42,7 +42,7 @@ module Tilia
42
42
  @server.http_request = req
43
43
  @server.exec
44
44
 
45
- assert_equal(nil, @server.http_response.status)
45
+ assert_equal(500, @server.http_response.status)
46
46
  end
47
47
 
48
48
  def before_bind_cancel_handler(_path)
@@ -85,11 +85,13 @@ module Tilia
85
85
  end
86
86
  )
87
87
 
88
- @server.invoke_method(
89
- Http::Request.new('BLABLA', '/'),
90
- Http::Response.new,
91
- false
92
- )
88
+ assert_raises(Exception) do
89
+ @server.invoke_method(
90
+ Http::Request.new('BLABLA', '/'),
91
+ Http::Response.new,
92
+ false
93
+ )
94
+ end
93
95
 
94
96
  assert_equal(2, k)
95
97
  end
@@ -2,25 +2,33 @@ require 'test_helper'
2
2
 
3
3
  module Tilia
4
4
  module Dav
5
- class ServerRangeTest < AbstractServer
6
- def root_node
7
- FsExt::Directory.new(@temp_dir)
5
+ class ServerRangeTest < DavServerTest
6
+ def setup
7
+ @setup_files = true
8
+ super
9
+ @server.create_file('files/test.txt', 'Test contents')
10
+ @last_modified = Http::Util.to_http_date(
11
+ Time.zone.at(@server.tree.node_for_path('files/test.txt').last_modified)
12
+ )
13
+
14
+ stream = StringIO.new
15
+ stream.write('Test contents')
16
+ stream.rewind
17
+ streaming_file = Mock::StreamingFile.new(
18
+ 'no-seeking.txt',
19
+ stream
20
+ )
21
+ streaming_file.size = 12
22
+ @server.tree.node_for_path('files').add_node(streaming_file)
8
23
  end
9
24
 
10
25
  def test_range
11
- server_vars = {
12
- 'PATH_INFO' => '/test.txt',
13
- 'REQUEST_METHOD' => 'GET',
14
- 'HTTP_RANGE' => 'bytes=2-5'
15
- }
16
- filename = "#{@temp_dir}/test.txt"
17
-
18
- request = Http::Sapi.create_from_server_array(server_vars)
19
- @server.http_request = request
20
- @server.exec
21
-
22
- stat = ::File.stat(filename)
23
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
26
+ request = Http::Request.new(
27
+ 'GET',
28
+ '/files/test.txt',
29
+ 'Range' => 'bytes=2-5'
30
+ )
31
+ response = request(request)
24
32
 
25
33
  assert_equal(
26
34
  {
@@ -28,30 +36,23 @@ module Tilia
28
36
  'Content-Type' => ['application/octet-stream'],
29
37
  'Content-Length' => [4],
30
38
  'Content-Range' => ['bytes 2-5/13'],
31
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
32
- 'ETag' => [etag]
39
+ 'Last-Modified' => [@last_modified],
40
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
33
41
  },
34
- @response.headers
42
+ response.headers
35
43
  )
36
44
 
37
- assert_equal(206, @response.status)
38
- assert_equal('st c', @response.body_as_string[0, 4])
45
+ assert_equal(206, response.status)
46
+ assert_equal('st c', response.body_as_string)
39
47
  end
40
48
 
41
49
  def test_start_range
42
- server_vars = {
43
- 'PATH_INFO' => '/test.txt',
44
- 'REQUEST_METHOD' => 'GET',
45
- 'HTTP_RANGE' => 'bytes=2-'
46
- }
47
- filename = "#{@temp_dir}/test.txt"
48
-
49
- request = Http::Sapi.create_from_server_array(server_vars)
50
- @server.http_request = request
51
- @server.exec
52
-
53
- stat = ::File.stat(filename)
54
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
50
+ request = Http::Request.new(
51
+ 'GET',
52
+ '/files/test.txt',
53
+ 'Range' => 'bytes=2-'
54
+ )
55
+ response = request(request)
55
56
 
56
57
  assert_equal(
57
58
  {
@@ -59,30 +60,23 @@ module Tilia
59
60
  'Content-Type' => ['application/octet-stream'],
60
61
  'Content-Length' => [11],
61
62
  'Content-Range' => ['bytes 2-12/13'],
62
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
63
- 'ETag' => [etag]
63
+ 'Last-Modified' => [@last_modified],
64
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
64
65
  },
65
- @response.headers
66
+ response.headers
66
67
  )
67
68
 
68
- assert_equal(206, @response.status)
69
- assert_equal('st contents', @response.body_as_string[0, 11])
69
+ assert_equal(206, response.status)
70
+ assert_equal('st contents', response.body_as_string)
70
71
  end
71
72
 
72
73
  def test_end_range
73
- server_vars = {
74
- 'PATH_INFO' => '/test.txt',
75
- 'REQUEST_METHOD' => 'GET',
76
- 'HTTP_RANGE' => 'bytes=-8'
77
- }
78
- filename = "#{@temp_dir}/test.txt"
79
-
80
- request = Http::Sapi.create_from_server_array(server_vars)
81
- @server.http_request = request
82
- @server.exec
83
-
84
- stat = ::File.stat(filename)
85
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
74
+ request = Http::Request.new(
75
+ 'GET',
76
+ '/files/test.txt',
77
+ 'Range' => 'bytes=-8'
78
+ )
79
+ response = request(request)
86
80
 
87
81
  assert_equal(
88
82
  {
@@ -90,61 +84,70 @@ module Tilia
90
84
  'Content-Type' => ['application/octet-stream'],
91
85
  'Content-Length' => [8],
92
86
  'Content-Range' => ['bytes 5-12/13'],
93
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
94
- 'ETag' => [etag]
87
+ 'Last-Modified' => [@last_modified],
88
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
95
89
  },
96
- @response.headers
90
+ response.headers
97
91
  )
98
92
 
99
- assert_equal(206, @response.status)
100
- assert_equal('contents', @response.body_as_string[0, 8])
93
+ assert_equal(206, response.status)
94
+ assert_equal('contents', response.body_as_string)
101
95
  end
102
96
 
103
97
  def test_too_high_range
104
- server_vars = {
105
- 'PATH_INFO' => '/test.txt',
106
- 'REQUEST_METHOD' => 'GET',
107
- 'HTTP_RANGE' => 'bytes=100-200'
108
- }
109
-
110
- request = Http::Sapi.create_from_server_array(server_vars)
111
- @server.http_request = request
112
- @server.exec
98
+ request = Http::Request.new(
99
+ 'GET',
100
+ '/files/test.txt',
101
+ 'Range' => 'bytes=100-200'
102
+ )
103
+ response = request(request)
113
104
 
114
- assert_equal(416, @response.status)
105
+ assert_equal(416, response.status)
115
106
  end
116
107
 
117
108
  def test_crazy_range
118
- server_vars = {
119
- 'PATH_INFO' => '/test.txt',
120
- 'REQUEST_METHOD' => 'GET',
121
- 'HTTP_RANGE' => 'bytes=8-4'
122
- }
123
-
124
- request = Http::Sapi.create_from_server_array(server_vars)
125
- @server.http_request = request
126
- @server.exec
109
+ request = Http::Request.new(
110
+ 'GET',
111
+ '/files/test.txt',
112
+ 'Range' => 'bytes=8-4'
113
+ )
114
+ response = request(request)
127
115
 
128
- assert_equal(416, @response.status)
116
+ assert_equal(416, response.status)
129
117
  end
130
118
 
131
- def test_if_range_etag
132
- node = @server.tree.node_for_path('test.txt')
119
+ def test_non_seekable_stream
120
+ request = Http::Request.new(
121
+ 'GET',
122
+ '/files/no-seeking.txt',
123
+ 'Range' => 'bytes=2-5'
124
+ )
125
+ response = request(request)
133
126
 
134
- server_vars = {
135
- 'PATH_INFO' => '/test.txt',
136
- 'REQUEST_METHOD' => 'GET',
137
- 'HTTP_RANGE' => 'bytes=2-5',
138
- 'HTTP_IF_RANGE' => node.etag
139
- }
140
- filename = "#{@temp_dir}/test.txt"
127
+ assert_equal(206, response.status, response)
128
+ assert_equal(
129
+ {
130
+ 'X-Sabre-Version' => [Version::VERSION],
131
+ 'Content-Type' => ['application/octet-stream'],
132
+ 'Content-Length' => [4],
133
+ 'Content-Range' => ['bytes 2-5/12'],
134
+ # 'ETag' => ['"' . md5('Test contents') . '"'],
135
+ 'Last-Modified' => [@last_modified]
136
+ },
137
+ response.headers
138
+ )
141
139
 
142
- request = Http::Sapi.create_from_server_array(server_vars)
143
- @server.http_request = request
144
- @server.exec
140
+ assert_equal('st c', response.body_as_string)
141
+ end
145
142
 
146
- stat = ::File.stat(filename)
147
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
143
+ def test_if_range_etag
144
+ request = Http::Request.new(
145
+ 'GET',
146
+ '/files/test.txt',
147
+ 'Range' => 'bytes=2-5',
148
+ 'If-Range' => '"' + Digest::MD5.hexdigest('Test contents') + '"'
149
+ )
150
+ response = request(request)
148
151
 
149
152
  assert_equal(
150
153
  {
@@ -152,64 +155,48 @@ module Tilia
152
155
  'Content-Type' => ['application/octet-stream'],
153
156
  'Content-Length' => [4],
154
157
  'Content-Range' => ['bytes 2-5/13'],
155
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
156
- 'ETag' => [etag]
158
+ 'Last-Modified' => [@last_modified],
159
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
157
160
  },
158
- @response.headers
161
+ response.headers
159
162
  )
160
163
 
161
- assert_equal(206, @response.status)
162
- assert_equal('st c', @response.body_as_string[0, 4])
164
+ assert_equal(206, response.status)
165
+ assert_equal('st c', response.body_as_string)
163
166
  end
164
167
 
165
168
  def test_if_range_etag_incorrect
166
- node = @server.tree.node_for_path('test.txt')
167
-
168
- server_vars = {
169
- 'PATH_INFO' => '/test.txt',
170
- 'REQUEST_METHOD' => 'GET',
171
- 'HTTP_RANGE' => 'bytes=2-5',
172
- 'HTTP_IF_RANGE' => node.etag + 'blabla'
173
- }
174
- filename = "#{@temp_dir}/test.txt"
175
-
176
- request = Http::Sapi.create_from_server_array(server_vars)
177
- @server.http_request = request
178
- @server.exec
179
-
180
- stat = ::File.stat(filename)
181
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
169
+ request = Http::Request.new(
170
+ 'GET',
171
+ '/files/test.txt',
172
+ 'Range' => 'bytes=2-5',
173
+ 'If-Range' => '"foobar"'
174
+ )
175
+ response = request(request)
182
176
 
183
177
  assert_equal(
184
178
  {
185
179
  'X-Sabre-Version' => [Version::VERSION],
186
180
  'Content-Type' => ['application/octet-stream'],
187
181
  'Content-Length' => [13],
188
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
189
- 'ETag' => [etag]
182
+ 'Last-Modified' => [@last_modified],
183
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
190
184
  },
191
- @response.headers
185
+ response.headers
192
186
  )
193
187
 
194
- assert_equal(200, @response.status)
195
- assert_equal('Test contents', @response.body_as_string)
188
+ assert_equal(200, response.status)
189
+ assert_equal('Test contents', response.body_as_string)
196
190
  end
197
191
 
198
192
  def test_if_range_modification_date
199
- server_vars = {
200
- 'PATH_INFO' => '/test.txt',
201
- 'REQUEST_METHOD' => 'GET',
202
- 'HTTP_RANGE' => 'bytes=2-5',
203
- 'HTTP_IF_RANGE' => 'tomorrow'
204
- }
205
- filename = "#{@temp_dir}/test.txt"
206
-
207
- request = Http::Sapi.create_from_server_array(server_vars)
208
- @server.http_request = request
209
- @server.exec
210
-
211
- stat = ::File.stat(filename)
212
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
193
+ request = Http::Request.new(
194
+ 'GET',
195
+ '/files/test.txt',
196
+ 'Range' => 'bytes=2-5',
197
+ 'If-Range' => 'tomorrow'
198
+ )
199
+ response = request(request)
213
200
 
214
201
  assert_equal(
215
202
  {
@@ -217,45 +204,38 @@ module Tilia
217
204
  'Content-Type' => ['application/octet-stream'],
218
205
  'Content-Length' => [4],
219
206
  'Content-Range' => ['bytes 2-5/13'],
220
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
221
- 'ETag' => [etag]
207
+ 'Last-Modified' => [@last_modified],
208
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
222
209
  },
223
- @response.headers
210
+ response.headers
224
211
  )
225
212
 
226
- assert_equal(206, @response.status)
227
- assert_equal('st c', @response.body_as_string[0, 4])
213
+ assert_equal(206, response.status)
214
+ assert_equal('st c', response.body_as_string)
228
215
  end
229
216
 
230
217
  def test_if_range_modification_date_modified
231
- server_vars = {
232
- 'PATH_INFO' => '/test.txt',
233
- 'REQUEST_METHOD' => 'GET',
234
- 'HTTP_RANGE' => 'bytes=2-5',
235
- 'HTTP_IF_RANGE' => '-2 years'
236
- }
237
- filename = "#{@temp_dir}/test.txt"
238
-
239
- request = Http::Sapi.create_from_server_array(server_vars)
240
- @server.http_request = request
241
- @server.exec
242
-
243
- stat = ::File.stat(filename)
244
- etag = '"' + Digest::SHA1.hexdigest(stat.ino.to_s + stat.size.to_s + stat.mtime.to_s) + '"'
218
+ request = Http::Request.new(
219
+ 'GET',
220
+ '/files/test.txt',
221
+ 'Range' => 'bytes=2-5',
222
+ 'If-Range' => '-2 years'
223
+ )
224
+ response = request(request)
245
225
 
246
226
  assert_equal(
247
227
  {
248
228
  'X-Sabre-Version' => [Version::VERSION],
249
229
  'Content-Type' => ['application/octet-stream'],
250
230
  'Content-Length' => [13],
251
- 'Last-Modified' => [Http::Util.to_http_date(::File.mtime(filename))],
252
- 'ETag' => [etag]
231
+ 'Last-Modified' => [@last_modified],
232
+ 'ETag' => ['"' + Digest::MD5.hexdigest('Test contents') + '"']
253
233
  },
254
- @response.headers
234
+ response.headers
255
235
  )
256
236
 
257
- assert_equal(200, @response.status)
258
- assert_equal('Test contents', @response.body_as_string)
237
+ assert_equal(200, response.status)
238
+ assert_equal('Test contents', response.body_as_string)
259
239
  end
260
240
  end
261
241
  end
@@ -386,6 +386,20 @@ module Tilia
386
386
 
387
387
  assert_equal(expected, result)
388
388
  end
389
+
390
+ # There are certain cases where no HTTP status may be set. We need to
391
+ # intercept these and set it to a default error message.
392
+ def test_no_http_status_set
393
+ @server.on(
394
+ 'method:GET',
395
+ -> (request, response) { false },
396
+ 1
397
+ )
398
+
399
+ @server.http_request = Http::Request.new('GET', '/')
400
+ @server.exec
401
+ assert_equal(500, @response.status)
402
+ end
389
403
  end
390
404
  end
391
405
  end
@@ -165,7 +165,62 @@ XML
165
165
  assert_instance_equal(expected, result['value'])
166
166
  end
167
167
 
168
- # In the case of {DAV:}prop, a deserializer should never get called, if
168
+ def test_serialize_urlencoding
169
+ inner_props = {
170
+ 200 => {
171
+ '{DAV:}displayname' => 'my file',
172
+ }
173
+ }
174
+
175
+ property = Response.new('space here', inner_props)
176
+
177
+ xml = write('{DAV:}root' => {'{DAV:}response' => property})
178
+ expected = <<XML
179
+ <?xml version="1.0"?>
180
+ <d:root xmlns:d="DAV:">
181
+ <d:response>
182
+ <d:href>/space%20here</d:href>
183
+ <d:propstat>
184
+ <d:prop>
185
+ <d:displayname>my file</d:displayname>
186
+ </d:prop>
187
+ <d:status>HTTP/1.1 200 OK</d:status>
188
+ </d:propstat>
189
+ </d:response>
190
+ </d:root>
191
+ XML
192
+ assert_xml_equal(expected, xml)
193
+ end
194
+
195
+ # The WebDAV spec _requires_ at least one DAV:propstat to appear for
196
+ # every DAV:response. In some circumstances however, there are no
197
+ # properties to encode.
198
+ #
199
+ # In those cases we MUST specify at least one DAV:propstat anyway, with
200
+ # no properties.
201
+ def test_serialize_no_properties
202
+
203
+ inner_props = []
204
+
205
+ property = Response.new('uri', inner_props)
206
+
207
+ xml = write('{DAV:}root' => { '{DAV:}response' => property})
208
+ expected = <<XML
209
+ <?xml version="1.0"?>
210
+ <d:root xmlns:d="DAV:">
211
+ <d:response>
212
+ <d:href>/uri</d:href>
213
+ <d:propstat>
214
+ <d:prop />
215
+ <d:status>HTTP/1.1 418 I\'m a teapot</d:status>
216
+ </d:propstat>
217
+ </d:response>
218
+ </d:root>
219
+ XML
220
+ assert_xml_equal(expected, xml)
221
+ end
222
+
223
+ # In the case of {DAV:}prop, a deserializer should never get called, if
169
224
  # the property element is empty.
170
225
  def test_deserialize_complex_property_empty
171
226
  xml = <<XML