protocol-caldav 1.0.2 → 1.1.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 +4 -4
- data/lib/protocol/caldav/collection.rb +99 -75
- data/lib/protocol/caldav/constants.rb +23 -27
- data/lib/protocol/caldav/content_line.rb +94 -91
- data/lib/protocol/caldav/ctag.rb +46 -50
- data/lib/protocol/caldav/etag.rb +24 -28
- data/lib/protocol/caldav/filter/addressbook.rb +41 -28
- data/lib/protocol/caldav/filter/calendar.rb +65 -44
- data/lib/protocol/caldav/filter/match.rb +632 -556
- data/lib/protocol/caldav/filter/parser.rb +273 -252
- data/lib/protocol/caldav/ical/component.rb +49 -46
- data/lib/protocol/caldav/ical/expand.rb +148 -134
- data/lib/protocol/caldav/ical/freebusy.rb +106 -74
- data/lib/protocol/caldav/ical/parser.rb +139 -139
- data/lib/protocol/caldav/ical/property.rb +22 -21
- data/lib/protocol/caldav/ical/rrule.rb +346 -235
- data/lib/protocol/caldav/item.rb +41 -43
- data/lib/protocol/caldav/multistatus.rb +43 -46
- data/lib/protocol/caldav/path.rb +82 -79
- data/lib/protocol/caldav/storage.rb +24 -28
- data/lib/protocol/caldav/vcard/card.rb +25 -27
- data/lib/protocol/caldav/vcard/parser.rb +56 -59
- data/lib/protocol/caldav/version.rb +1 -1
- data/lib/protocol/caldav/xml.rb +84 -80
- data/lib/protocol/caldav/xml_builder.rb +4 -2
- metadata +18 -4
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
|
|
6
3
|
require "protocol/caldav"
|
|
7
4
|
|
|
8
5
|
module Protocol
|
|
@@ -12,77 +9,77 @@ module Protocol
|
|
|
12
9
|
module_function
|
|
13
10
|
|
|
14
11
|
def parse(text)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
if text.nil? || text.strip.empty?
|
|
13
|
+
nil
|
|
14
|
+
else
|
|
15
|
+
text = text.sub(/\A\xEF\xBB\xBF/, '')
|
|
16
|
+
lines = ContentLine.unfold(text).split("\n").map(&:strip).reject(&:empty?)
|
|
17
|
+
props = []
|
|
18
|
+
inside = false
|
|
19
|
+
lines.each do |line|
|
|
20
|
+
parsed = ContentLine.parse_line(line)
|
|
21
|
+
unless parsed
|
|
22
|
+
next
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
name, params, value = parsed
|
|
26
|
+
|
|
27
|
+
if name.casecmp?('BEGIN') && value.strip.casecmp?('VCARD')
|
|
28
|
+
inside = true
|
|
29
|
+
elsif name.casecmp?('END') && value.strip.casecmp?('VCARD')
|
|
30
|
+
break
|
|
31
|
+
elsif inside
|
|
32
|
+
props << Ical::Property.new(name: name, params: params, value: value)
|
|
33
|
+
end
|
|
35
34
|
end
|
|
35
|
+
props.empty? ? nil : Card.new(properties: props)
|
|
36
36
|
end
|
|
37
|
-
|
|
38
|
-
props.empty? ? nil : Card.new(properties: props)
|
|
39
37
|
end
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
end
|
|
44
42
|
|
|
43
|
+
__END__
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
end
|
|
45
|
+
describe "Protocol::Caldav::Vcard::Parser" do
|
|
46
|
+
def parse(text)
|
|
47
|
+
Protocol::Caldav::Vcard::Parser.parse(text)
|
|
48
|
+
end
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
it "parses a flat BEGIN:VCARD / END:VCARD" do
|
|
51
|
+
card = parse("BEGIN:VCARD\r\nVERSION:3.0\r\nFN:John\r\nEND:VCARD")
|
|
52
|
+
card.should.not.be.nil
|
|
53
|
+
card.find_property("FN").value.should.equal "John"
|
|
54
|
+
end
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
it "handles VERSION:3.0 and VERSION:4.0 without distinction" do
|
|
57
|
+
card3 = parse("BEGIN:VCARD\r\nVERSION:3.0\r\nFN:A\r\nEND:VCARD")
|
|
58
|
+
card4 = parse("BEGIN:VCARD\r\nVERSION:4.0\r\nFN:A\r\nEND:VCARD")
|
|
59
|
+
card3.find_property("VERSION").value.should.equal "3.0"
|
|
60
|
+
card4.find_property("VERSION").value.should.equal "4.0"
|
|
61
|
+
end
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
it "handles structured values in N property as raw string" do
|
|
64
|
+
card = parse("BEGIN:VCARD\r\nN:Doe;John;;;Jr.\r\nEND:VCARD")
|
|
65
|
+
card.find_property("N").value.should.equal "Doe;John;;;Jr."
|
|
66
|
+
end
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
it "returns nil for empty input" do
|
|
69
|
+
parse("").should.be.nil
|
|
70
|
+
end
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
it "returns nil for nil input" do
|
|
73
|
+
parse(nil).should.be.nil
|
|
74
|
+
end
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
it "tolerates LF-only line endings" do
|
|
77
|
+
card = parse("BEGIN:VCARD\nFN:Test\nEND:VCARD")
|
|
78
|
+
card.find_property("FN").value.should.equal "Test"
|
|
79
|
+
end
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
end
|
|
81
|
+
it "tolerates BOM" do
|
|
82
|
+
card = parse("\xEF\xBB\xBFBEGIN:VCARD\r\nFN:Test\r\nEND:VCARD")
|
|
83
|
+
card.should.not.be.nil
|
|
87
84
|
end
|
|
88
85
|
end
|
data/lib/protocol/caldav/xml.rb
CHANGED
|
@@ -1,122 +1,126 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
|
|
6
3
|
module Protocol
|
|
7
4
|
module Caldav
|
|
8
5
|
module Xml
|
|
9
6
|
module_function
|
|
10
7
|
|
|
11
8
|
def escape(str)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
if str
|
|
10
|
+
str.to_s
|
|
11
|
+
.gsub('&', '&')
|
|
12
|
+
.gsub('<', '<')
|
|
13
|
+
.gsub('>', '>')
|
|
14
|
+
.gsub('"', '"')
|
|
15
|
+
else
|
|
16
|
+
''
|
|
17
|
+
end
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def extract_value(xml, tag)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
if xml.nil? || xml.empty?
|
|
22
|
+
nil
|
|
23
|
+
else
|
|
24
|
+
match = xml.match(/<[^>]*#{Regexp.escape(tag)}[^>]*>([^<]*)</)
|
|
25
|
+
if match
|
|
26
|
+
value = match[1].strip
|
|
27
|
+
value.empty? ? nil : value
|
|
28
|
+
else
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def extract_attr(xml, tag, attr)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if xml.nil? || xml.empty?
|
|
36
|
+
nil
|
|
37
|
+
else
|
|
38
|
+
match = xml.match(/<[^>]*#{Regexp.escape(tag)}[^>]*#{Regexp.escape(attr)}="([^"]*)"/)
|
|
39
|
+
match ? match[1] : nil
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
end
|
|
39
44
|
end
|
|
40
45
|
|
|
46
|
+
__END__
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
describe "
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
+
describe "Protocol::Caldav::Xml" do
|
|
49
|
+
describe ".escape" do
|
|
50
|
+
it "escapes ampersand" do
|
|
51
|
+
Protocol::Caldav::Xml.escape("a&b").should.equal "a&b"
|
|
52
|
+
end
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
it "escapes less-than" do
|
|
55
|
+
Protocol::Caldav::Xml.escape("a<b").should.equal "a<b"
|
|
56
|
+
end
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
it "escapes greater-than" do
|
|
59
|
+
Protocol::Caldav::Xml.escape("a>b").should.equal "a>b"
|
|
60
|
+
end
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
it "escapes double-quote" do
|
|
63
|
+
Protocol::Caldav::Xml.escape('a"b').should.equal "a"b"
|
|
64
|
+
end
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
it "escapes all five entities together" do
|
|
67
|
+
Protocol::Caldav::Xml.escape('&<>"').should.equal '&<>"'
|
|
68
|
+
end
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
it "returns empty string for nil" do
|
|
71
|
+
Protocol::Caldav::Xml.escape(nil).should.equal ''
|
|
72
|
+
end
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
it "returns empty string for empty string" do
|
|
75
|
+
Protocol::Caldav::Xml.escape('').should.equal ''
|
|
76
|
+
end
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
78
|
+
it "passes through plain text unchanged" do
|
|
79
|
+
Protocol::Caldav::Xml.escape("hello world").should.equal "hello world"
|
|
76
80
|
end
|
|
81
|
+
end
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
describe ".extract_value" do
|
|
84
|
+
it "extracts text content from a tag" do
|
|
85
|
+
Protocol::Caldav::Xml.extract_value('<d:displayname>Work</d:displayname>', 'displayname').should.equal 'Work'
|
|
86
|
+
end
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
it "returns nil for empty content" do
|
|
89
|
+
Protocol::Caldav::Xml.extract_value('<d:displayname></d:displayname>', 'displayname').should.be.nil
|
|
90
|
+
end
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
it "returns nil when tag not found" do
|
|
93
|
+
Protocol::Caldav::Xml.extract_value('<d:other>x</d:other>', 'displayname').should.be.nil
|
|
94
|
+
end
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
it "returns nil for nil input" do
|
|
97
|
+
Protocol::Caldav::Xml.extract_value(nil, 'displayname').should.be.nil
|
|
98
|
+
end
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
it "returns nil for empty input" do
|
|
101
|
+
Protocol::Caldav::Xml.extract_value('', 'displayname').should.be.nil
|
|
102
|
+
end
|
|
98
103
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
104
|
+
it "strips whitespace from value" do
|
|
105
|
+
Protocol::Caldav::Xml.extract_value('<d:displayname> Work </d:displayname>', 'displayname').should.equal 'Work'
|
|
102
106
|
end
|
|
107
|
+
end
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
describe ".extract_attr" do
|
|
110
|
+
it "extracts attribute value from a tag" do
|
|
111
|
+
Protocol::Caldav::Xml.extract_attr('<c:comp-filter name="VCALENDAR">', 'comp-filter', 'name').should.equal 'VCALENDAR'
|
|
112
|
+
end
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
it "returns nil when tag not found" do
|
|
115
|
+
Protocol::Caldav::Xml.extract_attr('<c:other name="x">', 'comp-filter', 'name').should.be.nil
|
|
116
|
+
end
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
it "returns nil for nil input" do
|
|
119
|
+
Protocol::Caldav::Xml.extract_attr(nil, 'comp-filter', 'name').should.be.nil
|
|
120
|
+
end
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
end
|
|
122
|
+
it "returns nil for empty input" do
|
|
123
|
+
Protocol::Caldav::Xml.extract_attr('', 'comp-filter', 'name').should.be.nil
|
|
120
124
|
end
|
|
121
125
|
end
|
|
122
126
|
end
|
|
@@ -10,7 +10,7 @@ module Protocol
|
|
|
10
10
|
"xmlns:c" => "urn:ietf:params:xml:ns:caldav",
|
|
11
11
|
"xmlns:cr" => "urn:ietf:params:xml:ns:carddav",
|
|
12
12
|
"xmlns:cs" => "http://calendarserver.org/ns/",
|
|
13
|
-
"xmlns:x" => "http://apple.com/ns/ical/"
|
|
13
|
+
"xmlns:x" => "http://apple.com/ns/ical/",
|
|
14
14
|
}.freeze
|
|
15
15
|
|
|
16
16
|
module_function
|
|
@@ -27,7 +27,9 @@ module Protocol
|
|
|
27
27
|
def response(xml, href:)
|
|
28
28
|
xml.tag!("d:response") do
|
|
29
29
|
xml.tag!("d:href", href)
|
|
30
|
-
|
|
30
|
+
if block_given?
|
|
31
|
+
yield xml
|
|
32
|
+
end
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-caldav
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan K
|
|
@@ -37,20 +37,34 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rubocop
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.88'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.88'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: scampi
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
43
57
|
requirements:
|
|
44
58
|
- - "~>"
|
|
45
59
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
47
|
-
type: :
|
|
60
|
+
version: '1.0'
|
|
61
|
+
type: :development
|
|
48
62
|
prerelease: false
|
|
49
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
64
|
requirements:
|
|
51
65
|
- - "~>"
|
|
52
66
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
67
|
+
version: '1.0'
|
|
54
68
|
description: |
|
|
55
69
|
Pure protocol code for CalDAV (RFC 4791) and CardDAV (RFC 6352).
|
|
56
70
|
No rack, no async, no I/O. Pair with async-caldav for a server.
|