vcard 0.1.1 → 0.2.0
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.
- 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
|