async-caldav 1.2.0 → 1.2.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 +4 -4
- data/lib/async/caldav/client/addressbook.rb +17 -25
- data/lib/async/caldav/client/calendar.rb +31 -36
- data/lib/async/caldav/client.rb +24 -23
- data/lib/async/caldav/handlers/propfind.rb +71 -81
- data/lib/async/caldav/handlers/proppatch.rb +8 -11
- data/lib/async/caldav/handlers/report.rb +64 -82
- data/lib/async/caldav/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0517bf6a8c74d0d76b09a3ea8115e6dd65114f3991e25d3c97578ec69cb64a9c
|
|
4
|
+
data.tar.gz: 5f83b3cb2b118fe7ef22eef7140df410eea5f167f41e86b317dd260dbc6e0fc7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 46c8c7806937d6945f1a1766a8ab6999d272b0e3d599a34bdc33fab780f10a106ae0e36ae380a45f17bf9a7af5a5bb302b9c8f9ec9cd1a8c84584e7b30baebe9
|
|
7
|
+
data.tar.gz: ec019c2ee4e1d7e53ab85dabcb74098c6f03cbf97e3808df67f2e7ebd270a994a67ddd2cd2de970e8080b7f2588955534c9587469f67228bffac8ddef333b683
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "scampi"
|
|
5
|
+
require "builder"
|
|
5
6
|
|
|
6
7
|
module Async
|
|
7
8
|
module Caldav
|
|
@@ -16,22 +17,13 @@ module Async
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def contacts(filter: nil)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
#{filter}
|
|
25
|
-
</cr:addressbook-query>
|
|
26
|
-
XML
|
|
27
|
-
else
|
|
28
|
-
<<~XML
|
|
29
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
30
|
-
<cr:addressbook-query xmlns:d="DAV:" xmlns:cr="urn:ietf:params:xml:ns:carddav">
|
|
31
|
-
<d:prop><d:getetag/><cr:address-data/></d:prop>
|
|
32
|
-
</cr:addressbook-query>
|
|
33
|
-
XML
|
|
20
|
+
x = Builder::XmlMarkup.new
|
|
21
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
22
|
+
x.tag!("cr:addressbook-query", "xmlns:d" => "DAV:", "xmlns:cr" => "urn:ietf:params:xml:ns:carddav") do
|
|
23
|
+
x.tag!("d:prop") { x.tag!("d:getetag"); x.tag!("cr:address-data") }
|
|
24
|
+
x << filter if filter
|
|
34
25
|
end
|
|
26
|
+
body = x.target!
|
|
35
27
|
|
|
36
28
|
status, _, resp_body = @client.request('REPORT', @path, body: body, headers: { 'Content-Type' => 'text/xml' })
|
|
37
29
|
raise Error, "REPORT failed: #{status}" unless status == 207
|
|
@@ -95,17 +87,17 @@ module Async
|
|
|
95
87
|
end
|
|
96
88
|
|
|
97
89
|
def proppatch(displayname: nil)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
x = Builder::XmlMarkup.new
|
|
91
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
92
|
+
x.tag!("d:propertyupdate", "xmlns:d" => "DAV:") do
|
|
93
|
+
x.tag!("d:set") do
|
|
94
|
+
x.tag!("d:prop") do
|
|
95
|
+
x.tag!("d:displayname", displayname) if displayname
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
107
99
|
|
|
108
|
-
status, = @client.request('PROPPATCH', @path, body:
|
|
100
|
+
status, = @client.request('PROPPATCH', @path, body: x.target!, headers: { 'Content-Type' => 'text/xml' })
|
|
109
101
|
raise Error, "PROPPATCH failed: #{status}" unless status == 207
|
|
110
102
|
|
|
111
103
|
@displayname = displayname if displayname
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "scampi"
|
|
5
|
+
require "builder"
|
|
5
6
|
|
|
6
7
|
module Async
|
|
7
8
|
module Caldav
|
|
@@ -19,22 +20,13 @@ module Async
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def events(filter: nil)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#{filter}
|
|
28
|
-
</c:calendar-query>
|
|
29
|
-
XML
|
|
30
|
-
else
|
|
31
|
-
<<~XML
|
|
32
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
33
|
-
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
34
|
-
<d:prop><d:getetag/><c:calendar-data/></d:prop>
|
|
35
|
-
</c:calendar-query>
|
|
36
|
-
XML
|
|
23
|
+
x = Builder::XmlMarkup.new
|
|
24
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
25
|
+
x.tag!("c:calendar-query", "xmlns:d" => "DAV:", "xmlns:c" => "urn:ietf:params:xml:ns:caldav") do
|
|
26
|
+
x.tag!("d:prop") { x.tag!("d:getetag"); x.tag!("c:calendar-data") }
|
|
27
|
+
x << filter if filter
|
|
37
28
|
end
|
|
29
|
+
body = x.target!
|
|
38
30
|
|
|
39
31
|
status, _, resp_body = @client.request('REPORT', @path, body: body, headers: { 'Content-Type' => 'text/xml' })
|
|
40
32
|
raise Error, "REPORT failed: #{status}" unless status == 207
|
|
@@ -104,19 +96,19 @@ module Async
|
|
|
104
96
|
end
|
|
105
97
|
|
|
106
98
|
def proppatch(displayname: nil, description: nil, color: nil)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
status, = @client.request('PROPPATCH', @path, body:
|
|
99
|
+
x = Builder::XmlMarkup.new
|
|
100
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
101
|
+
x.tag!("d:propertyupdate", "xmlns:d" => "DAV:", "xmlns:c" => "urn:ietf:params:xml:ns:caldav", "xmlns:x" => "http://apple.com/ns/ical/") do
|
|
102
|
+
x.tag!("d:set") do
|
|
103
|
+
x.tag!("d:prop") do
|
|
104
|
+
x.tag!("d:displayname", displayname) if displayname
|
|
105
|
+
x.tag!("c:calendar-description", description) if description
|
|
106
|
+
x.tag!("x:calendar-color", color) if color
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
status, = @client.request('PROPPATCH', @path, body: x.target!, headers: { 'Content-Type' => 'text/xml' })
|
|
120
112
|
raise Error, "PROPPATCH failed: #{status}" unless status == 207
|
|
121
113
|
|
|
122
114
|
@displayname = displayname if displayname
|
|
@@ -126,14 +118,17 @@ module Async
|
|
|
126
118
|
end
|
|
127
119
|
|
|
128
120
|
def sync(token: nil)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
121
|
+
x = Builder::XmlMarkup.new
|
|
122
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
123
|
+
x.tag!("d:sync-collection", "xmlns:d" => "DAV:") do
|
|
124
|
+
x.tag!("d:prop") { x.tag!("d:getetag") }
|
|
125
|
+
if token
|
|
126
|
+
x.tag!("d:sync-token", token)
|
|
127
|
+
else
|
|
128
|
+
x.tag!("d:sync-token")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
body = x.target!
|
|
137
132
|
|
|
138
133
|
status, _, resp_body = @client.request('REPORT', @path, body: body, headers: { 'Content-Type' => 'text/xml' })
|
|
139
134
|
|
data/lib/async/caldav/client.rb
CHANGED
|
@@ -82,18 +82,18 @@ module Async
|
|
|
82
82
|
|
|
83
83
|
def create_calendar(name, displayname: nil, description: nil, color: nil)
|
|
84
84
|
path = "/calendars/#{@user}/#{name}/"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
status, = request('MKCALENDAR', path, body:
|
|
85
|
+
x = Builder::XmlMarkup.new
|
|
86
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
87
|
+
x.tag!("c:mkcalendar", "xmlns:d" => "DAV:", "xmlns:c" => "urn:ietf:params:xml:ns:caldav") do
|
|
88
|
+
x.tag!("d:set") do
|
|
89
|
+
x.tag!("d:prop") do
|
|
90
|
+
x.tag!("d:displayname", displayname || name) if displayname || name
|
|
91
|
+
x.tag!("c:calendar-description", description) if description
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
status, = request('MKCALENDAR', path, body: x.target!, headers: { 'Content-Type' => 'text/xml' })
|
|
97
97
|
raise Error, "MKCALENDAR failed: #{status}" unless status == 201
|
|
98
98
|
|
|
99
99
|
Calendar.new(self, path, displayname: displayname || name, description: description, color: color)
|
|
@@ -101,17 +101,18 @@ module Async
|
|
|
101
101
|
|
|
102
102
|
def create_addressbook(name, displayname: nil)
|
|
103
103
|
path = "/addressbooks/#{@user}/#{name}/"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
x = Builder::XmlMarkup.new
|
|
105
|
+
x.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
106
|
+
x.tag!("d:mkcol", "xmlns:d" => "DAV:", "xmlns:cr" => "urn:ietf:params:xml:ns:carddav") do
|
|
107
|
+
x.tag!("d:set") do
|
|
108
|
+
x.tag!("d:prop") do
|
|
109
|
+
x.tag!("d:resourcetype") { x.tag!("d:collection"); x.tag!("cr:addressbook") }
|
|
110
|
+
x.tag!("d:displayname", displayname || name)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
status, = request('MKCOL', path, body: x.target!, headers: { 'Content-Type' => 'text/xml' })
|
|
115
116
|
raise Error, "MKCOL failed: #{status}" unless status == 201
|
|
116
117
|
|
|
117
118
|
Addressbook.new(self, path, displayname: displayname || name)
|
|
@@ -13,6 +13,7 @@ module Async
|
|
|
13
13
|
def call(path:, storage:, user:, headers: {}, body: nil, **)
|
|
14
14
|
depth = headers['depth'] || '1'
|
|
15
15
|
propname = body&.include?('propname')
|
|
16
|
+
build = propname ? :build_propname : :build_propfind
|
|
16
17
|
|
|
17
18
|
col_path = path.ensure_trailing_slash
|
|
18
19
|
collection = storage.get_collection(col_path.to_s)
|
|
@@ -23,97 +24,86 @@ module Async
|
|
|
23
24
|
return [404, { 'content-type' => 'text/plain' }, ['Not Found']]
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
xml = Protocol::Caldav::Multistatus.new.to_xml do |x|
|
|
28
|
+
if collection
|
|
29
|
+
col = Protocol::Caldav::Collection.new(
|
|
30
|
+
path: col_path,
|
|
31
|
+
type: collection[:type],
|
|
32
|
+
displayname: collection[:displayname],
|
|
33
|
+
description: collection[:description],
|
|
34
|
+
color: collection[:color],
|
|
35
|
+
props: collection[:props]
|
|
36
|
+
)
|
|
37
|
+
col.send(build, x)
|
|
38
|
+
|
|
39
|
+
if depth != '0'
|
|
40
|
+
storage.list_collections(col_path.to_s).each do |child_path, child_data|
|
|
41
|
+
child_p = Protocol::Caldav::Path.new(child_path, storage_class: storage)
|
|
42
|
+
child_col = Protocol::Caldav::Collection.new(
|
|
43
|
+
path: child_p,
|
|
44
|
+
type: child_data[:type],
|
|
45
|
+
displayname: child_data[:displayname],
|
|
46
|
+
description: child_data[:description],
|
|
47
|
+
color: child_data[:color],
|
|
48
|
+
props: child_data[:props]
|
|
49
|
+
)
|
|
50
|
+
child_col.send(build, x)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
storage.list_items(col_path.to_s).each do |item_path, data|
|
|
54
|
+
item_p = Protocol::Caldav::Path.new(item_path, storage_class: storage)
|
|
55
|
+
item = Protocol::Caldav::Item.new(
|
|
56
|
+
path: item_p,
|
|
57
|
+
body: data[:body],
|
|
58
|
+
content_type: data[:content_type],
|
|
59
|
+
etag: data[:etag]
|
|
60
|
+
)
|
|
61
|
+
item.send(build, x)
|
|
62
|
+
end
|
|
52
63
|
end
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# Still list child collections/items for depth=1
|
|
79
|
-
if depth != '0'
|
|
80
|
-
storage.list_collections(col_path.to_s).each do |child_path, child_data|
|
|
81
|
-
child_p = Protocol::Caldav::Path.new(child_path, storage_class: storage)
|
|
82
|
-
child_col = Protocol::Caldav::Collection.new(
|
|
83
|
-
path: child_p,
|
|
84
|
-
type: child_data[:type],
|
|
85
|
-
displayname: child_data[:displayname],
|
|
86
|
-
description: child_data[:description],
|
|
87
|
-
color: child_data[:color],
|
|
88
|
-
props: child_data[:props]
|
|
89
|
-
)
|
|
90
|
-
responses << (propname ? child_col.to_propname_xml : child_col.to_propfind_xml)
|
|
64
|
+
elsif item_data
|
|
65
|
+
item = Protocol::Caldav::Item.new(
|
|
66
|
+
path: path,
|
|
67
|
+
body: item_data[:body],
|
|
68
|
+
content_type: item_data[:content_type],
|
|
69
|
+
etag: item_data[:etag]
|
|
70
|
+
)
|
|
71
|
+
item.send(build, x)
|
|
72
|
+
else
|
|
73
|
+
build_discovery(x, path, user)
|
|
74
|
+
|
|
75
|
+
if depth != '0'
|
|
76
|
+
storage.list_collections(col_path.to_s).each do |child_path, child_data|
|
|
77
|
+
child_p = Protocol::Caldav::Path.new(child_path, storage_class: storage)
|
|
78
|
+
child_col = Protocol::Caldav::Collection.new(
|
|
79
|
+
path: child_p,
|
|
80
|
+
type: child_data[:type],
|
|
81
|
+
displayname: child_data[:displayname],
|
|
82
|
+
description: child_data[:description],
|
|
83
|
+
color: child_data[:color],
|
|
84
|
+
props: child_data[:props]
|
|
85
|
+
)
|
|
86
|
+
child_col.send(build, x)
|
|
87
|
+
end
|
|
91
88
|
end
|
|
92
89
|
end
|
|
93
90
|
end
|
|
94
91
|
|
|
95
|
-
xml = Protocol::Caldav::Multistatus.new(responses).to_xml
|
|
96
92
|
[207, Protocol::Caldav::Constants::DAV_HEADERS, [xml]]
|
|
97
93
|
end
|
|
98
94
|
|
|
99
|
-
def
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<cr:addressbook-home-set><d:href>/addressbooks/#{user}/</d:href></cr:addressbook-home-set>
|
|
109
|
-
</d:prop>
|
|
110
|
-
<d:status>HTTP/1.1 200 OK</d:status>
|
|
111
|
-
</d:propstat>
|
|
112
|
-
</d:response>
|
|
113
|
-
XML
|
|
95
|
+
def build_discovery(xml, path, user)
|
|
96
|
+
Protocol::Caldav::XmlBuilder.response(xml, href: path.to_s) do
|
|
97
|
+
Protocol::Caldav::XmlBuilder.propstat_ok(xml) do
|
|
98
|
+
xml.tag!("d:resourcetype") { xml.tag!("d:collection") }
|
|
99
|
+
xml.tag!("d:current-user-principal") { xml.tag!("d:href", "/#{user}/") }
|
|
100
|
+
xml.tag!("c:calendar-home-set") { xml.tag!("d:href", "/calendars/#{user}/") }
|
|
101
|
+
xml.tag!("cr:addressbook-home-set") { xml.tag!("d:href", "/addressbooks/#{user}/") }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
114
104
|
end
|
|
115
105
|
|
|
116
|
-
private_class_method :
|
|
106
|
+
private_class_method :build_discovery
|
|
117
107
|
end
|
|
118
108
|
end
|
|
119
109
|
end
|
|
@@ -37,17 +37,14 @@ module Async
|
|
|
37
37
|
|
|
38
38
|
storage.update_collection(col_path.to_s, updates)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
XML
|
|
49
|
-
|
|
50
|
-
xml = Protocol::Caldav::Multistatus.new([response_xml]).to_xml
|
|
40
|
+
xml = Protocol::Caldav::Multistatus.new.to_xml do |x|
|
|
41
|
+
Protocol::Caldav::XmlBuilder.response(x, href: col_path.to_s) do
|
|
42
|
+
x.tag!("d:propstat") do
|
|
43
|
+
x.tag!("d:prop")
|
|
44
|
+
x.tag!("d:status", "HTTP/1.1 200 OK")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
51
48
|
[207, Protocol::Caldav::Constants::DAV_HEADERS, [xml]]
|
|
52
49
|
end
|
|
53
50
|
end
|
|
@@ -43,45 +43,46 @@ module Async
|
|
|
43
43
|
items = multi.select { |_, data| data }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
xml = Protocol::Caldav::Multistatus.new.to_xml do |x|
|
|
47
|
+
items.each do |item_path, data|
|
|
48
|
+
next unless data
|
|
49
|
+
|
|
50
|
+
# Apply filter
|
|
51
|
+
if filter
|
|
52
|
+
if resource_type == :addressbook
|
|
53
|
+
card = Protocol::Caldav::Vcard::Parser.parse(data[:body])
|
|
54
|
+
next unless card && Protocol::Caldav::Filter::Match.addressbook?(filter, card)
|
|
55
|
+
else
|
|
56
|
+
component = Protocol::Caldav::Ical::Parser.parse(data[:body])
|
|
57
|
+
next unless component && Protocol::Caldav::Filter::Match.calendar?(filter, component)
|
|
58
|
+
end
|
|
57
59
|
end
|
|
58
|
-
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
item_body = data[:body]
|
|
62
|
+
|
|
63
|
+
# Apply expand if requested (calendar items only)
|
|
64
|
+
if expand_range && resource_type != :addressbook
|
|
65
|
+
component = Protocol::Caldav::Ical::Parser.parse(item_body)
|
|
66
|
+
if component
|
|
67
|
+
item_body = Protocol::Caldav::Ical::Expand.expand(
|
|
68
|
+
component,
|
|
69
|
+
range_start: expand_range[:start],
|
|
70
|
+
range_end: expand_range[:end]
|
|
71
|
+
)
|
|
72
|
+
end
|
|
71
73
|
end
|
|
72
|
-
end
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
item_p = Protocol::Caldav::Path.new(item_path, storage_class: storage)
|
|
76
|
+
item = Protocol::Caldav::Item.new(
|
|
77
|
+
path: item_p,
|
|
78
|
+
body: item_body,
|
|
79
|
+
content_type: data[:content_type],
|
|
80
|
+
etag: data[:etag]
|
|
81
|
+
)
|
|
82
|
+
item.build_report(x, data_tag: data_tag)
|
|
83
|
+
end
|
|
82
84
|
end
|
|
83
85
|
|
|
84
|
-
xml = Protocol::Caldav::Multistatus.new(responses).to_xml
|
|
85
86
|
[207, Protocol::Caldav::Constants::DAV_HEADERS, [xml]]
|
|
86
87
|
end
|
|
87
88
|
|
|
@@ -97,67 +98,48 @@ module Async
|
|
|
97
98
|
# Incremental sync
|
|
98
99
|
result = storage.sync_changes(col_path, old_token)
|
|
99
100
|
unless result
|
|
100
|
-
|
|
101
|
-
error_xml
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
XML
|
|
107
|
-
return [403, { 'content-type' => 'application/xml' }, [error_xml]]
|
|
101
|
+
error_xml = Builder::XmlMarkup.new
|
|
102
|
+
error_xml.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
|
103
|
+
error_xml.tag!("d:error", "xmlns:d" => "DAV:") do
|
|
104
|
+
error_xml.tag!("d:valid-sync-token")
|
|
105
|
+
end
|
|
106
|
+
return [403, { 'content-type' => 'application/xml' }, [error_xml.target!]]
|
|
108
107
|
end
|
|
109
108
|
|
|
110
109
|
new_token, changes = result
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<d:prop>
|
|
126
|
-
<d:getetag>#{Protocol::Caldav::Xml.escape(etag)}</d:getetag>
|
|
127
|
-
</d:prop>
|
|
128
|
-
<d:status>HTTP/1.1 200 OK</d:status>
|
|
129
|
-
</d:propstat>
|
|
130
|
-
</d:response>
|
|
131
|
-
XML
|
|
110
|
+
xml = Protocol::Caldav::XmlBuilder.multistatus do |x|
|
|
111
|
+
changes.each do |item_path, status|
|
|
112
|
+
if status == :deleted
|
|
113
|
+
Protocol::Caldav::XmlBuilder.response(x, href: item_path) do
|
|
114
|
+
x.tag!("d:status", "HTTP/1.1 404 Not Found")
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
etag = storage.etag(item_path)
|
|
118
|
+
Protocol::Caldav::XmlBuilder.response(x, href: item_path) do
|
|
119
|
+
Protocol::Caldav::XmlBuilder.propstat_ok(x) do
|
|
120
|
+
x.tag!("d:getetag", etag)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
132
124
|
end
|
|
125
|
+
x.tag!("d:sync-token", new_token)
|
|
133
126
|
end
|
|
134
127
|
else
|
|
135
128
|
# Initial sync — return all items
|
|
136
129
|
new_token = storage.snapshot_sync(col_path)
|
|
137
130
|
items = storage.list_items(col_path)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
</d:propstat>
|
|
148
|
-
</d:response>
|
|
149
|
-
XML
|
|
131
|
+
xml = Protocol::Caldav::XmlBuilder.multistatus do |x|
|
|
132
|
+
items.each do |item_path, data|
|
|
133
|
+
Protocol::Caldav::XmlBuilder.response(x, href: item_path) do
|
|
134
|
+
Protocol::Caldav::XmlBuilder.propstat_ok(x) do
|
|
135
|
+
x.tag!("d:getetag", data[:etag])
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
x.tag!("d:sync-token", new_token)
|
|
150
140
|
end
|
|
151
141
|
end
|
|
152
142
|
|
|
153
|
-
xml = <<~XML
|
|
154
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
155
|
-
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:cr="urn:ietf:params:xml:ns:carddav" xmlns:cs="http://calendarserver.org/ns/" xmlns:x="http://apple.com/ns/ical/">
|
|
156
|
-
#{responses.join}
|
|
157
|
-
<d:sync-token>#{Protocol::Caldav::Xml.escape(new_token)}</d:sync-token>
|
|
158
|
-
</d:multistatus>
|
|
159
|
-
XML
|
|
160
|
-
|
|
161
143
|
[207, Protocol::Caldav::Constants::DAV_HEADERS, [xml]]
|
|
162
144
|
end
|
|
163
145
|
|
data/lib/async/caldav/version.rb
CHANGED