vobject 0.1.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|