vobject 0.1.0 → 1.0.2
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/.github/workflows/macos.yml +38 -0
- data/.github/workflows/ubuntu.yml +56 -0
- data/.github/workflows/windows.yml +40 -0
- data/.hound.yml +3 -0
- data/.rubocop.tb.yml +650 -0
- data/.rubocop.yml +1077 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +21 -17
- data/README.adoc +151 -0
- data/Rakefile +1 -1
- data/lib/c.rb +173 -0
- data/lib/error.rb +19 -0
- data/lib/vcalendar.rb +77 -0
- data/lib/vcard.rb +67 -0
- data/lib/vobject.rb +13 -170
- data/lib/vobject/component.rb +87 -36
- data/lib/vobject/parameter.rb +116 -0
- data/lib/vobject/parametervalue.rb +26 -0
- data/lib/vobject/property.rb +134 -55
- data/lib/vobject/propertyvalue.rb +46 -0
- data/lib/vobject/vcalendar/component.rb +106 -0
- data/lib/vobject/vcalendar/grammar.rb +595 -0
- data/lib/vobject/vcalendar/paramcheck.rb +259 -0
- data/lib/vobject/vcalendar/propertyparent.rb +98 -0
- data/lib/vobject/vcalendar/propertyvalue.rb +606 -0
- data/lib/vobject/vcalendar/typegrammars.rb +605 -0
- data/lib/vobject/vcard/v3_0/component.rb +40 -0
- data/lib/vobject/vcard/v3_0/grammar.rb +175 -0
- data/lib/vobject/vcard/v3_0/paramcheck.rb +110 -0
- data/lib/vobject/vcard/v3_0/parameter.rb +17 -0
- data/lib/vobject/vcard/v3_0/property.rb +18 -0
- data/lib/vobject/vcard/v3_0/propertyvalue.rb +401 -0
- data/lib/vobject/vcard/v3_0/typegrammars.rb +425 -0
- data/lib/vobject/vcard/v4_0/component.rb +40 -0
- data/lib/vobject/vcard/v4_0/grammar.rb +224 -0
- data/lib/vobject/vcard/v4_0/paramcheck.rb +269 -0
- data/lib/vobject/vcard/v4_0/parameter.rb +18 -0
- data/lib/vobject/vcard/v4_0/property.rb +63 -0
- data/lib/vobject/vcard/v4_0/propertyvalue.rb +404 -0
- data/lib/vobject/vcard/v4_0/typegrammars.rb +539 -0
- data/lib/vobject/version.rb +1 -1
- data/vobject.gemspec +19 -16
- metadata +81 -26
- data/.travis.yml +0 -5
- data/README.md +0 -94
@@ -0,0 +1,605 @@
|
|
1
|
+
require "rsec"
|
2
|
+
require "set"
|
3
|
+
require "uri"
|
4
|
+
require "date"
|
5
|
+
require "tzinfo"
|
6
|
+
include Rsec::Helpers
|
7
|
+
require_relative "../../c"
|
8
|
+
require_relative "../../error"
|
9
|
+
require_relative "./propertyparent"
|
10
|
+
require "vobject"
|
11
|
+
require_relative "./propertyvalue"
|
12
|
+
|
13
|
+
module Vobject::Vcalendar
|
14
|
+
class Typegrammars
|
15
|
+
class << self
|
16
|
+
# property value types, each defining their own parser
|
17
|
+
def recur
|
18
|
+
freq = /SECONDLY/i.r | /MINUTELY/i.r | /HOURLY/i.r | /DAILY/i.r |
|
19
|
+
/WEEKLY/i.r | /MONTHLY/i.r | /YEARLY/i.r
|
20
|
+
enddate = C::DATE_TIME | C::DATE
|
21
|
+
seconds = /[0-9]{1,2}/.r
|
22
|
+
byseclist = seq(seconds << ",".r, lazy { byseclist }) do |s, l|
|
23
|
+
[s, l].flatten
|
24
|
+
end | seconds.map { |s| [s] }
|
25
|
+
minutes = /[0-9]{1,2}/.r
|
26
|
+
byminlist = seq(minutes << ",".r, lazy { byminlist }) do |m, l|
|
27
|
+
[m, l].flatten
|
28
|
+
end | minutes.map { |m| [m] }
|
29
|
+
hours = /[0-9]{1,2}/.r
|
30
|
+
byhrlist = seq(hours << ",".r, lazy { byhrlist }) do |h, l|
|
31
|
+
[h, l].flatten
|
32
|
+
end | hours.map { |h| [h] }
|
33
|
+
ordwk = /[0-9]{1,2}/.r
|
34
|
+
weekday = /SU/i.r | /MO/i.r | /TU/i.r | /WE/i.r | /TH/i.r | /FR/i.r | /SA/i.r
|
35
|
+
weekdaynum1 = seq(C::SIGN._?, ordwk) do |s, o|
|
36
|
+
h = { ordwk: o }
|
37
|
+
h[:sign] = s[0] unless s.empty?
|
38
|
+
h
|
39
|
+
end
|
40
|
+
weekdaynum = seq(weekdaynum1._?, weekday) do |a, b|
|
41
|
+
h = { weekday: b }
|
42
|
+
h = h.merge a[0] unless a.empty?
|
43
|
+
h
|
44
|
+
end
|
45
|
+
bywdaylist = seq(weekdaynum << ",".r, lazy { bywdaylist }) do |w, l|
|
46
|
+
[w, l].flatten
|
47
|
+
end | weekdaynum.map { |w| [w] }
|
48
|
+
ordmoday = /[0-9]{1,2}/.r
|
49
|
+
monthdaynum = seq(C::SIGN._?, ordmoday) do |s, o|
|
50
|
+
h = { ordmoday: o }
|
51
|
+
h[:sign] = s[0] unless s.empty?
|
52
|
+
h
|
53
|
+
end
|
54
|
+
bymodaylist = seq(monthdaynum << ",".r, lazy { bymodaylist }) do |m, l|
|
55
|
+
[m, l].flatten
|
56
|
+
end | monthdaynum.map { |m| [m] }
|
57
|
+
ordyrday = /[0-9]{1,3}/.r
|
58
|
+
yeardaynum = seq(C::SIGN._?, ordyrday) do |s, o|
|
59
|
+
h = { ordyrday: o }
|
60
|
+
h[:sign] = s[0] unless s.empty?
|
61
|
+
h
|
62
|
+
end
|
63
|
+
byyrdaylist = seq(yeardaynum << ",".r, lazy { byyrdaylist }) do |y, l|
|
64
|
+
[y, l].flatten
|
65
|
+
end | yeardaynum.map { |y| [y] }
|
66
|
+
weeknum = seq(C::SIGN._?, ordwk) do |s, o|
|
67
|
+
h = { ordwk: o }
|
68
|
+
h[:sign] = s[0] unless s.empty?
|
69
|
+
h
|
70
|
+
end
|
71
|
+
bywknolist = seq(weeknum << ",".r, lazy { bywknolist }) do |w, l|
|
72
|
+
[w, l].flatten
|
73
|
+
end | weeknum.map { |w| [w] }
|
74
|
+
# monthnum = /[0-9]{1,2}/.r
|
75
|
+
# RFC 7529 add leap month indicator
|
76
|
+
monthnum = /[0-9]{1,2}L?/i.r
|
77
|
+
bymolist = seq(monthnum << ",".r, lazy { bymolist }) do |m, l|
|
78
|
+
[m, l].flatten
|
79
|
+
end | monthnum.map { |m| [m] }
|
80
|
+
setposday = yeardaynum
|
81
|
+
bysplist = seq(setposday << ",".r, lazy { bysplist }) do |s, l|
|
82
|
+
[s, l].flatten
|
83
|
+
end | setposday.map { |s| [s] }
|
84
|
+
# http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
|
85
|
+
rscale = C::XNAME_VCAL | /buddhist/i.r | /chinese/i.r | /coptic/i.r | /dangi/i.r |
|
86
|
+
/ethioaa/i.r | /ethiopic-amete-alem/i.r | /ethiopic/i.r |
|
87
|
+
/gregory/i.r | /hebrew/i.r | /indian/i.r | /islamic/i.r |
|
88
|
+
/islamic-umalqura/i.r | /islamic-tbla/i.r | /islamic-civil/i.r |
|
89
|
+
/islamic-rgsa/i.r | /iso8601/i.r | /japanese/i.r | /persian/i.r |
|
90
|
+
/roc/i.r | /islamicc/i.r | /gregorian/i.r
|
91
|
+
skip = /OMIT/i.r | /BACKWARD/i.r | /FORWARD/i.r
|
92
|
+
recur_rule_part = seq(/FREQ/i.r << "=".r, freq) { |_k, v| { freq: v } } |
|
93
|
+
seq(/UNTIL/i.r << "=".r, enddate) { |_k, v| { until: v } } |
|
94
|
+
seq(/COUNT/i.r << "=".r, /[0-9]+/i.r) { |_k, v| { count: v } } |
|
95
|
+
seq(/INTERVAL/i.r << "=".r, /[0-9]+/i.r) { |_k, v| { interval: v } } |
|
96
|
+
seq(/BYSECOND/i.r << "=".r, byseclist) { |_k, v| { bysecond: v } } |
|
97
|
+
seq(/BYMINUTE/i.r << "=".r, byminlist) { |_k, v| { byminute: v } } |
|
98
|
+
seq(/BYHOUR/i.r << "=".r, byhrlist) { |_k, v| { byhour: v } } |
|
99
|
+
seq(/BYDAY/i.r << "=".r, bywdaylist) { |_k, v| { byday: v } } |
|
100
|
+
seq(/BYMONTHDAY/i.r << "=".r, bymodaylist) { |_k, v| { bymonthday: v } } |
|
101
|
+
seq(/BYYEARDAY/i.r << "=".r, byyrdaylist) { |_k, v| { byyearday: v } } |
|
102
|
+
seq(/BYWEEKNO/i.r << "=".r, bywknolist) { |_k, v| { byweekno: v } } |
|
103
|
+
seq(/BYMONTH/i.r << "=".r, bymolist) { |_k, v| { bymonth: v } } |
|
104
|
+
seq(/BYSETPOS/i.r << "=".r, bysplist) { |_k, v| { bysetpos: v } } |
|
105
|
+
seq(/WKST/i.r << "=".r, weekday) { |_k, v| { wkst: v } } |
|
106
|
+
# RFC 7529
|
107
|
+
seq(/RSCALE/i.r << "=".r, rscale) { |_k, v| { rscale: v } } |
|
108
|
+
seq(/SKIP/i.r << "=".r, skip) { |_k, v| { skip: v } }
|
109
|
+
recur1 = seq(recur_rule_part, ";", lazy { recur1 }) { |h, _, r| h.merge r } |
|
110
|
+
recur_rule_part
|
111
|
+
recur = recur1.map { |r| PropertyValue::Recur.new r }
|
112
|
+
recur.eof
|
113
|
+
end
|
114
|
+
|
115
|
+
def integer
|
116
|
+
integer = prim(:int32).map { |i| PropertyValue::Integer.new i }
|
117
|
+
integer.eof
|
118
|
+
end
|
119
|
+
|
120
|
+
def percent_complete
|
121
|
+
integer = prim(:int32).map do |a|
|
122
|
+
if a >= 0 && a <= 100
|
123
|
+
PropertyValue::PercentComplete.new a
|
124
|
+
else
|
125
|
+
{ error: "Percentage outside of range 0..100" }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
integer.eof
|
129
|
+
end
|
130
|
+
|
131
|
+
def priority
|
132
|
+
integer = prim(:int32).map do |a|
|
133
|
+
if a >= 0 && a <= 9
|
134
|
+
PropertyValue::Priority.new a
|
135
|
+
else
|
136
|
+
{ error: "Percentage outside of range 0..100" }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
integer.eof
|
140
|
+
end
|
141
|
+
|
142
|
+
def float_t
|
143
|
+
float_t = prim(:double).map { |f| PropertyValue::Float.new f }
|
144
|
+
float_t.eof
|
145
|
+
end
|
146
|
+
|
147
|
+
def time_t
|
148
|
+
time_t = C::TIME.map { |t| PropertyValue::Time.new t }
|
149
|
+
time_t.eof
|
150
|
+
end
|
151
|
+
|
152
|
+
def geovalue
|
153
|
+
float = prim(:double)
|
154
|
+
geovalue = seq(float << ";".r, float) do |a, b|
|
155
|
+
if a <= 180.0 && a >= -180.0 && b <= 180 && b > -180
|
156
|
+
PropertyValue::Geovalue.new(lat: a, long: b)
|
157
|
+
else
|
158
|
+
{ error: "Latitude/Longitude outside of range -180..180" }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
geovalue.eof
|
162
|
+
end
|
163
|
+
|
164
|
+
def calscalevalue
|
165
|
+
calscalevalue = /GREGORIAN/i.r.map { PropertyValue::Calscale.new "GREGORIAN" }
|
166
|
+
calscalevalue.eof
|
167
|
+
end
|
168
|
+
|
169
|
+
def iana_token
|
170
|
+
iana_token = C::IANATOKEN.map { |x| PropertyValue::Ianatoken.new x }
|
171
|
+
iana_token.eof
|
172
|
+
end
|
173
|
+
|
174
|
+
def versionvalue
|
175
|
+
versionvalue = seq(prim(:double) << ";".r,
|
176
|
+
prim(:double)) do |x, y|
|
177
|
+
PropertyValue::Version.new [x, y]
|
178
|
+
end | "2.0".r.map do
|
179
|
+
PropertyValue::Version.new ["2.0"]
|
180
|
+
end | prim(:double).map do |v|
|
181
|
+
PropertyValue::Version.new v
|
182
|
+
end
|
183
|
+
versionvalue.eof
|
184
|
+
end
|
185
|
+
|
186
|
+
def binary
|
187
|
+
binary = seq(/[a-zA-Z0-9+\/]*/.r, /={0,2}/.r) do |b, q|
|
188
|
+
if (b.length + q.length) % 4 == 0
|
189
|
+
PropertyValue::Binary.new(b + q)
|
190
|
+
else
|
191
|
+
{ error: "Malformed binary coding" }
|
192
|
+
end
|
193
|
+
end
|
194
|
+
binary.eof
|
195
|
+
end
|
196
|
+
|
197
|
+
def uri
|
198
|
+
uri = /\S+/.r.map do |s|
|
199
|
+
if s =~ URI::DEFAULT_PARSER.make_regexp
|
200
|
+
PropertyValue::Uri.new(s)
|
201
|
+
else
|
202
|
+
{ error: "Invalid URI" }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
uri.eof
|
206
|
+
end
|
207
|
+
|
208
|
+
def text_t
|
209
|
+
text_t = C::TEXT.map { |t| PropertyValue::Text.new(unescape(t)) }
|
210
|
+
text_t.eof
|
211
|
+
end
|
212
|
+
|
213
|
+
def textlist
|
214
|
+
textlist1 =
|
215
|
+
seq(C::TEXT << ",".r, lazy { textlist1 }) { |a, b| [unescape(a), b].flatten } |
|
216
|
+
C::TEXT.map { |t| [unescape(t)] }
|
217
|
+
textlist = textlist1.map { |m| PropertyValue::Textlist.new m }
|
218
|
+
textlist.eof
|
219
|
+
end
|
220
|
+
|
221
|
+
def request_statusvalue
|
222
|
+
@req_status = Set.new %w{2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 4.0 5.0 5.1 5.2 5.3}
|
223
|
+
extdata = seq(";".r, C::TEXT) { |_, t| t }
|
224
|
+
request_statusvalue = seq(/[0-9](\.[0-9]){1,2}/.r << ";".r, C::TEXT, extdata._?) do |n, t1, t2|
|
225
|
+
return { error: "Invalid request status #{n}" } unless @req_status.include?(n) # RFC 5546
|
226
|
+
hash = { statcode: n, statdesc: t1 }
|
227
|
+
hash[:extdata] = t2[0] unless t2.empty?
|
228
|
+
Vobject::Vcalendar::PropertyValue::Requeststatusvalue.new hash
|
229
|
+
end
|
230
|
+
request_statusvalue.eof
|
231
|
+
end
|
232
|
+
|
233
|
+
def classvalue
|
234
|
+
classvalue = (/PUBLIC/i.r | /PRIVATE/i.r | /CONFIDENTIAL/i.r | C::XNAME_VCAL | C::IANATOKEN).map do |m|
|
235
|
+
PropertyValue::ClassValue.new m
|
236
|
+
end
|
237
|
+
classvalue.eof
|
238
|
+
end
|
239
|
+
|
240
|
+
def eventstatus
|
241
|
+
eventstatus = (/TENTATIVE/i.r | /CONFIRMED/i.r | /CANCELLED/i.r).map do |m|
|
242
|
+
PropertyValue::EventStatus.new m
|
243
|
+
end
|
244
|
+
eventstatus.eof
|
245
|
+
end
|
246
|
+
|
247
|
+
def todostatus
|
248
|
+
todostatus = (/NEEDS-ACTION/i.r | /COMPLETED/i.r | /IN-PROCESS/i.r | /CANCELLED/i.r).map do |m|
|
249
|
+
PropertyValue::Todostatus.new m
|
250
|
+
end
|
251
|
+
todostatus.eof
|
252
|
+
end
|
253
|
+
|
254
|
+
def journalstatus
|
255
|
+
journalstatus = (/DRAFT/i.r | /FINAL/i.r | /CANCELLED/i.r).map do |m|
|
256
|
+
PropertyValue::Journalstatus.new m
|
257
|
+
end
|
258
|
+
journalstatus.eof
|
259
|
+
end
|
260
|
+
|
261
|
+
def date_t
|
262
|
+
date_t = C::DATE
|
263
|
+
date_t.eof
|
264
|
+
end
|
265
|
+
|
266
|
+
def datelist
|
267
|
+
datelist1 = seq(C::DATE << ",".r, lazy { datelist1 }) do |d, l|
|
268
|
+
[d, l].flatten
|
269
|
+
end | C::DATE.map { |d| [d] }
|
270
|
+
datelist = datelist1.map { |m| PropertyValue::Datelist.new m }
|
271
|
+
datelist.eof
|
272
|
+
end
|
273
|
+
|
274
|
+
def date_time_t
|
275
|
+
C::DATE_TIME.eof
|
276
|
+
end
|
277
|
+
|
278
|
+
def date_timelist
|
279
|
+
date_timelist1 = seq(C::DATE_TIME << ",".r,
|
280
|
+
lazy { date_timelist1 }) do |d, l|
|
281
|
+
[d, l].flatten
|
282
|
+
end | C::DATE_TIME.map { |d| [d] }
|
283
|
+
date_timelist = date_timelist1.map do |m|
|
284
|
+
PropertyValue::Datetimelist.new m
|
285
|
+
end
|
286
|
+
date_timelist.eof
|
287
|
+
end
|
288
|
+
|
289
|
+
def date_time_utc_t
|
290
|
+
date_time_utc_t = C::DATE_TIME_UTC
|
291
|
+
date_time_utc_t.eof
|
292
|
+
end
|
293
|
+
|
294
|
+
def date_time_utclist
|
295
|
+
date_time_utclist1 = seq(C::DATE_TIME_UTC << ",".r, lazy { date_time_utclist1 }) do |d, l|
|
296
|
+
[d, l].flatten
|
297
|
+
end | C::DATE_TIME_UTC.map { |d| [d] }
|
298
|
+
date_time_utclist = date_time_utclist1.map do |m|
|
299
|
+
PropertyValue::Datetimeutclist.new m
|
300
|
+
end
|
301
|
+
date_time_utclist.eof
|
302
|
+
end
|
303
|
+
|
304
|
+
def duration_t
|
305
|
+
duration = C::DURATION.map { |d| PropertyValue::Duration.new d }
|
306
|
+
duration.eof
|
307
|
+
end
|
308
|
+
|
309
|
+
def periodlist
|
310
|
+
period_explicit = seq(C::DATE_TIME << "/".r, C::DATE_TIME) do |s, e|
|
311
|
+
{ start: s, end: e }
|
312
|
+
end
|
313
|
+
period_start = seq(C::DATE_TIME << "/".r, C::DURATION) do |s, d|
|
314
|
+
{ start: s, duration: PropertyValue::Duration.new(d) }
|
315
|
+
end
|
316
|
+
period = period_explicit | period_start
|
317
|
+
periodlist1 = seq(period << ",".r, lazy { periodlist1 }) do |p, l|
|
318
|
+
[p, l].flatten
|
319
|
+
end | period.map { |p| [p] }
|
320
|
+
periodlist = periodlist1.map { |m| PropertyValue::Periodlist.new m }
|
321
|
+
periodlist.eof
|
322
|
+
end
|
323
|
+
|
324
|
+
def transpvalue
|
325
|
+
transpvalue = (/OPAQUE/i.r | /TRANSPARENT/i.r).map do |m|
|
326
|
+
PropertyValue::TranspValue.new m
|
327
|
+
end
|
328
|
+
transpvalue.eof
|
329
|
+
end
|
330
|
+
|
331
|
+
def utc_offset
|
332
|
+
utc_offset = seq(C::SIGN, /[0-9]{2}/.r, /[0-9]{2}/.r,
|
333
|
+
/[0-9]{2}/.r._?) do |sign, h, m, sec|
|
334
|
+
hash = { sign: sign, hr: h, min: m }
|
335
|
+
hash[:sec] = sec[0] unless sec.empty?
|
336
|
+
PropertyValue::Utcoffset.new hash
|
337
|
+
end
|
338
|
+
utc_offset.eof
|
339
|
+
end
|
340
|
+
|
341
|
+
def actionvalue
|
342
|
+
actionvalue = (/AUDIO/i.r | /DISPLAY/i.r | /EMAIL/i.r | C::IANATOKEN |
|
343
|
+
C::XNAME_VCAL).map { |m| PropertyValue::ActionValue.new m }
|
344
|
+
actionvalue.eof
|
345
|
+
end
|
346
|
+
|
347
|
+
def boolean
|
348
|
+
boolean = C::BOOLEAN.map { |b| PropertyValue::Boolean.new b }
|
349
|
+
boolean.eof
|
350
|
+
end
|
351
|
+
|
352
|
+
# RFC 5546
|
353
|
+
def methodvalue
|
354
|
+
methodvalue = (/PUBLISH/i.r | /REQUEST/i.r | /REPLY/i.r | /ADD/i.r |
|
355
|
+
/CANCEL/i.r | /REFRESH/i.r | /COUNTER/i.r |
|
356
|
+
/DECLINECOUNTER/i.r).map { |m| PropertyValue::MethodValue.new m }
|
357
|
+
methodvalue.eof
|
358
|
+
end
|
359
|
+
|
360
|
+
# RFC 7953
|
361
|
+
def busytype
|
362
|
+
busytype = (/BUSY-UNAVAILABLE/i.r | /BUSY-TENTATIVE/i.r | /BUSY/i.r |
|
363
|
+
C::IANATOKEN |
|
364
|
+
C::XNAME_VCAL).map { |m| PropertyValue::BusyType.new m }
|
365
|
+
busytype.eof
|
366
|
+
end
|
367
|
+
|
368
|
+
# https://www.w3.org/TR/2011/REC-css3-color-20110607/#svg-color
|
369
|
+
def color
|
370
|
+
color = C::COLOR.map { |m| PropertyValue::Color.new m }
|
371
|
+
color.eof
|
372
|
+
end
|
373
|
+
|
374
|
+
# text escapes: \\ \; \, \N \n
|
375
|
+
def unescape(x)
|
376
|
+
# temporarily escape \\ as \007f, which is disallowed in any text
|
377
|
+
x.gsub(/\\\\/, "\u007f").gsub(/\\;/, ";").gsub(/\\,/, ",").
|
378
|
+
gsub(/\\[Nn]/, "\n").tr("\u007f", "\\")
|
379
|
+
end
|
380
|
+
|
381
|
+
def registered_propname
|
382
|
+
registered_propname = C::NAME_VCAL
|
383
|
+
registered_propname.eof
|
384
|
+
end
|
385
|
+
|
386
|
+
def registered_propname?(x)
|
387
|
+
p = registered_propname.parse(x)
|
388
|
+
not(Rsec::INVALID[p])
|
389
|
+
end
|
390
|
+
|
391
|
+
# Enforce type restrictions on values of particular properties.
|
392
|
+
# If successful, return typed interpretation of string
|
393
|
+
def typematch(strict, key, params, component, value, ctx)
|
394
|
+
errors = []
|
395
|
+
errors << property_parent(strict, key, component, value, ctx)
|
396
|
+
ctx1 = Rsec::ParseContext.new value, "source"
|
397
|
+
case key
|
398
|
+
when :CALSCALE
|
399
|
+
ret = calscalevalue._parse ctx1
|
400
|
+
when :METHOD
|
401
|
+
ret = methodvalue._parse ctx1
|
402
|
+
when :VERSION
|
403
|
+
ret = versionvalue._parse ctx1
|
404
|
+
when :ATTACH
|
405
|
+
ret = if params[:VALUE] == "BINARY"
|
406
|
+
binary._parse ctx1
|
407
|
+
else
|
408
|
+
uri._parse ctx1
|
409
|
+
end
|
410
|
+
when :IMAGE
|
411
|
+
parse_err(strict, errors, "No VALUE parameter specified for property #{key}", ctx1) if params.empty?
|
412
|
+
parse_err(strict, errors, "No VALUE parameter specified for property #{key}", ctx1) unless params[:VALUE]
|
413
|
+
if params[:VALUE] == "BINARY"
|
414
|
+
parse_err(strict, errors, "No ENCODING parameter specified for property #{key}", ctx1) unless params[:ENCODING]
|
415
|
+
parse_err(strict, errors, "Incorrect ENCODING parameter specified for property #{key}", ctx1) unless params[:ENCODING] == "BASE64"
|
416
|
+
ret = binary._parse ctx1
|
417
|
+
elsif params[:VALUE] == "URI"
|
418
|
+
ret = uri._parse ctx1
|
419
|
+
else
|
420
|
+
parse_err(strict, errors, "Incorrect VALUE parameter specified for property #{key}", ctx1)
|
421
|
+
end
|
422
|
+
when :CATEGORIES, :RESOURCES
|
423
|
+
ret = textlist._parse ctx1
|
424
|
+
when :CLASS
|
425
|
+
ret = classvalue._parse ctx1
|
426
|
+
when :COMMENT, :DESCRIPTION, :LOCATION, :SUMMARY, :TZID, :TZNAME,
|
427
|
+
:CONTACT, :RELATED_TO, :UID, :PRODID, :NAME
|
428
|
+
ret = text_t._parse ctx1
|
429
|
+
when :GEO
|
430
|
+
ret = geovalue._parse ctx1
|
431
|
+
when :PERCENT_COMPLETE
|
432
|
+
ret = percent_complete._parse ctx1
|
433
|
+
when :PRIORITY
|
434
|
+
ret = priority._parse ctx1
|
435
|
+
when :STATUS
|
436
|
+
ret = case component
|
437
|
+
when :EVENT
|
438
|
+
eventstatus._parse ctx1
|
439
|
+
when :TODO
|
440
|
+
todostatus._parse ctx1
|
441
|
+
when :JOURNAL
|
442
|
+
journalstatus._parse ctx1
|
443
|
+
else
|
444
|
+
text_t._parse ctx1
|
445
|
+
end
|
446
|
+
when :COMPLETED, :CREATED, :DTSTAMP, :LAST_MODIFIED
|
447
|
+
ret = date_time_utc_t._parse ctx1
|
448
|
+
when :DTEND, :DTSTART, :DUE, :RECURRENCE_ID
|
449
|
+
if params && params[:VALUE] == "DATE"
|
450
|
+
ret = date_t._parse ctx1
|
451
|
+
elsif component == :FREEBUSY
|
452
|
+
ret = date_time_utc_t._parse ctx1
|
453
|
+
elsif params && params[:TZID]
|
454
|
+
if [:STANDARD || :DAYLIGHT].include? component
|
455
|
+
parse_err(strict, errors, "Specified TZID within property #{key} in #{component}", ctx1)
|
456
|
+
end
|
457
|
+
begin
|
458
|
+
tz = TZInfo::Timezone.get(params[:TZID])
|
459
|
+
ret = date_time_t._parse ctx1
|
460
|
+
# note that we use the registered tz information to map to UTC, rather than look up the values witin the VTIMEZONE component
|
461
|
+
ret.value[:time] = tz.local_to_utc(ret.value[:time])
|
462
|
+
ret.value[:zone] = params[:TZID]
|
463
|
+
rescue
|
464
|
+
# undefined timezone: default to floating local
|
465
|
+
ret = date_time_t._parse ctx1
|
466
|
+
end
|
467
|
+
else
|
468
|
+
ret = date_time_t._parse ctx1
|
469
|
+
end
|
470
|
+
when :EXDATE
|
471
|
+
if params && params[:VALUE] == "DATE"
|
472
|
+
ret = datelist._parse ctx1
|
473
|
+
elsif params && params[:TZID]
|
474
|
+
if [:STANDARD || :DAYLIGHT].include? component
|
475
|
+
parse_err(strict, errors, "Specified TZID within property #{key} in #{component}", ctx1)
|
476
|
+
end
|
477
|
+
tz = TZInfo::Timezone.get(params[:TZID])
|
478
|
+
ret = date_timelist._parse ctx1
|
479
|
+
ret.value.each do |x|
|
480
|
+
x.value[:time] = tz.local_to_utc(x.value[:time])
|
481
|
+
x.value[:zone] = params[:TZID]
|
482
|
+
end
|
483
|
+
else
|
484
|
+
ret = date_timelist._parse ctx1
|
485
|
+
end
|
486
|
+
when :RDATE
|
487
|
+
if params && params[:VALUE] == "DATE"
|
488
|
+
ret = datelist._parse ctx1
|
489
|
+
elsif params && params[:VALUE] == "PERIOD"
|
490
|
+
ret = periodlist._parse ctx1
|
491
|
+
elsif params && params[:TZID]
|
492
|
+
if [:STANDARD || :DAYLIGHT].include? component
|
493
|
+
parse_err(strict, errors, "Specified TZID within property #{key} in #{component}", ctx1)
|
494
|
+
end
|
495
|
+
tz = TZInfo::Timezone.get(params[:TZID])
|
496
|
+
ret = date_timelist._parse ctx1
|
497
|
+
ret.value.each do |x|
|
498
|
+
x.value[:time] = tz.local_to_utc(x.value[:time])
|
499
|
+
x.value[:zone] = params[:TZID]
|
500
|
+
end
|
501
|
+
else
|
502
|
+
ret = date_timelist._parse ctx1
|
503
|
+
end
|
504
|
+
when :TRIGGER
|
505
|
+
if params && params[:VALUE] == "DATE-TIME" || /^\d{8}T/.match(value)
|
506
|
+
if params && params[:RELATED]
|
507
|
+
parse_err(strict, errors, "Specified RELATED within property #{key} as date-time", ctx1)
|
508
|
+
end
|
509
|
+
ret = date_time_utc_t._parse ctx1
|
510
|
+
else
|
511
|
+
ret = duration_t._parse ctx1
|
512
|
+
end
|
513
|
+
when :FREEBUSY
|
514
|
+
ret = periodlist._parse ctx1
|
515
|
+
when :TRANSP
|
516
|
+
ret = transpvalue._parse ctx1
|
517
|
+
when :TZOFFSETFROM, :TZOFFSETTO
|
518
|
+
ret = utc_offset._parse ctx1
|
519
|
+
when :TZURI, :URL, :SOURCE, :CONFERENCE
|
520
|
+
if key == :CONFERENCE
|
521
|
+
parse_err(strict, errors, "Missing URI VALUE parameter", ctx1) if params.empty?
|
522
|
+
parse_err(strict, errors, "Missing URI VALUE parameter", ctx1) if !params[:VALUE]
|
523
|
+
parse_err(strict, errors, "report_error Type mismatch of VALUE parameter #{params[:VALUE]} for property #{key}", ctx1) if params[:VALUE] != "URI"
|
524
|
+
end
|
525
|
+
ret = uri._parse ctx1
|
526
|
+
when :ATTENDEE, :ORGANIZER
|
527
|
+
ret = uri._parse ctx1
|
528
|
+
when :RRULE
|
529
|
+
ret = recur._parse ctx1
|
530
|
+
when :ACTION
|
531
|
+
ret = actionvalue._parse ctx1
|
532
|
+
when :REPEAT, :SEQUENCE
|
533
|
+
ret = integer._parse ctx1
|
534
|
+
when :REQUEST_STATUS
|
535
|
+
ret = request_statusvalue._parse ctx1
|
536
|
+
# RFC 7953
|
537
|
+
when :BUSYTYPE
|
538
|
+
ret = busytype._parse ctx1
|
539
|
+
# RFC 7986
|
540
|
+
when :REFRESH_INTERVAL
|
541
|
+
parse_err(strict, errors, "Missing VALUE parameter for property #{key}", ctx1) if params.empty?
|
542
|
+
parse_err(strict, errors, "Missing VALUE parameter for property #{key}", ctx1) if !params[:VALUE]
|
543
|
+
parse_err(strict, errors, "Type mismatch of VALUE parameter #{params[:VALUE]} for property #{key}", ctx1) if params[:VALUE] != "DURATION"
|
544
|
+
ret = duration_t._parse ctx1
|
545
|
+
# RFC 7986
|
546
|
+
when :COLOR
|
547
|
+
ret = color._parse ctx1
|
548
|
+
else
|
549
|
+
if params && params[:VALUE]
|
550
|
+
case params[:VALUE]
|
551
|
+
when "BOOLEAN"
|
552
|
+
ret = boolean._parse ctx1
|
553
|
+
when "BINARY"
|
554
|
+
ret = binary._parse ctx1
|
555
|
+
when "CAL-ADDRESS"
|
556
|
+
ret = uri._parse ctx1
|
557
|
+
when "DATE-TIME"
|
558
|
+
ret = date_time_t._parse ctx1
|
559
|
+
when "DATE"
|
560
|
+
ret = date_t._parse ctx1
|
561
|
+
when "DURATION"
|
562
|
+
ret = duration_t._parse ctx1
|
563
|
+
when "FLOAT"
|
564
|
+
ret = float_t._parse ctx1
|
565
|
+
when "INTEGER"
|
566
|
+
ret = integer._parse ctx1
|
567
|
+
when "PERIOD"
|
568
|
+
ret = period._parse ctx1
|
569
|
+
when "RECUR"
|
570
|
+
ret = recur._parse ctx1
|
571
|
+
when "TEXT"
|
572
|
+
ret = text_t._parse ctx1
|
573
|
+
when "TIME"
|
574
|
+
ret = time_t._parse ctx1
|
575
|
+
when "URI"
|
576
|
+
ret = uri._parse ctx1
|
577
|
+
when "UTC-OFFSET"
|
578
|
+
ret = utc_offset._parse ctx1
|
579
|
+
end
|
580
|
+
else
|
581
|
+
ret = text_t._parse ctx1
|
582
|
+
end
|
583
|
+
end
|
584
|
+
if ret.is_a?(Hash) && ret[:error]
|
585
|
+
parse_err(strict, errors, "#{ret[:error]} for property #{key}, value #{value}", ctx)
|
586
|
+
end
|
587
|
+
if Rsec::INVALID[ret]
|
588
|
+
parse_err(strict, errors, "Type mismatch for property #{key}, value #{value}", ctx)
|
589
|
+
end
|
590
|
+
Rsec::Fail.reset
|
591
|
+
[ret, errors]
|
592
|
+
end
|
593
|
+
|
594
|
+
private
|
595
|
+
|
596
|
+
def parse_err(strict, errors, msg, ctx)
|
597
|
+
if strict
|
598
|
+
raise ctx.report_error msg, "source"
|
599
|
+
else
|
600
|
+
errors << ctx.report_error(msg, "source")
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|