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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/macos.yml +38 -0
  3. data/.github/workflows/ubuntu.yml +56 -0
  4. data/.github/workflows/windows.yml +40 -0
  5. data/.hound.yml +3 -0
  6. data/.rubocop.tb.yml +650 -0
  7. data/.rubocop.yml +1077 -0
  8. data/Gemfile +1 -1
  9. data/LICENSE.txt +21 -17
  10. data/README.adoc +151 -0
  11. data/Rakefile +1 -1
  12. data/lib/c.rb +173 -0
  13. data/lib/error.rb +19 -0
  14. data/lib/vcalendar.rb +77 -0
  15. data/lib/vcard.rb +67 -0
  16. data/lib/vobject.rb +13 -170
  17. data/lib/vobject/component.rb +87 -36
  18. data/lib/vobject/parameter.rb +116 -0
  19. data/lib/vobject/parametervalue.rb +26 -0
  20. data/lib/vobject/property.rb +134 -55
  21. data/lib/vobject/propertyvalue.rb +46 -0
  22. data/lib/vobject/vcalendar/component.rb +106 -0
  23. data/lib/vobject/vcalendar/grammar.rb +595 -0
  24. data/lib/vobject/vcalendar/paramcheck.rb +259 -0
  25. data/lib/vobject/vcalendar/propertyparent.rb +98 -0
  26. data/lib/vobject/vcalendar/propertyvalue.rb +606 -0
  27. data/lib/vobject/vcalendar/typegrammars.rb +605 -0
  28. data/lib/vobject/vcard/v3_0/component.rb +40 -0
  29. data/lib/vobject/vcard/v3_0/grammar.rb +175 -0
  30. data/lib/vobject/vcard/v3_0/paramcheck.rb +110 -0
  31. data/lib/vobject/vcard/v3_0/parameter.rb +17 -0
  32. data/lib/vobject/vcard/v3_0/property.rb +18 -0
  33. data/lib/vobject/vcard/v3_0/propertyvalue.rb +401 -0
  34. data/lib/vobject/vcard/v3_0/typegrammars.rb +425 -0
  35. data/lib/vobject/vcard/v4_0/component.rb +40 -0
  36. data/lib/vobject/vcard/v4_0/grammar.rb +224 -0
  37. data/lib/vobject/vcard/v4_0/paramcheck.rb +269 -0
  38. data/lib/vobject/vcard/v4_0/parameter.rb +18 -0
  39. data/lib/vobject/vcard/v4_0/property.rb +63 -0
  40. data/lib/vobject/vcard/v4_0/propertyvalue.rb +404 -0
  41. data/lib/vobject/vcard/v4_0/typegrammars.rb +539 -0
  42. data/lib/vobject/version.rb +1 -1
  43. data/vobject.gemspec +19 -16
  44. metadata +81 -26
  45. data/.travis.yml +0 -5
  46. 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