vcard 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +15 -5
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +17 -0
- data/Rakefile +4 -52
- data/{LICENSE → VPIM-LICENSE.txt} +2 -2
- data/lib/vcard.rb +304 -28
- data/lib/vcard/attachment.rb +10 -12
- data/lib/vcard/bnf.rb +66 -0
- data/lib/vcard/dirinfo.rb +24 -26
- data/lib/vcard/enumerator.rb +5 -7
- data/lib/vcard/errors.rb +23 -0
- data/lib/vcard/field.rb +56 -58
- data/lib/vcard/vcard.rb +210 -240
- data/lib/vcard/version.rb +3 -0
- data/test/field_test.rb +55 -55
- data/test/fixtures/bday_decode.vcard +3 -0
- data/test/fixtures/bday_decode_2.vcard +6 -0
- data/test/fixtures/empty_tel.vcard +3 -0
- data/test/fixtures/ex1.vcard +7 -0
- data/test/fixtures/ex2.vcard +9 -0
- data/test/fixtures/ex3.vcard +30 -0
- data/test/fixtures/ex_21.vcard +16 -0
- data/test/fixtures/ex_21_case0.vcard +15 -0
- data/test/fixtures/ex_apple1.vcard +13 -0
- data/test/fixtures/ex_attach.vcard +16 -0
- data/test/fixtures/ex_bdays.vcard +8 -0
- data/test/fixtures/ex_encode_1.vcard +10 -0
- data/test/fixtures/ex_ical_1.vcal +47 -0
- data/test/fixtures/gmail.vcard +27 -0
- data/test/fixtures/highrise.vcard +41 -0
- data/test/fixtures/multiple_occurences_of_type.vcard +17 -0
- data/test/fixtures/nickname0.vcard +2 -0
- data/test/fixtures/nickname1.vcard +3 -0
- data/test/fixtures/nickname2.vcard +3 -0
- data/test/fixtures/nickname3.vcard +3 -0
- data/test/fixtures/nickname4.vcard +4 -0
- data/test/fixtures/nickname5.vcard +5 -0
- data/test/fixtures/slash_in_field_name.vcard +3 -0
- data/test/fixtures/tst1.vcard +9 -0
- data/test/fixtures/url_decode.vcard +4 -0
- data/test/test_helper.rb +34 -6
- data/test/vcard_test.rb +87 -577
- data/vcard.gemspec +19 -0
- metadata +88 -43
- data/.document +0 -5
- data/README.rdoc +0 -7
- data/VERSION +0 -1
- data/lib/vcard/rfc2425.rb +0 -367
data/lib/vcard/attachment.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
Copyright (C) 2008 Sam Roberts
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
=end
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the same terms as the ruby language itself, see the file
|
5
|
+
# LICENSE-VPIM.txt for details.
|
8
6
|
|
9
|
-
module
|
7
|
+
module Vcard
|
10
8
|
|
11
9
|
# Attachments are used by both iCalendar and vCard. They are either a URI or
|
12
10
|
# inline data, and their decoded value will be either a Uri or a Inline, as
|
@@ -31,14 +29,14 @@ module Vpim
|
|
31
29
|
# iCalendar and vCard put the format in different parameters, and the
|
32
30
|
# default kind of value is different.
|
33
31
|
def Attachment.decode(field, defkind, fmtparam) #:nodoc:
|
34
|
-
format = field.pvalue(fmtparam) ||
|
32
|
+
format = field.pvalue(fmtparam) || ""
|
35
33
|
kind = field.kind || defkind
|
36
34
|
case kind
|
37
|
-
when
|
38
|
-
Inline.new(
|
39
|
-
when
|
35
|
+
when "text"
|
36
|
+
Inline.new(::Vcard.decode_text(field.value), format)
|
37
|
+
when "uri"
|
40
38
|
Uri.new(field.value_raw, format)
|
41
|
-
when
|
39
|
+
when "binary"
|
42
40
|
Inline.new(field.value, format)
|
43
41
|
else
|
44
42
|
raise InvalidEncodingError, "Attachment of type #{kind} is not allowed"
|
data/lib/vcard/bnf.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
2
|
+
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the same terms as the ruby language itself, see the file
|
5
|
+
# LICENSE-VPIM.txt for details.
|
6
|
+
|
7
|
+
module Vcard
|
8
|
+
# Contains regular expression strings for the EBNF of RFC 2425.
|
9
|
+
module Bnf #:nodoc:
|
10
|
+
|
11
|
+
# 1*(ALPHA / DIGIT / "-")
|
12
|
+
# Note: I think I can add A-Z here, and get rid of the "i" matches elsewhere.
|
13
|
+
# Note: added "_" to allowed because its produced by Notes (X-LOTUS-CHILD_UID:)
|
14
|
+
# Note: added "/" to allowed because its produced by KAddressBook (X-messaging/xmpp-All:)
|
15
|
+
# Note: added " " to allowed because its produced by highrisehq.com (X-GOOGLE TALK:)
|
16
|
+
NAME = "[-a-z0-9_/][-a-z0-9_/ ]*"
|
17
|
+
|
18
|
+
# <"> <Any character except CTLs, DQUOTE> <">
|
19
|
+
QSTR = '"([^"]*)"'
|
20
|
+
|
21
|
+
# *<Any character except CTLs, DQUOTE, ";", ":", ",">
|
22
|
+
PTEXT = '([^";:,]+)'
|
23
|
+
|
24
|
+
# param-value = ptext / quoted-string
|
25
|
+
PVALUE = "(?:#{QSTR}|#{PTEXT})"
|
26
|
+
|
27
|
+
# param = name "=" param-value *("," param-value)
|
28
|
+
# Note: v2.1 allows a type or encoding param-value to appear without the type=
|
29
|
+
# or the encoding=. This is hideous, but we try and support it, if there
|
30
|
+
# is no "=", then $2 will be "", and we will treat it as a v2.1 param.
|
31
|
+
PARAM = ";(#{NAME})(=?)((?:#{PVALUE})?(?:,#{PVALUE})*)"
|
32
|
+
|
33
|
+
# V3.0: contentline = [group "."] name *(";" param) ":" value
|
34
|
+
# V2.1: contentline = *( group "." ) name *(";" param) ":" value
|
35
|
+
#
|
36
|
+
# We accept the V2.1 syntax for backwards compatibility.
|
37
|
+
#LINE = "((?:#{NAME}\\.)*)?(#{NAME})([^:]*)\:(.*)"
|
38
|
+
LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):(.*)$"
|
39
|
+
|
40
|
+
# date = date-fullyear ["-"] date-month ["-"] date-mday
|
41
|
+
# date-fullyear = 4 DIGIT
|
42
|
+
# date-month = 2 DIGIT
|
43
|
+
# date-mday = 2 DIGIT
|
44
|
+
DATE = "(\d\d\d\d)-?(\d\d)-?(\d\d)"
|
45
|
+
|
46
|
+
# time = time-hour [":"] time-minute [":"] time-second [time-secfrac] [time-zone]
|
47
|
+
# time-hour = 2 DIGIT
|
48
|
+
# time-minute = 2 DIGIT
|
49
|
+
# time-second = 2 DIGIT
|
50
|
+
# time-secfrac = "," 1*DIGIT
|
51
|
+
# time-zone = "Z" / time-numzone
|
52
|
+
# time-numzone = sign time-hour [":"] time-minute
|
53
|
+
TIME = "(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?"
|
54
|
+
|
55
|
+
# integer = (["+"] / "-") 1*DIGIT
|
56
|
+
INTEGER = "[-+]?\d+"
|
57
|
+
|
58
|
+
# QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
|
59
|
+
# ; Any character except CTLs and DQUOTE
|
60
|
+
QSAFECHAR = "[ \t\x21\x23-\x7e\x80-\xff]"
|
61
|
+
|
62
|
+
# SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
|
63
|
+
# ; Any character except CTLs, DQUOTE, ";", ":", ","
|
64
|
+
SAFECHAR = "[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]"
|
65
|
+
end
|
66
|
+
end
|
data/lib/vcard/dirinfo.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
Copyright (C) 2008 Sam Roberts
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
=end
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the same terms as the ruby language itself, see the file
|
5
|
+
# LICENSE-VPIM.txt for details.
|
8
6
|
|
9
|
-
module
|
7
|
+
module Vcard
|
10
8
|
# An RFC 2425 directory info object.
|
11
9
|
#
|
12
10
|
# A directory information object is a sequence of fields. The basic
|
@@ -19,13 +17,13 @@ module Vpim
|
|
19
17
|
#
|
20
18
|
# Here's an example of encoding a simple vCard using the low-level APIs:
|
21
19
|
#
|
22
|
-
# card =
|
23
|
-
# card <<
|
24
|
-
# card <<
|
25
|
-
# card <<
|
20
|
+
# card = Vcard::Vcard.create
|
21
|
+
# card << Vcard::DirectoryInfo::Field.create("EMAIL", "user.name@example.com", "TYPE" => "INTERNET" )
|
22
|
+
# card << Vcard::DirectoryInfo::Field.create("URL", "http://www.example.com/user" )
|
23
|
+
# card << Vcard::DirectoryInfo::Field.create("FN", "User Name" )
|
26
24
|
# puts card.to_s
|
27
25
|
#
|
28
|
-
# Don't do it like that, use
|
26
|
+
# Don't do it like that, use Vcard::Vcard::Maker.
|
29
27
|
class DirectoryInfo
|
30
28
|
include Enumerable
|
31
29
|
|
@@ -35,7 +33,7 @@ module Vpim
|
|
35
33
|
# specified, check the BEGIN/END fields.
|
36
34
|
def initialize(fields, profile = nil) #:nodoc:
|
37
35
|
if fields.detect { |f| ! f.kind_of? DirectoryInfo::Field }
|
38
|
-
raise ArgumentError,
|
36
|
+
raise ArgumentError, "fields must be an array of DirectoryInfo::Field objects"
|
39
37
|
end
|
40
38
|
|
41
39
|
@string = nil # this is used as a flag to indicate that recoding will be necessary
|
@@ -53,7 +51,7 @@ module Vpim
|
|
53
51
|
# The lines in the string may be delimited using IETF (CRLF) or Unix (LF) conventions.
|
54
52
|
#
|
55
53
|
# A DirectoryInfo is mutable, you can add new fields to it, see
|
56
|
-
#
|
54
|
+
# Vcard::DirectoryInfo::Field#create() for how to create a new Field.
|
57
55
|
#
|
58
56
|
# TODO: I don't believe this is ever used, maybe I can remove it.
|
59
57
|
def DirectoryInfo.decode(card) #:nodoc:
|
@@ -67,7 +65,7 @@ module Vpim
|
|
67
65
|
raise ArgumentError, "DirectoryInfo cannot be created from a #{card.type}"
|
68
66
|
end
|
69
67
|
|
70
|
-
fields =
|
68
|
+
fields = ::Vcard.decode(string)
|
71
69
|
|
72
70
|
new(fields)
|
73
71
|
end
|
@@ -84,9 +82,9 @@ module Vpim
|
|
84
82
|
|
85
83
|
if profile
|
86
84
|
p = profile.to_str
|
87
|
-
f = [ Field.create(
|
85
|
+
f = [ Field.create("BEGIN", p) ]
|
88
86
|
f.concat fields
|
89
|
-
f.push Field.create(
|
87
|
+
f.push Field.create("END", p)
|
90
88
|
fields = f
|
91
89
|
end
|
92
90
|
|
@@ -103,7 +101,7 @@ module Vpim
|
|
103
101
|
# The value of the first field named +name+, or nil if no
|
104
102
|
# match is found.
|
105
103
|
def [](name)
|
106
|
-
enum_by_name(name).each { |f| return f.value if f.value !=
|
104
|
+
enum_by_name(name).each { |f| return f.value if f.value != ""}
|
107
105
|
enum_by_name(name).each { |f| return f.value }
|
108
106
|
nil
|
109
107
|
end
|
@@ -153,11 +151,11 @@ module Vpim
|
|
153
151
|
#
|
154
152
|
# Print all the nicknames in a card:
|
155
153
|
#
|
156
|
-
# card.enum_by_name(
|
154
|
+
# card.enum_by_name("NICKNAME") { |f| puts f.value }
|
157
155
|
#
|
158
156
|
# Print an Array of the preferred email addresses in the card:
|
159
157
|
#
|
160
|
-
# pref_emails = card.enum_by_name(
|
158
|
+
# pref_emails = card.enum_by_name("EMAIL").select { |f| f.pref? }
|
161
159
|
def enum_by_name(name)
|
162
160
|
Enumerator.new(self, Proc.new { |field| field.name?(name) })
|
163
161
|
end
|
@@ -172,9 +170,9 @@ module Vpim
|
|
172
170
|
# end
|
173
171
|
# end
|
174
172
|
#
|
175
|
-
# or to get an array of all the fields in group
|
173
|
+
# or to get an array of all the fields in group "AGROUP", you could do:
|
176
174
|
#
|
177
|
-
# card.enum_by_group(
|
175
|
+
# card.enum_by_group("AGROUP").to_a
|
178
176
|
def enum_by_group(group)
|
179
177
|
Enumerator.new(self, Proc.new { |field| field.group?(group) })
|
180
178
|
end
|
@@ -224,8 +222,8 @@ module Vpim
|
|
224
222
|
# the Vcard::Maker examples.
|
225
223
|
def delete(field)
|
226
224
|
case
|
227
|
-
when field.name?(
|
228
|
-
raise ArgumentError,
|
225
|
+
when field.name?("BEGIN"), field.name?("END")
|
226
|
+
raise ArgumentError, "Cannot delete BEGIN or END fields."
|
229
227
|
else
|
230
228
|
@fields.delete field
|
231
229
|
end
|
@@ -251,10 +249,10 @@ module Vpim
|
|
251
249
|
unless @fields.first
|
252
250
|
raise "No fields to check"
|
253
251
|
end
|
254
|
-
unless @fields.first.name?
|
252
|
+
unless @fields.first.name? "BEGIN"
|
255
253
|
raise "Needs BEGIN, found: #{@fields.first.encode nil}"
|
256
254
|
end
|
257
|
-
unless @fields.last.name?
|
255
|
+
unless @fields.last.name? "END"
|
258
256
|
raise "Needs END, found: #{@fields.last.encode nil}"
|
259
257
|
end
|
260
258
|
unless @fields.last.value? @fields.first.value
|
data/lib/vcard/enumerator.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
Copyright (C) 2008 Sam Roberts
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
=end
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the same terms as the ruby language itself, see the file
|
5
|
+
# LICENSE-VPIM.txt for details.
|
8
6
|
|
9
|
-
module
|
7
|
+
module Vcard
|
10
8
|
# This is a way for an object to have multiple ways of being enumerated via
|
11
9
|
# argument to it's #each() method. An Enumerator mixes in Enumerable, so the
|
12
10
|
# standard APIs such as Enumerable#map(), Enumerable#to_a(), and
|
data/lib/vcard/errors.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
2
|
+
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the same terms as the ruby language itself, see the file
|
5
|
+
# LICENSE-VPIM.txt for details.
|
6
|
+
|
7
|
+
module Vcard
|
8
|
+
# Exception used to indicate that data being decoded is invalid, the message
|
9
|
+
# should describe what is invalid.
|
10
|
+
class InvalidEncodingError < StandardError; end
|
11
|
+
|
12
|
+
# Exception used to indicate that data being decoded is unsupported, the message
|
13
|
+
# should describe what is unsupported.
|
14
|
+
#
|
15
|
+
# If its unsupported, its likely because I didn't anticipate it being useful
|
16
|
+
# to support this, and it likely it could be supported on request.
|
17
|
+
class UnsupportedError < StandardError; end
|
18
|
+
|
19
|
+
# Exception used to indicate that encoding failed, probably because the
|
20
|
+
# object would not result in validly encoded data. The message should
|
21
|
+
# describe what is unsupported.
|
22
|
+
class Unencodeable < StandardError; end
|
23
|
+
end
|
data/lib/vcard/field.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
Copyright (C) 2008 Sam Roberts
|
1
|
+
# Copyright (C) 2008 Sam Roberts
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
=end
|
3
|
+
# This library is free software; you can redistribute it and/or modify it
|
4
|
+
# under the same terms as the ruby language itself, see the file COPYING for
|
5
|
+
# details.
|
8
6
|
|
9
|
-
module
|
7
|
+
module Vcard
|
10
8
|
|
11
9
|
class DirectoryInfo
|
12
10
|
|
@@ -19,7 +17,7 @@ module Vpim
|
|
19
17
|
# pvalue_iset/idel/iadd, where set sets them all, add adds if not present,
|
20
18
|
# and del deletes any that are present
|
21
19
|
# - I really, really, need a case-insensitive string...
|
22
|
-
# - should allow nil as a field value, its not the same as
|
20
|
+
# - should allow nil as a field value, its not the same as "", if there is
|
23
21
|
# more than one pvalue, the empty string will show up. This isn't strictly
|
24
22
|
# disallowed, but its odd. Should also strip empty strings on decoding, if
|
25
23
|
# I don't already.
|
@@ -37,14 +35,14 @@ module Vpim
|
|
37
35
|
end
|
38
36
|
|
39
37
|
# Encode a field.
|
40
|
-
def Field.encode0(group, name, params={}, value=
|
38
|
+
def Field.encode0(group, name, params={}, value="") # :nodoc:
|
41
39
|
line = ""
|
42
40
|
|
43
41
|
# A reminder of the line format:
|
44
42
|
# [<group>.]<name>;<pname>=<pvalue>,<pvalue>:<value>
|
45
43
|
|
46
44
|
if group
|
47
|
-
line << group <<
|
45
|
+
line << group << "."
|
48
46
|
end
|
49
47
|
|
50
48
|
line << name
|
@@ -55,15 +53,15 @@ module Vpim
|
|
55
53
|
pvalues = [ pvalues ]
|
56
54
|
end
|
57
55
|
|
58
|
-
line <<
|
56
|
+
line << ";" << pname << "="
|
59
57
|
|
60
|
-
sep = "" # set to
|
58
|
+
sep = "" # set to "," after one pvalue has been appended
|
61
59
|
|
62
60
|
pvalues.each do |pvalue|
|
63
61
|
# check if we need to do any encoding
|
64
|
-
if pname.casecmp(
|
65
|
-
pvalue =
|
66
|
-
value = [ value.to_str ].pack(
|
62
|
+
if pname.casecmp("ENCODING") == 0 && pvalue == :b64
|
63
|
+
pvalue = "B" # the RFC definition of the base64 param value
|
64
|
+
value = [ value.to_str ].pack("m").gsub("\n", "")
|
67
65
|
end
|
68
66
|
|
69
67
|
line << sep << pvalue
|
@@ -71,7 +69,7 @@ module Vpim
|
|
71
69
|
end
|
72
70
|
end
|
73
71
|
|
74
|
-
line <<
|
72
|
+
line << ":"
|
75
73
|
|
76
74
|
line << Field.value_str(value)
|
77
75
|
|
@@ -79,16 +77,16 @@ module Vpim
|
|
79
77
|
end
|
80
78
|
|
81
79
|
def Field.value_str(value) # :nodoc:
|
82
|
-
line =
|
80
|
+
line = ""
|
83
81
|
case value
|
84
82
|
when Date
|
85
|
-
line <<
|
83
|
+
line << ::Vcard.encode_date(value)
|
86
84
|
|
87
85
|
when Time #, DateTime
|
88
|
-
line <<
|
86
|
+
line << ::Vcard.encode_date_time(value)
|
89
87
|
|
90
88
|
when Array
|
91
|
-
line << value.map { |v| Field.value_str(v) }.join(
|
89
|
+
line << value.map { |v| Field.value_str(v) }.join(";")
|
92
90
|
|
93
91
|
when Symbol
|
94
92
|
line << value
|
@@ -103,7 +101,7 @@ module Vpim
|
|
103
101
|
# Decode a field.
|
104
102
|
def Field.decode0(atline) # :nodoc:
|
105
103
|
unless atline =~ %r{#{Bnf::LINE}}i
|
106
|
-
raise
|
104
|
+
raise ::Vcard::InvalidEncodingError, atline
|
107
105
|
end
|
108
106
|
|
109
107
|
atgroup = $1.upcase
|
@@ -117,7 +115,7 @@ module Vpim
|
|
117
115
|
atvalue.strip!
|
118
116
|
|
119
117
|
if atgroup.length > 0
|
120
|
-
atgroup.chomp!(
|
118
|
+
atgroup.chomp!(".")
|
121
119
|
else
|
122
120
|
atgroup = nil
|
123
121
|
end
|
@@ -134,21 +132,21 @@ module Vpim
|
|
134
132
|
name = $1.upcase
|
135
133
|
params = $3
|
136
134
|
|
137
|
-
# v2.1 params have no
|
138
|
-
# is (either its a known encoding, or we treat it as a
|
135
|
+
# v2.1 params have no "=" sign, figure out what kind of param it
|
136
|
+
# is (either its a known encoding, or we treat it as a "TYPE"
|
139
137
|
# param).
|
140
138
|
|
141
139
|
if $2 == ""
|
142
140
|
params = $1
|
143
141
|
case $1
|
144
142
|
when /quoted-printable/i
|
145
|
-
name =
|
143
|
+
name = "ENCODING"
|
146
144
|
|
147
145
|
when /base64/i
|
148
|
-
name =
|
146
|
+
name = "ENCODING"
|
149
147
|
|
150
148
|
else
|
151
|
-
name =
|
149
|
+
name = "TYPE"
|
152
150
|
end
|
153
151
|
end
|
154
152
|
|
@@ -188,16 +186,16 @@ module Vpim
|
|
188
186
|
# name (a String) to either a single string or symbol, or an array of
|
189
187
|
# strings and symbols (parameters can be multi-valued).
|
190
188
|
#
|
191
|
-
# If
|
189
|
+
# If "ENCODING" => :b64 is specified as a parameter, the value will be
|
192
190
|
# base-64 encoded. If it's already base-64 encoded, then use String
|
193
|
-
# values (
|
191
|
+
# values ("ENCODING" => "B"), and no further encoding will be done by
|
194
192
|
# this routine.
|
195
193
|
#
|
196
194
|
# Currently handled value types are:
|
197
195
|
# - Time, encoded as a date-time value
|
198
196
|
# - Date, encoded as a date value
|
199
197
|
# - String, encoded directly
|
200
|
-
# - Array of String, concatentated with
|
198
|
+
# - Array of String, concatentated with ";" between them.
|
201
199
|
#
|
202
200
|
# TODO - need a way to encode String values as TEXT, at least optionally,
|
203
201
|
# so as to escape special chars, etc.
|
@@ -206,7 +204,7 @@ module Vpim
|
|
206
204
|
|
207
205
|
begin
|
208
206
|
new(line)
|
209
|
-
rescue
|
207
|
+
rescue ::Vcard::InvalidEncodingError => e
|
210
208
|
raise ArgumentError, e.to_s
|
211
209
|
end
|
212
210
|
end
|
@@ -236,7 +234,7 @@ module Vpim
|
|
236
234
|
l = l.gsub(/.{#{width},#{width}}/) { |m| m + "\n " }
|
237
235
|
end
|
238
236
|
# Make sure it's terminated with no more than a single NL.
|
239
|
-
l.gsub(/\s*\z/,
|
237
|
+
l.gsub(/\s*\z/, "") + "\n"
|
240
238
|
end
|
241
239
|
|
242
240
|
alias to_s encode
|
@@ -306,17 +304,17 @@ module Vpim
|
|
306
304
|
# if VALUE parameter is not present.
|
307
305
|
def value
|
308
306
|
case encoding
|
309
|
-
when nil,
|
307
|
+
when nil, "8BIT", "7BIT" then @value
|
310
308
|
|
311
309
|
# Hack - if the base64 lines started with 2 SPC chars, which is invalid,
|
312
310
|
# there will be extra spaces in @value. Since no SPC chars show up in
|
313
311
|
# b64 encodings, they can be safely stripped out before unpacking.
|
314
|
-
when
|
312
|
+
when "B", "BASE64" then @value.gsub(" ", "").unpack("m*").first
|
315
313
|
|
316
|
-
when
|
314
|
+
when "QUOTED-PRINTABLE" then @value.unpack("M*").first
|
317
315
|
|
318
316
|
else
|
319
|
-
raise
|
317
|
+
raise ::Vcard::InvalidEncodingError, "unrecognized encoding (#{encoding})"
|
320
318
|
end
|
321
319
|
end
|
322
320
|
|
@@ -360,7 +358,7 @@ module Vpim
|
|
360
358
|
def type?(type)
|
361
359
|
type = type.to_str
|
362
360
|
|
363
|
-
types = param(
|
361
|
+
types = param("TYPE")
|
364
362
|
|
365
363
|
if types
|
366
364
|
types = types.detect { |t| t.casecmp(type) == 0 }
|
@@ -368,18 +366,18 @@ module Vpim
|
|
368
366
|
end
|
369
367
|
|
370
368
|
# Is this field marked as preferred? A vCard field is preferred if
|
371
|
-
# #type?(
|
369
|
+
# #type?("PREF"). This method is not necessarily meaningful for
|
372
370
|
# non-vCard profiles.
|
373
371
|
def pref?
|
374
|
-
type?
|
372
|
+
type? "PREF"
|
375
373
|
end
|
376
374
|
|
377
375
|
# Set whether a field is marked as preferred. See #pref?
|
378
376
|
def pref=(ispref)
|
379
377
|
if ispref
|
380
|
-
pvalue_iadd(
|
378
|
+
pvalue_iadd("TYPE", "PREF")
|
381
379
|
else
|
382
|
-
pvalue_idel(
|
380
|
+
pvalue_idel("TYPE", "PREF")
|
383
381
|
end
|
384
382
|
end
|
385
383
|
|
@@ -392,11 +390,11 @@ module Vpim
|
|
392
390
|
# The value of the ENCODING parameter, if present, or nil if not
|
393
391
|
# present.
|
394
392
|
def encoding
|
395
|
-
e = param(
|
393
|
+
e = param("ENCODING")
|
396
394
|
|
397
395
|
if e
|
398
396
|
if e.length > 1
|
399
|
-
raise
|
397
|
+
raise ::Vcard::InvalidEncodingError, "multi-valued param 'ENCODING' (#{e})"
|
400
398
|
end
|
401
399
|
e = e.first.upcase
|
402
400
|
end
|
@@ -406,7 +404,7 @@ module Vpim
|
|
406
404
|
# The type of the value, as specified by the VALUE parameter, nil if
|
407
405
|
# unspecified.
|
408
406
|
def kind
|
409
|
-
v = param(
|
407
|
+
v = param("VALUE")
|
410
408
|
if v
|
411
409
|
if v.size > 1
|
412
410
|
raise InvalidEncodingError, "multi-valued param 'VALUE' (#{values})"
|
@@ -430,7 +428,7 @@ module Vpim
|
|
430
428
|
# its ridiculous! I think I need my own DateTime variant.
|
431
429
|
def to_time
|
432
430
|
begin
|
433
|
-
|
431
|
+
::Vcard.decode_date_time_list(value).collect do |d|
|
434
432
|
# We get [ year, month, day, hour, min, sec, usec, tz ]
|
435
433
|
begin
|
436
434
|
if(d.pop == "Z")
|
@@ -439,16 +437,16 @@ module Vpim
|
|
439
437
|
Time.local(*d)
|
440
438
|
end
|
441
439
|
rescue ArgumentError => e
|
442
|
-
raise
|
440
|
+
raise ::Vcard::InvalidEncodingError, "Time.gm(#{d.join(', ')}) failed with #{e.message}"
|
443
441
|
end
|
444
442
|
end
|
445
|
-
rescue
|
446
|
-
|
443
|
+
rescue ::Vcard::InvalidEncodingError
|
444
|
+
::Vcard.decode_date_list(value).collect do |d|
|
447
445
|
# We get [ year, month, day ]
|
448
446
|
begin
|
449
447
|
Time.gm(*d)
|
450
448
|
rescue ArgumentError => e
|
451
|
-
raise
|
449
|
+
raise ::Vcard::InvalidEncodingError, "Time.gm(#{d.join(', ')}) failed with #{e.message}"
|
452
450
|
end
|
453
451
|
end
|
454
452
|
end
|
@@ -463,12 +461,12 @@ module Vpim
|
|
463
461
|
# works an InvalidEncodingError will be raised.
|
464
462
|
def to_date
|
465
463
|
begin
|
466
|
-
|
464
|
+
::Vcard.decode_date_time_list(value).collect do |d|
|
467
465
|
# We get [ year, month, day, hour, min, sec, usec, tz ]
|
468
466
|
Date.new(d[0], d[1], d[2])
|
469
467
|
end
|
470
|
-
rescue
|
471
|
-
|
468
|
+
rescue ::Vcard::InvalidEncodingError
|
469
|
+
::Vcard.decode_date_list(value).collect do |d|
|
472
470
|
# We get [ year, month, day ]
|
473
471
|
Date.new(*d)
|
474
472
|
end
|
@@ -479,11 +477,11 @@ module Vpim
|
|
479
477
|
# characters, this method will strip them, if present.
|
480
478
|
#
|
481
479
|
# In theory, #value could also do this, but it would need to know that
|
482
|
-
# the value is of type
|
480
|
+
# the value is of type "TEXT", and often for text values the "VALUE"
|
483
481
|
# parameter is not present, so knowledge of the expected type of the
|
484
482
|
# field is required from the decoder.
|
485
483
|
def to_text
|
486
|
-
|
484
|
+
::Vcard.decode_text(value)
|
487
485
|
end
|
488
486
|
|
489
487
|
# The undecoded value, see +value+.
|
@@ -516,10 +514,10 @@ module Vpim
|
|
516
514
|
# currently has. See Field.create() for a description of +pvalue+.
|
517
515
|
#
|
518
516
|
# Example:
|
519
|
-
# if field[
|
520
|
-
# field[
|
517
|
+
# if field["TYPE"]
|
518
|
+
# field["TYPE"] << "HOME"
|
521
519
|
# else
|
522
|
-
# field[
|
520
|
+
# field["TYPE"] = [ "HOME" ]
|
523
521
|
# end
|
524
522
|
#
|
525
523
|
# TODO - this could be an alias to #pvalue_set
|
@@ -597,7 +595,7 @@ module Vpim
|
|
597
595
|
begin
|
598
596
|
@group, @name, @params, @value = Field.decode0(line)
|
599
597
|
@line = line
|
600
|
-
rescue
|
598
|
+
rescue ::Vcard::InvalidEncodingError => e
|
601
599
|
raise ArgumentError, e.to_s
|
602
600
|
end
|
603
601
|
self
|