ruby-vobject 0.1.0 → 1.0.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 +5 -5
- data/.hound.yml +3 -0
- data/.rubocop.tb.yml +650 -0
- data/.rubocop.yml +1077 -0
- data/.travis.yml +16 -3
- data/Gemfile +1 -1
- data/LICENSE.txt +21 -17
- data/README.adoc +142 -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 +176 -0
- data/lib/vobject/vcard/v3_0/paramcheck.rb +111 -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 +426 -0
- data/lib/vobject/vcard/v4_0/component.rb +40 -0
- data/lib/vobject/vcard/v4_0/grammar.rb +225 -0
- data/lib/vobject/vcard/v4_0/paramcheck.rb +270 -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 +540 -0
- data/lib/vobject/vcard/version.rb +3 -0
- data/lib/vobject/version.rb +1 -1
- data/ruby-vobject.gemspec +19 -11
- metadata +93 -17
- data/README.md +0 -94
@@ -0,0 +1,225 @@
|
|
1
|
+
require "rsec"
|
2
|
+
require "set"
|
3
|
+
require "uri"
|
4
|
+
require "date"
|
5
|
+
include Rsec::Helpers
|
6
|
+
require "vobject/vcard/version"
|
7
|
+
require "vobject"
|
8
|
+
require "vobject/component"
|
9
|
+
require "vobject/vcard/v4_0/paramcheck"
|
10
|
+
require "vobject/vcard/v4_0/typegrammars"
|
11
|
+
require_relative "../../../c"
|
12
|
+
require_relative "../../../error"
|
13
|
+
|
14
|
+
module Vcard::V4_0
|
15
|
+
class Grammar
|
16
|
+
attr_accessor :strict, :errors
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def unfold(str)
|
20
|
+
str.gsub(/[\n\r]+[ \t]/, "")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# RFC 6868
|
25
|
+
def rfc6868decode(x)
|
26
|
+
x.gsub(/\^n/, "\n").gsub(/\^\^/, "^").gsub(/\^'/, '"')
|
27
|
+
end
|
28
|
+
|
29
|
+
def vobject_grammar
|
30
|
+
# properties with value cardinality 1
|
31
|
+
@cardinality1 = {}
|
32
|
+
@cardinality1[:PARAM] = Set.new [:VALUE]
|
33
|
+
@cardinality1[:PROP] = Set.new [:KIND, :N, :BDAY, :ANNIVERSARY, :GENDER, :PRODID, :REV, :UID, :BIRTHPLACE, :DEATHPLACE, :DEATHDATE]
|
34
|
+
|
35
|
+
group = C::IANATOKEN
|
36
|
+
linegroup = group << "."
|
37
|
+
beginend = /BEGIN/i.r | /END/i.r
|
38
|
+
|
39
|
+
# parameters && parameter types
|
40
|
+
paramname = /LANGUAGE/i.r | /VALUE/i.r | /PREF/i.r | /ALTID/i.r | /PID/i.r |
|
41
|
+
/TYPE/i.r | /MEDIATYPE/i.r | /CALSCALE/i.r | /SORT-AS/i.r |
|
42
|
+
/GEO/i.r | /TZ/i.r | /LABEL/i.r | /INDEX/i.r | /LEVEL/i.r
|
43
|
+
otherparamname = C::NAME_VCARD ^ paramname
|
44
|
+
paramvalue = C::QUOTEDSTRING_VCARD.map { |s| rfc6868decode s } | C::PTEXT_VCARD.map { |s| rfc6868decode(s).upcase }
|
45
|
+
# tzidvalue = seq("/".r._?, C::PTEXT_VCARD).map { |_, val| val }
|
46
|
+
calscalevalue = /GREGORIAN/i.r | C::IANATOKEN | C::XNAME_VCARD
|
47
|
+
prefvalue = /[0-9]{1,2}/i.r | "100".r
|
48
|
+
pidvalue = /[0-9]+(\.[0-9]+)?/.r
|
49
|
+
pidvaluelist = seq(pidvalue, ",", lazy { pidvaluelist }) do |a, _, b|
|
50
|
+
[a, b].flatten
|
51
|
+
end | (pidvalue ^ ",".r).map { |z| [z] }
|
52
|
+
typeparamtel1 = /TEXT/i.r | /VOICE/i.r | /FAX/i.r | /CELL/i.r | /VIDEO/i.r |
|
53
|
+
/PAGER/i.r | /TEXTPHONE/i.r
|
54
|
+
typeparamtel = typeparamtel1 | C::IANATOKEN | C::XNAME_VCARD
|
55
|
+
typeparamrelated = /CONTACT/i.r | /ACQUAINTANCE/i.r | /FRIEND/i.r | /MET/i.r |
|
56
|
+
/CO-WORKER/i.r | /COLLEAGUE/i.r | /CO-RESIDENT/i.r | /NEIGHBOR/i.r |
|
57
|
+
/CHILD/i.r | /PARENT/i.r | /SIBLING/i.r | /SPOUSE/i.r | /KIN/i.r |
|
58
|
+
/MUSE/i.r | /CRUSH/i.r | /DATE/i.r | /SWEETHEART/i.r | /ME/i.r |
|
59
|
+
/AGENT/i.r | /EMERGENCY/i.r
|
60
|
+
typevalue = /WORK/i.r | /HOME/i.r | typeparamtel1 | typeparamrelated | C::IANATOKEN | C::XNAME_VCARD
|
61
|
+
typevaluelist = seq(typevalue << ",".r, lazy { typevaluelist }) do |a, b|
|
62
|
+
[a.upcase, b].flatten
|
63
|
+
end | typevalue.map { |t| [t.upcase] }
|
64
|
+
typeparamtel1list = seq(typeparamtel << ",".r, lazy { typeparamtel1list }) do |a, b|
|
65
|
+
[a.upcase, b].flatten
|
66
|
+
end | typeparamtel.map { |t| [t.upcase] }
|
67
|
+
geourlvalue = seq('"'.r >> C::TEXT4 << '"'.r) do |s|
|
68
|
+
parse_err("geo value not a URI") unless s =~ URI::DEFAULT_PARSER.make_regexp
|
69
|
+
s
|
70
|
+
end
|
71
|
+
tzvalue = paramvalue | geourlvalue
|
72
|
+
valuetype = /TEXT/i.r | /URI/i.r | /TIMESTAMP/i.r | /TIME/i.r | /DATE-TIME/i.r | /DATE/i.r |
|
73
|
+
/DATE-AND-OR-TIME/i.r | /BOOLEAN/i.r | /INTEGER/i.r | /FLOAT/i.r | /UTC-OFFSET/i.r |
|
74
|
+
/LANGUAGE-TAG/i.r | C::IANATOKEN | C::XNAME_VCARD
|
75
|
+
mediaattr = /[!\"#$%&'*+.^A-Z0-9a-z_`i{}|~-]+/.r
|
76
|
+
mediavalue = mediaattr | C::QUOTEDSTRING_VCARD
|
77
|
+
mediatail = seq(";".r >> mediaattr << "=".r, mediavalue).map do |a, v|
|
78
|
+
";#{a}=#{v}"
|
79
|
+
end
|
80
|
+
rfc4288regname = /[A-Za-z0-9!#$&.+^+-]{1,127}/.r
|
81
|
+
rfc4288typename = rfc4288regname
|
82
|
+
rfc4288subtypename = rfc4288regname
|
83
|
+
mediavalue = seq(rfc4288typename << "/".r, rfc4288subtypename, mediatail.star).map do |t, s, tail|
|
84
|
+
ret = "#{t}/#{s}"
|
85
|
+
ret = ret . tail[0] unless tail.empty?
|
86
|
+
ret
|
87
|
+
end
|
88
|
+
pvalue_list = (seq(paramvalue << ",".r, lazy { pvalue_list }) & /[;:]/.r).map do |e, list|
|
89
|
+
[e.sub(Regexp.new("^\"(.+)\"$"), '\1').gsub(/\\n/, "\n"), list].flatten
|
90
|
+
end | (paramvalue & /[;:]/.r).map do |e|
|
91
|
+
[e.sub(Regexp.new("^\"(.+)\"$"), '\1').gsub(/\\n/, "\n")]
|
92
|
+
end
|
93
|
+
quoted_string_list = (seq(C::QUOTEDSTRING_VCARD << ",".r, lazy { quoted_string_list }) & /[;:]/.r).map do |e, list|
|
94
|
+
[e.sub(Regexp.new("^\"(.+)\"$"), '\1').gsub(/\\n/, "\n"), list].flatten
|
95
|
+
end | (C::QUOTEDSTRING_VCARD & /[;:]/.r).map do |e|
|
96
|
+
[e.sub(Regexp.new("^\"(.+)\"$"), '\1').gsub(/\\n/, "\n")]
|
97
|
+
end
|
98
|
+
|
99
|
+
# fmttypevalue = seq(rfc4288typename, "/", rfc4288subtypename).map(&:join)
|
100
|
+
levelvalue = /beginner/i.r | /average/i.r | /expert/i.r | /high/i.r | /medium/i.r | /low/i.r
|
101
|
+
|
102
|
+
param = seq(/ALTID/i.r, "=", paramvalue) do |name, _, val|
|
103
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
104
|
+
end | seq(/LANGUAGE/i.r, "=", C::RFC5646LANGVALUE) do |name, _, val|
|
105
|
+
{ name.upcase.tr("-", "_").to_sym => val.upcase }
|
106
|
+
end | seq(/PREF/i.r, "=", prefvalue) do |name, _, val|
|
107
|
+
{ name.upcase.tr("-", "_").to_sym => val.upcase }
|
108
|
+
end | seq(/TYPE/i.r, "=", "\"".r >> typevaluelist << "\"".r) do |name, _, val|
|
109
|
+
# not in spec but in examples. Errata ID 3488, "Held for Document Update": acknwoledged as error requiring an updated spec. With this included, TYPE="x,y,z" is a list of values; the proper ABNF behaviour is that "x,y,z" is interpreted as a single value
|
110
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
111
|
+
end | seq(/TYPE/i.r, "=", typevaluelist) do |name, _, val|
|
112
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
113
|
+
end | seq(/MEDIATYPE/i.r, "=", mediavalue) do |name, _, val|
|
114
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
115
|
+
end | seq(/CALSCALE/i.r, "=", calscalevalue) do |name, _, val|
|
116
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
117
|
+
end | seq(/SORT-AS/i.r, "=", pvalue_list) do |name, _, val|
|
118
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
119
|
+
end | seq(/TZ/i.r, "=", tzvalue) do |name, _, val|
|
120
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
121
|
+
end | seq(/GEO/i.r, "=", geourlvalue) do |name, _, val|
|
122
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
123
|
+
end | seq(/VALUE/i.r, "=", valuetype) do |name, _, val|
|
124
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
125
|
+
end | seq(/PID/i.r, "=", pidvaluelist) do |name, _, val|
|
126
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
127
|
+
end | seq(/INDEX/i.r, "=", prim(:int32)) do |name, _, val|
|
128
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
129
|
+
end | seq(/LEVEL/i.r, "=", levelvalue) do |name, _, val|
|
130
|
+
{ name.upcase.tr("-", "_").to_sym => val.upcase }
|
131
|
+
end | seq(otherparamname, "=", pvalue_list) do |name, _, val|
|
132
|
+
val = val[0] if val.length == 1
|
133
|
+
{ name.upcase.tr("-", "_").to_sym => val }
|
134
|
+
end | seq(paramname, "=", pvalue_list) do |name, _, val|
|
135
|
+
parse_err("Violated format of parameter value #{name} = #{val}")
|
136
|
+
end
|
137
|
+
|
138
|
+
params = seq(";".r >> param, lazy { params }) do |p, ps|
|
139
|
+
p.merge(ps) do |key, old, new|
|
140
|
+
if @cardinality1[:PARAM].include?(key)
|
141
|
+
parse_err("Violated cardinality of parameter #{key}")
|
142
|
+
end
|
143
|
+
[old, new].flatten
|
144
|
+
# deal with duplicate properties
|
145
|
+
end
|
146
|
+
end | seq(";".r >> param ^ ";".r).map { |e| e[0] }
|
147
|
+
|
148
|
+
contentline = seq(linegroup._?, C::NAME_VCARD, params._? << ":".r,
|
149
|
+
C::VALUE, /[\r\n]/) do |l, name, p, value, _|
|
150
|
+
key = name.upcase.tr("-", "_").to_sym
|
151
|
+
hash = { key => {} }
|
152
|
+
errors << Paramcheck.paramcheck(strict, key, p.empty? ? {} : p[0], @ctx)
|
153
|
+
hash[key][:value], errors1 = Typegrammars.typematch(strict, key, p[0], :GENERIC, value)
|
154
|
+
errors << errors1
|
155
|
+
hash[key][:group] = l[0] unless l.empty?
|
156
|
+
hash[key][:params] = p[0] unless p.empty?
|
157
|
+
hash
|
158
|
+
end
|
159
|
+
props = seq(contentline, lazy { props }) do |c, rest|
|
160
|
+
c.merge(rest) do |key, old, new|
|
161
|
+
if @cardinality1[:PROP].include?(key.upcase) &&
|
162
|
+
!(new.is_a?(Array) &&
|
163
|
+
new[0].key?(:params) && new[0][:params].key?(:ALTID) &&
|
164
|
+
old.key?(:params) && old[:params].key?(:ALTID) &&
|
165
|
+
old[:params][:ALTID] == new[0][:params][:ALTID]) &&
|
166
|
+
!(new.is_a?(Hash) &&
|
167
|
+
old.key?(:params) && old[:params].key?(:ALTID) &&
|
168
|
+
new.key?(:params) && new[:params].key?(:ALTID) &&
|
169
|
+
old[:params][:ALTID] == new[:params][:ALTID])
|
170
|
+
parse_err("Violated cardinality of property #{key}")
|
171
|
+
end
|
172
|
+
[old, new].flatten
|
173
|
+
# deal with duplicate properties
|
174
|
+
end
|
175
|
+
end | ("".r & beginend).map { {} }
|
176
|
+
|
177
|
+
calpropname = /VERSION/i.r
|
178
|
+
calprop = seq(calpropname << ":".r, C::VALUE, /[\r\n]/.r) do |key, value|
|
179
|
+
key = key.upcase.tr("-", "_").to_sym
|
180
|
+
hash = { key => {} }
|
181
|
+
hash[key][:value], errors1 = Typegrammars.typematch(strict, key, nil, :VCARD, value)
|
182
|
+
errors << errors1
|
183
|
+
hash
|
184
|
+
end
|
185
|
+
vobject = seq(/BEGIN:VCARD[\r\n]/i.r >> calprop, props << /END:VCARD[\r\n]/i.r) do |v, rest|
|
186
|
+
parse_err("Missing VERSION attribute") unless v.has_key?(:VERSION)
|
187
|
+
parse_err("Missing FN attribute") unless rest.has_key?(:FN)
|
188
|
+
rest.delete(:END)
|
189
|
+
{ VCARD: v.merge(rest), errors: errors.flatten }
|
190
|
+
end
|
191
|
+
vobject.eof
|
192
|
+
end
|
193
|
+
|
194
|
+
def initialize(strict)
|
195
|
+
self.strict = strict
|
196
|
+
self.errors = []
|
197
|
+
end
|
198
|
+
|
199
|
+
def parse(vobject)
|
200
|
+
@ctx = Rsec::ParseContext.new self.class.unfold(vobject), "source"
|
201
|
+
ret = vobject_grammar._parse @ctx
|
202
|
+
if !ret || Rsec::INVALID[ret]
|
203
|
+
if strict
|
204
|
+
raise @ctx.generate_error "source"
|
205
|
+
else
|
206
|
+
errors << @ctx.generate_error("source")
|
207
|
+
ret = { VCARD: nil, errors: errors.flatten }
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
Rsec::Fail.reset
|
212
|
+
ret
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def parse_err(msg)
|
218
|
+
if strict
|
219
|
+
raise @ctx.report_error msg, "source"
|
220
|
+
else
|
221
|
+
errors << @ctx.report_error(msg, "source")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
require "set"
|
2
|
+
require "uri"
|
3
|
+
require "date"
|
4
|
+
include Rsec::Helpers
|
5
|
+
require "vobject/vcard/version"
|
6
|
+
require "vobject"
|
7
|
+
|
8
|
+
module Vcard::V4_0
|
9
|
+
class Paramcheck
|
10
|
+
class << self
|
11
|
+
def paramcheck(strict, prop, params, ctx)
|
12
|
+
errors = []
|
13
|
+
if params && params[:TYPE]
|
14
|
+
case prop
|
15
|
+
when :FN, :NICKNAME, :PHOTO, :ADR, :TEL, :EMAIL, :IMPP, :LANG, :TZ,
|
16
|
+
:GEO, :TITLE, :ROLE, :LOGO, :ORG, :RELATED, :CATEGORIES, :NOTE,
|
17
|
+
:SOUND, :URL, :KEY, :FBURL, :CALADRURI, :CALURI, :EXPERTISE,
|
18
|
+
:HOBBY, :INTEREST, :ORG_DIRECTORY
|
19
|
+
# no-op
|
20
|
+
when /^x/i
|
21
|
+
# no-op
|
22
|
+
else
|
23
|
+
parse_err(strict, errors, ":TYPE parameter given for #{prop}", ctx)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
if params && params[:MEDIATYPE]
|
27
|
+
case prop
|
28
|
+
when :SOURCE, :PHOTO, :IMPP, :GEO, :LOGO, :MEMBER, :SOUND, :URL,
|
29
|
+
:FBURL, :CALADRURI, :CALURI, :UID, :TZ
|
30
|
+
# no-op
|
31
|
+
when :TEL, :KEY
|
32
|
+
if params[:VALUE] == "uri"
|
33
|
+
else
|
34
|
+
parse_err(strict, errors, ":MEDIATYPE parameter given for #{prop} with :VALUE of text", ctx)
|
35
|
+
end
|
36
|
+
when :RELATED
|
37
|
+
if params[:VALUE] == "text"
|
38
|
+
parse_err(strict, errors, ":MEDIATYPE parameter given for #{prop} with :VALUE of text", ctx)
|
39
|
+
end
|
40
|
+
when /^x/i
|
41
|
+
else
|
42
|
+
parse_err(strict, errors, ":MEDIATYPE parameter given for #{prop}", ctx)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if params && params[:CALSCALE]
|
46
|
+
case prop
|
47
|
+
when :BDAY, :ANNIVERSARY
|
48
|
+
# no-op
|
49
|
+
when :DEATHDATE
|
50
|
+
if params[:VALUE] == "text"
|
51
|
+
parse_err(strict, errors, ":CALSCALE parameter given for #{prop} with :VALUE of text", ctx)
|
52
|
+
end
|
53
|
+
when /^x/i
|
54
|
+
# no-op
|
55
|
+
else
|
56
|
+
parse_err(strict, errors, ":CALSCALE parameter given for #{prop}", ctx)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if params && params[:GEO]
|
60
|
+
case prop
|
61
|
+
when :ADR
|
62
|
+
# no-op
|
63
|
+
when /^x/i
|
64
|
+
# no-op
|
65
|
+
else
|
66
|
+
parse_err(strict, errors, ":GEO parameter given for #{prop}", ctx)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
if params && params[:TZ]
|
70
|
+
case prop
|
71
|
+
when :ADR
|
72
|
+
# no-op
|
73
|
+
when /^x/i
|
74
|
+
# no-op
|
75
|
+
else
|
76
|
+
parse_err(strict, errors, ":TZ parameter given for #{prop}", ctx)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if params && params[:LANGUAGE]
|
80
|
+
case prop
|
81
|
+
when :FN, :N, :NICKNAME, :ADR, :TITLE, :ROLE, :LOGO, :ORG, :NOTE,
|
82
|
+
:SOUND, :BIRTHPLACE, :DEATHPLACE, :EXPERTISE, :HOBBY, :INTEREST, :ORG_DIRECTORY
|
83
|
+
# no-op
|
84
|
+
when :BDAY, :ANNIVERSARY, :DEATHDATE
|
85
|
+
# added :ANNIVERSARY per errata
|
86
|
+
if params[:VALUE] == "text"
|
87
|
+
else
|
88
|
+
parse_err(strict, errors, ":LANGUAGE parameter given for #{prop} with :VALUE of date/time", ctx)
|
89
|
+
end
|
90
|
+
when :RELATED
|
91
|
+
if params[:VALUE] == "text"
|
92
|
+
else
|
93
|
+
parse_err(strict, errors, ":LANGUAGE parameter given for #{prop} with :VALUE of uri", ctx)
|
94
|
+
end
|
95
|
+
when /^x/i
|
96
|
+
# no-op
|
97
|
+
else
|
98
|
+
parse_err(strict, errors, ":LANGUAGE parameter given for #{prop}", ctx)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
if params && params[:VALUE]
|
102
|
+
case prop
|
103
|
+
when :SOURCE, :KIND, :XML, :FN, :N, :NICKNAME, :PHOTO, :GENDER, :ADR,
|
104
|
+
:TEL, :EMAIL, :IMPP, :LANG, :TZ, :GEO, :TITLE, :ROLE, :LOGO, :ORG,
|
105
|
+
:MEMBER, :RELATED, :CATEGORIES, :NOTE, :PRODID, :REV, :SOUND, :URL, :VERSION,
|
106
|
+
:KEY, :FBURL, :CALADRURI, :CALURI, :BDAY, :ANNIVERSARY, :BIRTHPLACE,
|
107
|
+
:DEATHPLACE, :DEATHDATE
|
108
|
+
# no-op
|
109
|
+
when /^x/i
|
110
|
+
# no-op
|
111
|
+
else
|
112
|
+
parse_err(strict, errors, ":VALUE parameter given for #{prop}", ctx)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if params && params[:PREF]
|
116
|
+
case prop
|
117
|
+
when :SOURCE, :FN, :NICKNAME, :PHOTO, :ADR, :TEL, :EMAIL, :IMPP, :LANG,
|
118
|
+
:TZ, :GEO, :TITLE, :ROLE, :LOGO, :ORG, :MEMBER, :RELATED, :CATEGORIES,
|
119
|
+
:NOTE, :SOUND, :URL, :KEY, :FBURL, :CALADRURI, :CALURI, :EXPERTISE,
|
120
|
+
:HOBBY, :INTEREST, :ORG_DIRECTORY, :ORG_DIRECTORY
|
121
|
+
# no-op
|
122
|
+
when /^x/i
|
123
|
+
# no-op
|
124
|
+
else
|
125
|
+
parse_err(strict, errors, ":PREF parameter given for #{prop}", ctx)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
if params && params[:PID]
|
129
|
+
case prop
|
130
|
+
when :SOURCE, :FN, :NICKNAME, :PHOTO, :ADR, :TEL, :EMAIL, :IMPP, :LANG,
|
131
|
+
:TZ, :GEO, :TITLE, :ROLE, :LOGO, :ORG, :MEMBER, :RELATED, :CATEGORIES,
|
132
|
+
:NOTE, :SOUND, :URL, :KEY, :FBURL, :CALADRURI, :CALURI, :ORG_DIRECTORY
|
133
|
+
# no-op
|
134
|
+
when /^x/i
|
135
|
+
# no-op
|
136
|
+
else
|
137
|
+
parse_err(strict, errors, ":PID parameter given for #{prop}", ctx)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
if params && params[:SORT_AS]
|
141
|
+
case prop
|
142
|
+
when :N, :ORG
|
143
|
+
# no-op
|
144
|
+
when /^x/i
|
145
|
+
# no-op
|
146
|
+
else
|
147
|
+
parse_err(strict, errors, ":SORT_AS parameter given for #{prop}", ctx)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
if params && params[:ALTID]
|
151
|
+
case prop
|
152
|
+
when :SOURCE, :XML, :FN, :N, :NICKNAME, :PHOTO, :BDAY, :ANNIVERSARY, :ADR,
|
153
|
+
:TEL, :EMAIL, :IMPP, :LANG, :TZ, :GEO, :TITLE, :ROLE, :LOGO, :ORG, :MEMBER,
|
154
|
+
:RELATED, :CATEGORIES, :NOTE, :SOUND, :URL, :KEY, :FBURL, :CALADRURI, :CALURI,
|
155
|
+
:BIRTHPLACE, :DEATHPLACE, :DEATHDATE, :EXPERTISE, :HOBBY, :INTEREST, :ORG_DIRECTORY
|
156
|
+
# no-op
|
157
|
+
when /^x/i
|
158
|
+
# no-op
|
159
|
+
else
|
160
|
+
parse_err(strict, errors, ":SOURCE parameter given for #{prop}", ctx)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
if params && params[:LABEL]
|
164
|
+
case prop
|
165
|
+
when :ADR
|
166
|
+
# no-op
|
167
|
+
when /^x/i
|
168
|
+
# no-op
|
169
|
+
else
|
170
|
+
parse_err(strict, errors, ":LABEL parameter given for #{prop}", ctx)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
if params && params[:LEVEL]
|
174
|
+
case prop
|
175
|
+
when :EXPERTISE, :HOBBY, :INTEREST
|
176
|
+
# no-op
|
177
|
+
when /^x/i
|
178
|
+
# no-op
|
179
|
+
else
|
180
|
+
parse_err(strict, errors, ":LEVEL parameter given for #{prop}", ctx)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
if params && params[:INDEX]
|
184
|
+
case prop
|
185
|
+
when :EXPERTISE, :HOBBY, :INTEREST, :ORG_DIRECTORY
|
186
|
+
# no-op
|
187
|
+
when /^x/i
|
188
|
+
# no-op
|
189
|
+
else
|
190
|
+
parse_err(strict, errors, ":INDEX parameter given for #{prop}", ctx)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
params.each do |p|
|
194
|
+
case p
|
195
|
+
when :LANGUAGE, :VALUE, :PREF, :PID, :TYPE, :GEO, :TZ, :SORT_AS, :CALSCALE,
|
196
|
+
:LABEL, :ALTID
|
197
|
+
# no-op
|
198
|
+
when /^x/i
|
199
|
+
# xname parameters are always allowed
|
200
|
+
else
|
201
|
+
# any-param
|
202
|
+
case prop
|
203
|
+
when :SOURCE, :KIND, :FN, :N, :NICKNAME, :PHOTO, :BDAY, :ANNIVERSARY, :GENDER,
|
204
|
+
:ADR, :TEL, :EMAIL, :IMPP, :LANG, :TZ, :GEO, :TITLE, :ROLE, :LOGO, :ORG, :MEMBER,
|
205
|
+
:RELATED, :CATEGORIES, :NOTE, :PRODID, :REV, :SOUND, :UID, :CLIENTPIDMAP, :URL,
|
206
|
+
:VERSION, :KEY, :FBURL, :CALADRURI, :CALURI, :BIRTHPLACE, :DEATHPLACE, :DEATHDATE,
|
207
|
+
:EXPERTISE, :HOBBY, :INTEREST, :ORG_DIRECTORY
|
208
|
+
# no-op
|
209
|
+
when /^x/i
|
210
|
+
# no-op
|
211
|
+
else
|
212
|
+
parse_err(strict, errors, "#{p} parameter given for #{prop}", ctx)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
case prop
|
217
|
+
when :SOURCE, :PHOTO, :IMPP, :GEO, :LOGO, :MEMBER, :SOUND, :URL, :FBURL,
|
218
|
+
:CALADRURI, :CALURI
|
219
|
+
params.each do |key, val|
|
220
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "uri"
|
221
|
+
end
|
222
|
+
when :LANG
|
223
|
+
params.each do |key, val|
|
224
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "language-tag"
|
225
|
+
end
|
226
|
+
when :REV
|
227
|
+
params.each do |key, val|
|
228
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "timestamp"
|
229
|
+
end
|
230
|
+
when :KIND, :XML, :FN, :N, :NICKNAME, :GENDER, :ADR, :EMAIL, :TITLE, :ROLE, :ORG,
|
231
|
+
:CATEGORIES, :NOTE, :PRODID, :VERSION
|
232
|
+
params.each do |key, val|
|
233
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "text"
|
234
|
+
end
|
235
|
+
when :BDAY, :ANNIVERSARY, :DEATHDATE
|
236
|
+
params.each do |key, val|
|
237
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "date-and-or-time" && val != "text"
|
238
|
+
end
|
239
|
+
when :TEL, :RELATED, :UID, :KEY, :BIRTHPLACE, :DEATHPLACE
|
240
|
+
params.each do |key, val|
|
241
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "uri" && val != "text"
|
242
|
+
end
|
243
|
+
when :TZ
|
244
|
+
params.each do |key, val|
|
245
|
+
parse_err(strict, errors, "illegal value #{val} given for parameter #{key} of #{prop}", ctx) if key == :VALUE && val != "uri" && val != "text" && val != "utc-offset"
|
246
|
+
end
|
247
|
+
when :EXPERTISE
|
248
|
+
if params && params[:LEVEL]
|
249
|
+
parse_err(strict, errors, "illegal value #{params[:LEVEL]} given for parameter :LEVEL of #{prop}", ctx) unless params[:LEVEL] =~ /^(beginner|average|expert)$/i
|
250
|
+
end
|
251
|
+
when :HOBBY, :INTEREST
|
252
|
+
if params && params[:LEVEL]
|
253
|
+
parse_err(strict, errors, "illegal value #{params[:LEVEL]} given for parameter :LEVEL of #{prop}", ctx) unless params[:LEVEL] =~ /^(high|medium|low)$/i
|
254
|
+
end
|
255
|
+
end
|
256
|
+
errors
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def parse_err(strict, errors, msg, ctx)
|
262
|
+
if strict
|
263
|
+
raise ctx.report_error msg, "source"
|
264
|
+
else
|
265
|
+
errors << ctx.report_error(msg, "source")
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|