vpim 0.17 → 0.323
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/lib/vpim/dirinfo.rb +11 -5
- data/lib/vpim/field.rb +26 -21
- data/lib/vpim/icalendar.rb +21 -105
- data/lib/vpim/maker/vcard.rb +31 -28
- data/lib/vpim/property/base.rb +63 -0
- data/lib/vpim/property/common.rb +159 -0
- data/lib/vpim/property/location.rb +29 -0
- data/lib/vpim/property/priority.rb +35 -0
- data/lib/vpim/property/resources.rb +16 -0
- data/lib/vpim/rfc2425.rb +21 -4
- data/lib/vpim/rrule.rb +1 -1
- data/lib/vpim/vcard.rb +5 -5
- data/lib/vpim/version.rb +16 -0
- data/lib/vpim/vevent.rb +54 -204
- data/lib/vpim/vpim.rb +10 -15
- data/lib/vpim.rb +14 -0
- metadata +9 -19
- data/lib/vpim/agent/plist.rb +0 -86
- data/lib/vpim/date.rb~ +0 -198
- data/lib/vpim/dirinfo.rb~ +0 -242
- data/lib/vpim/duration.rb~ +0 -121
- data/lib/vpim/enumerator.rb~ +0 -29
- data/lib/vpim/field.rb~ +0 -594
- data/lib/vpim/icalendar.rb~ +0 -548
- data/lib/vpim/maker/vcard.rb~ +0 -382
- data/lib/vpim/rfc2425.rb~ +0 -246
- data/lib/vpim/rrule.rb~ +0 -482
- data/lib/vpim/time.rb~ +0 -42
- data/lib/vpim/vcard.rb~ +0 -232
- data/lib/vpim/vevent.rb~ +0 -381
- data/lib/vpim/vpim.rb~ +0 -128
data/lib/vpim/maker/vcard.rb~
DELETED
@@ -1,382 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
$Id: vcard.rb,v 1.7 2005/02/04 21:09:34 sam Exp $
|
3
|
-
|
4
|
-
Copyright (C) 2006 Sam Roberts
|
5
|
-
|
6
|
-
This library is free software; you can redistribute it and/or modify it
|
7
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
-
details.
|
9
|
-
=end
|
10
|
-
|
11
|
-
require 'vpim/vcard'
|
12
|
-
|
13
|
-
module Vpim
|
14
|
-
module Maker
|
15
|
-
# A helper class to assist in building a vCard.
|
16
|
-
#
|
17
|
-
# Examples:
|
18
|
-
# - link:ex_mkvcard.txt: an example of making a vCard
|
19
|
-
# - link:ex_cpvcard.txt: an example of copying and modifying a vCard
|
20
|
-
#
|
21
|
-
# Note - The Maker module is modelled after rss/maker, but if a Vcard was
|
22
|
-
# mutable these methods could be added to Vpim::Vcard. Hm.
|
23
|
-
class Vcard
|
24
|
-
# Make a vCard for +full_name+.
|
25
|
-
#
|
26
|
-
# Yields +card+, a Vpim::Maker::Vcard to which fields can be added, and returns a Vpim::Vcard.
|
27
|
-
#
|
28
|
-
# Note that calling #add_name is required, all other fields are optional.
|
29
|
-
def Vcard.make(full_name, &block) # :yields: +card+
|
30
|
-
new(full_name).make(&block)
|
31
|
-
end
|
32
|
-
|
33
|
-
def make # :nodoc:
|
34
|
-
yield self
|
35
|
-
unless @card['N']
|
36
|
-
raise Vpim::InvalidEncodingError, 'It is mandatory to have a name field, see #add_name.'
|
37
|
-
end
|
38
|
-
@card
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def initialize(full_name) # :nodoc:
|
44
|
-
@card = Vpim::Vcard::create
|
45
|
-
@card << Vpim::DirectoryInfo::Field.create('FN', full_name )
|
46
|
-
# pp @card
|
47
|
-
end
|
48
|
-
|
49
|
-
public
|
50
|
-
|
51
|
-
# Add an arbitrary Field, +field+.
|
52
|
-
def add_field(field)
|
53
|
-
fieldname = field.name.upcase
|
54
|
-
case
|
55
|
-
when [ 'BEGIN', 'END', 'VERSION' ].include?(fieldname)
|
56
|
-
raise Vpim::InvalidEncodingError, "Not allowed to manually add #{field.name} to a vCard."
|
57
|
-
|
58
|
-
when [ 'N', 'FN' ].include?(fieldname)
|
59
|
-
if @card.field(fieldname)
|
60
|
-
raise Vpim::InvalidEncodingError, "Not allowed to add more than one #{fieldname} to a vCard."
|
61
|
-
end
|
62
|
-
@card << field
|
63
|
-
|
64
|
-
else
|
65
|
-
@card << field
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Copy the fields from +card+ into self using #add_field. If a block is
|
70
|
-
# provided, each Field from +card+ is yielded. The block should return a
|
71
|
-
# Field to add, or nil. The Field doesn't have to be the one yielded,
|
72
|
-
# this allows modified fields to be copied in (use Field#copy), or fields
|
73
|
-
# to be filtered out (if the block yields nil).
|
74
|
-
#
|
75
|
-
# The vCard fields BEGIN, END, VERSION aren't copied, and N and FN aren't copied
|
76
|
-
# if the target already has them
|
77
|
-
def copy(card) # :yields: Field
|
78
|
-
card.each do |field|
|
79
|
-
fieldname = field.name.upcase
|
80
|
-
case
|
81
|
-
when [ 'BEGIN', 'END', 'VERSION' ].include?(fieldname)
|
82
|
-
# Never copy these
|
83
|
-
|
84
|
-
when [ 'N', 'FN' ].include?(fieldname) && @card.field(fieldname)
|
85
|
-
# Copy these only if they don't already exist.
|
86
|
-
|
87
|
-
else
|
88
|
-
if block_given?
|
89
|
-
field = yield field
|
90
|
-
end
|
91
|
-
|
92
|
-
if field
|
93
|
-
add_field(field)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Add a name field, N.
|
100
|
-
#
|
101
|
-
# Warning: This is the only mandatory field, besides the full name, which
|
102
|
-
# is added from Vcard.make's +full_name+.
|
103
|
-
#
|
104
|
-
# Attributes of N are:
|
105
|
-
# - family: family name
|
106
|
-
# - given: given name
|
107
|
-
# - additional: additional names
|
108
|
-
# - prefix: such as "Ms." or "Dr."
|
109
|
-
# - suffix: such as "BFA", or "Sensei"
|
110
|
-
#
|
111
|
-
# All attributes are optional.
|
112
|
-
#
|
113
|
-
# FIXME: is it possible to deduce given/family from the full_name?
|
114
|
-
#
|
115
|
-
# FIXME: Each attribute can currently only have a single String value.
|
116
|
-
#
|
117
|
-
# FIXME: Need to escape specials in the String.
|
118
|
-
def add_name # :yield: n
|
119
|
-
x = Struct.new(:family, :given, :additional, :prefix, :suffix).new
|
120
|
-
yield x
|
121
|
-
@card << Vpim::DirectoryInfo::Field.create(
|
122
|
-
'N',
|
123
|
-
x.map { |s| s ? s.to_str : '' }
|
124
|
-
)
|
125
|
-
self
|
126
|
-
end
|
127
|
-
|
128
|
-
# Add a address field, ADR.
|
129
|
-
#
|
130
|
-
# Attributes of ADR that describe the address are:
|
131
|
-
# - pobox: post office box
|
132
|
-
# - extended: seldom used, its not clear what it is for
|
133
|
-
# - street: street address, multiple components should be separated by a comma, ','
|
134
|
-
# - locality: usually the city
|
135
|
-
# - region: usually the province or state
|
136
|
-
# - postalcode: postal code
|
137
|
-
# - country: country name, no standard for country naming is specified
|
138
|
-
#
|
139
|
-
# Attributes that describe how the address is used, and customary values, are:
|
140
|
-
# - location: home, work - often used, can be set to other values
|
141
|
-
# - preferred: true - often used, set if this is the preferred address
|
142
|
-
# - delivery: postal, parcel, dom (domestic), intl (international) - rarely used
|
143
|
-
#
|
144
|
-
# All attributes are optional. #location and #home can be set to arrays of
|
145
|
-
# strings.
|
146
|
-
#
|
147
|
-
# TODO: Add #label to support LABEL.
|
148
|
-
#
|
149
|
-
# FIXME: Need to escape specials in the String.
|
150
|
-
def add_addr # :yield: adr
|
151
|
-
x = Struct.new(
|
152
|
-
:location, :preferred, :delivery,
|
153
|
-
:pobox, :extended, :street, :locality, :region, :postalcode, :country
|
154
|
-
).new
|
155
|
-
yield x
|
156
|
-
|
157
|
-
values = x.to_a[3, 7].map { |s| s ? s.to_str : '' }
|
158
|
-
|
159
|
-
# All these attributes go into the TYPE parameter.
|
160
|
-
params = [ x[:location], x[:delivery] ]
|
161
|
-
params << 'pref' if x[:preferred]
|
162
|
-
params = params.flatten.uniq.compact.map { |s| s.to_str }
|
163
|
-
|
164
|
-
paramshash = {}
|
165
|
-
|
166
|
-
paramshash['type'] = params if params.first
|
167
|
-
|
168
|
-
@card << Vpim::DirectoryInfo::Field.create( 'ADR', values, paramshash)
|
169
|
-
self
|
170
|
-
end
|
171
|
-
|
172
|
-
# Add a telephone number field, TEL.
|
173
|
-
#
|
174
|
-
# +number+ is supposed to be a "X.500 Telephone Number" according to RFC 2426, if you happen
|
175
|
-
# to be familiar with that. Otherwise, anything that looks like a phone number should be OK.
|
176
|
-
#
|
177
|
-
# Attributes of TEL are:
|
178
|
-
# - location: home, work, msg, cell, car, pager - often used, can be set to other values
|
179
|
-
# - preferred: true - often used, set if this is the preferred telephone number
|
180
|
-
# - capability: voice,fax,video,bbs,modem,isdn,pcs - fax is useful, the others are rarely used
|
181
|
-
#
|
182
|
-
# All attributes are optional, and so is the block.
|
183
|
-
def add_tel(number) # :yield: tel
|
184
|
-
params = {}
|
185
|
-
if block_given?
|
186
|
-
x = Struct.new( :location, :preferred, :capability ).new
|
187
|
-
|
188
|
-
yield x
|
189
|
-
|
190
|
-
x[:preferred] = 'pref' if x[:preferred]
|
191
|
-
|
192
|
-
types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
|
193
|
-
|
194
|
-
params['type'] = types if types.first
|
195
|
-
end
|
196
|
-
|
197
|
-
@card << Vpim::DirectoryInfo::Field.create( 'TEL', number, params)
|
198
|
-
self
|
199
|
-
end
|
200
|
-
|
201
|
-
# Add a email address field, EMAIL.
|
202
|
-
#
|
203
|
-
# Attributes of EMAIL are:
|
204
|
-
# - location: home, work - often used, can be set to other values
|
205
|
-
# - preferred: true - often used, set if this is the preferred email address
|
206
|
-
# - protocol: internet,x400 - internet is the default, set this for other kinds
|
207
|
-
#
|
208
|
-
# All attributes are optional, and so is the block.
|
209
|
-
def add_email(email) # :yield: email
|
210
|
-
params = {}
|
211
|
-
if block_given?
|
212
|
-
x = Struct.new( :location, :preferred, :protocol ).new
|
213
|
-
|
214
|
-
yield x
|
215
|
-
|
216
|
-
x[:preferred] = 'pref' if x[:preferred]
|
217
|
-
|
218
|
-
types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
|
219
|
-
|
220
|
-
params['type'] = types if types.first
|
221
|
-
end
|
222
|
-
|
223
|
-
@card << Vpim::DirectoryInfo::Field.create( 'EMAIL', email, params)
|
224
|
-
self
|
225
|
-
end
|
226
|
-
|
227
|
-
# Add a nickname field, NICKNAME.
|
228
|
-
def nickname=(nickname)
|
229
|
-
@card << Vpim::DirectoryInfo::Field.create( 'NICKNAME', nickname );
|
230
|
-
end
|
231
|
-
|
232
|
-
# Add a birthday field, BDAY.
|
233
|
-
#
|
234
|
-
# +birthday+ must be a time or date object.
|
235
|
-
#
|
236
|
-
# Warning: It may confuse both humans and software if you add multiple
|
237
|
-
# birthdays.
|
238
|
-
def birthday=(birthday)
|
239
|
-
if !birthday.respond_to? :month
|
240
|
-
raise Vpim::InvalidEncodingError, 'birthday doesn\'t have #month, so it is not a date or time object.'
|
241
|
-
end
|
242
|
-
@card << Vpim::DirectoryInfo::Field.create( 'BDAY', birthday );
|
243
|
-
end
|
244
|
-
=begin
|
245
|
-
TODO - need text=() implemented in Field
|
246
|
-
|
247
|
-
# Add a note field, NOTE. It can contain newlines, they will be escaped.
|
248
|
-
def note=(note)
|
249
|
-
@card << Vpim::DirectoryInfo::Field.create( 'NOTE', note );
|
250
|
-
end
|
251
|
-
=end
|
252
|
-
|
253
|
-
# Add an instant-messaging/point of presence address field, IMPP. The address
|
254
|
-
# is a URL, with the syntax depending on the protocol.
|
255
|
-
#
|
256
|
-
# Attributes of IMPP are:
|
257
|
-
# - preferred: true - set if this is the preferred address
|
258
|
-
# - location: home, work, mobile - location of address
|
259
|
-
# - purpose: personal,business - purpose of communications
|
260
|
-
#
|
261
|
-
# All attributes are optional, and so is the block.
|
262
|
-
#
|
263
|
-
# The URL syntaxes for the messaging schemes is fairly complicated, so I
|
264
|
-
# don't try and build the URLs here, maybe in the future. This forces
|
265
|
-
# the user to know the URL for their own address, hopefully not too much
|
266
|
-
# of a burden.
|
267
|
-
#
|
268
|
-
# IMPP is defined in draft-jennings-impp-vcard-04.txt. It refers to the
|
269
|
-
# URI scheme of a number of messaging protocols, but doesn't give
|
270
|
-
# references to all of them:
|
271
|
-
# - "xmpp" indicates to use XMPP, draft-saintandre-xmpp-uri-06.txt
|
272
|
-
# - "irc" or "ircs" indicates to use IRC, draft-butcher-irc-url-04.txt
|
273
|
-
# - "sip" indicates to use SIP/SIMPLE, RFC 3261
|
274
|
-
# - "im" or "pres" indicates to use a CPIM or CPP gateway, RFC 3860 and RFC 3859
|
275
|
-
# - "ymsgr" indicates to use yahoo
|
276
|
-
# - "msn" might indicate to use Microsoft messenger
|
277
|
-
# - "aim" indicates to use AOL
|
278
|
-
#
|
279
|
-
def add_impp(url) # :yield: impp
|
280
|
-
params = {}
|
281
|
-
|
282
|
-
if block_given?
|
283
|
-
x = Struct.new( :location, :preferred, :purpose ).new
|
284
|
-
|
285
|
-
yield x
|
286
|
-
|
287
|
-
x[:preferred] = 'pref' if x[:preferred]
|
288
|
-
|
289
|
-
types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
|
290
|
-
|
291
|
-
params['type'] = types if types.first
|
292
|
-
end
|
293
|
-
|
294
|
-
@card << Vpim::DirectoryInfo::Field.create( 'IMPP', url, params)
|
295
|
-
self
|
296
|
-
end
|
297
|
-
|
298
|
-
# Add an Apple style AIM account name, +xaim+ is an AIM screen name.
|
299
|
-
#
|
300
|
-
# I don't know if this is conventional, or supported by anything other
|
301
|
-
# than AddressBook.app, but an example is:
|
302
|
-
# X-AIM;type=HOME;type=pref:exampleaccount
|
303
|
-
#
|
304
|
-
# Attributes of X-AIM are:
|
305
|
-
# - preferred: true - set if this is the preferred address
|
306
|
-
# - location: home, work, mobile - location of address
|
307
|
-
#
|
308
|
-
# All attributes are optional, and so is the block.
|
309
|
-
def add_x_aim(xaim) # :yield: xaim
|
310
|
-
params = {}
|
311
|
-
|
312
|
-
if block_given?
|
313
|
-
x = Struct.new( :location, :preferred ).new
|
314
|
-
|
315
|
-
yield x
|
316
|
-
|
317
|
-
x[:preferred] = 'pref' if x[:preferred]
|
318
|
-
|
319
|
-
types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
|
320
|
-
|
321
|
-
params['type'] = types if types.first
|
322
|
-
end
|
323
|
-
|
324
|
-
@card << Vpim::DirectoryInfo::Field.create( 'X-AIM', xaim, params)
|
325
|
-
self
|
326
|
-
end
|
327
|
-
|
328
|
-
|
329
|
-
# Add a photo field, PHOTO.
|
330
|
-
#
|
331
|
-
# Attributes of PHOTO are:
|
332
|
-
# - image: set to image data to inclue inline
|
333
|
-
# - link: set to the URL of the image data
|
334
|
-
# - type: string identifying the image type, supposed to be an "IANA registered image format",
|
335
|
-
# or a non-registered image format (usually these start with an x-)
|
336
|
-
#
|
337
|
-
# An error will be raised if neither image or link is set, or if both image
|
338
|
-
# and link is set.
|
339
|
-
#
|
340
|
-
# Setting type is optional for a link image, because either the URL, the
|
341
|
-
# image file extension, or a HTTP Content-Type may specify the type. If
|
342
|
-
# it's not a link, setting type is mandatory, though it can be set to an
|
343
|
-
# empty string, <code>''</code>, if the type is unknown.
|
344
|
-
#
|
345
|
-
# TODO - I'm not sure about this API. I'm thinking maybe it should be
|
346
|
-
# #add_photo(image, type), and that I should detect when the image is a
|
347
|
-
# URL, and make type mandatory if it wasn't a URL.
|
348
|
-
def add_photo # :yield: photo
|
349
|
-
x = Struct.new(:image, :link, :type).new
|
350
|
-
yield x
|
351
|
-
if x[:image] && x[:link]
|
352
|
-
raise Vpim::InvalidEncodingError, 'Image is not allowed to be both inline and a link.'
|
353
|
-
end
|
354
|
-
|
355
|
-
value = x[:image] || x[:link]
|
356
|
-
|
357
|
-
if !value
|
358
|
-
raise Vpim::InvalidEncodingError, 'A image link or inline data must be provided.'
|
359
|
-
end
|
360
|
-
|
361
|
-
params = {}
|
362
|
-
|
363
|
-
# Don't set type to the empty string.
|
364
|
-
params['type'] = x[:type] if( x[:type] && x[:type].length > 0 )
|
365
|
-
|
366
|
-
if x[:link]
|
367
|
-
params['value'] = 'uri'
|
368
|
-
else # it's inline, base-64 encode it
|
369
|
-
params['encoding'] = :b64
|
370
|
-
if !x[:type]
|
371
|
-
raise Vpim::InvalidEncodingError, 'Inline image data must have it\'s type set.'
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
@card << Vpim::DirectoryInfo::Field.create( 'PHOTO', value, params )
|
376
|
-
self
|
377
|
-
end
|
378
|
-
|
379
|
-
end
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
data/lib/vpim/rfc2425.rb~
DELETED
@@ -1,246 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
$Id: rfc2425.rb,v 1.10 2005/01/01 17:17:01 sam Exp $
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
5
|
-
|
6
|
-
This library is free software; you can redistribute it and/or modify it
|
7
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
-
details.
|
9
|
-
=end
|
10
|
-
|
11
|
-
require 'vpim/vpim'
|
12
|
-
|
13
|
-
module Vpim
|
14
|
-
# Contains regular expression strings for the EBNF of RFC 2425.
|
15
|
-
module Bnf #:nodoc:
|
16
|
-
|
17
|
-
# 1*(ALPHA / DIGIT / "-")
|
18
|
-
# Note: I think I can add A-Z here, and get rid of the "i" matches elsewhere.
|
19
|
-
# Note: added '_' to allowed because its produced by Notes - X-LOTUS-CHILD_UID
|
20
|
-
NAME = '[-a-z0-9_]+'
|
21
|
-
|
22
|
-
# <"> <Any character except CTLs, DQUOTE> <">
|
23
|
-
QSTR = '"([^"]*)"'
|
24
|
-
|
25
|
-
# *<Any character except CTLs, DQUOTE, ";", ":", ",">
|
26
|
-
PTEXT = '([^";:,]+)'
|
27
|
-
|
28
|
-
# param-value = ptext / quoted-string
|
29
|
-
PVALUE = "(?:#{QSTR}|#{PTEXT})"
|
30
|
-
|
31
|
-
# param = name "=" param-value *("," param-value)
|
32
|
-
# Note: v2.1 allows a type or encoding param-value to appear without the type=
|
33
|
-
# or the encoding=. This is hideous, but we try and support it, if there
|
34
|
-
# is no "=", then $2 will be "", and we will treat it as a v2.1 param.
|
35
|
-
PARAM = ";(#{NAME})(=?)((?:#{PVALUE})?(?:,#{PVALUE})*)"
|
36
|
-
|
37
|
-
# V3.0: contentline = [group "."] name *(";" param) ":" value
|
38
|
-
# V2.1: contentline = *( group "." ) name *(";" param) ":" value
|
39
|
-
#
|
40
|
-
# We accept the V2.1 syntax for backwards compatibility.
|
41
|
-
#LINE = "((?:#{NAME}\\.)*)?(#{NAME})([^:]*)\:(.*)"
|
42
|
-
LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):(.*)$"
|
43
|
-
|
44
|
-
# date = date-fullyear ["-"] date-month ["-"] date-mday
|
45
|
-
# date-fullyear = 4 DIGIT
|
46
|
-
# date-month = 2 DIGIT
|
47
|
-
# date-mday = 2 DIGIT
|
48
|
-
DATE = '(\d\d\d\d)-?(\d\d)-?(\d\d)'
|
49
|
-
|
50
|
-
# time = time-hour [":"] time-minute [":"] time-second [time-secfrac] [time-zone]
|
51
|
-
# time-hour = 2 DIGIT
|
52
|
-
# time-minute = 2 DIGIT
|
53
|
-
# time-second = 2 DIGIT
|
54
|
-
# time-secfrac = "," 1*DIGIT
|
55
|
-
# time-zone = "Z" / time-numzone
|
56
|
-
# time-numzome = sign time-hour [":"] time-minute
|
57
|
-
TIME = '(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
module Vpim
|
62
|
-
# Split on \r\n or \n to get the lines, unfold continued lines (they
|
63
|
-
# start with ' ' or \t), and return the array of unfolded lines.
|
64
|
-
#
|
65
|
-
# This also supports the (invalid) encoding convention of allowing empty
|
66
|
-
# lines to be inserted for readability - it does this by dropping zero-length
|
67
|
-
# lines.
|
68
|
-
def Vpim.unfold(card) #:nodoc:
|
69
|
-
unfolded = []
|
70
|
-
|
71
|
-
card.each do |line|
|
72
|
-
line.chomp!
|
73
|
-
# If it's a continuation line, add it to the last.
|
74
|
-
# If it's an empty line, drop it from the input.
|
75
|
-
if( line =~ /^[ \t]/ )
|
76
|
-
unfolded[-1] << line[1, line.size-1]
|
77
|
-
elsif( line =~ /^$/ )
|
78
|
-
else
|
79
|
-
unfolded << line
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
unfolded
|
84
|
-
end
|
85
|
-
|
86
|
-
# Convert a +sep+-seperated list of values into an array of values.
|
87
|
-
def Vpim.decode_list(value, sep = ',') # :nodoc:
|
88
|
-
list = []
|
89
|
-
|
90
|
-
value.each(sep) {
|
91
|
-
|item|
|
92
|
-
list << yield(item) unless item =~ %r{^\s*#{sep}?$}
|
93
|
-
}
|
94
|
-
list
|
95
|
-
end
|
96
|
-
|
97
|
-
# Convert a RFC 2425 date into an array of [year, month, day].
|
98
|
-
def Vpim.decode_date(v) # :nodoc:
|
99
|
-
unless v =~ %r{\s*#{Bnf::DATE}\s*}
|
100
|
-
raise Vpim::InvalidEncodingError, "date not valid (#{v})"
|
101
|
-
end
|
102
|
-
[$1.to_i, $2.to_i, $3.to_i]
|
103
|
-
end
|
104
|
-
|
105
|
-
# Note in the following the RFC2425 allows yyyy-mm-ddThh:mm:ss, but RFC2445
|
106
|
-
# does not. I choose to encode to the subset that is valid for both.
|
107
|
-
|
108
|
-
# Encode a Date object as "yyyymmdd".
|
109
|
-
def Vpim.encode_date(d) # :nodoc:
|
110
|
-
"%0.4d%0.2d%0.2d" % [ d.year, d.mon, d.day ]
|
111
|
-
end
|
112
|
-
|
113
|
-
# Encode a Date object as "yyyymmdd".
|
114
|
-
def Vpim.encode_time(d) # :nodoc:
|
115
|
-
"%0.4d%0.2d%0.2d" % [ d.year, d.mon, d.day ]
|
116
|
-
end
|
117
|
-
|
118
|
-
# Encode a Time or DateTime object as "yyyymmddThhmmss"
|
119
|
-
def Vpim.encode_date_time(d) # :nodoc:
|
120
|
-
"%0.4d%0.2d%0.2dT%0.2d%0.2d%0.2d" % [ d.year, d.mon, d.day, d.hour, d.min, d.sec ]
|
121
|
-
end
|
122
|
-
|
123
|
-
# Convert a RFC 2425 time into an array of [hour,min,sec,secfrac,timezone]
|
124
|
-
def Vpim.decode_time(v) # :nodoc:
|
125
|
-
unless match = %r{\s*#{Bnf::TIME}\s*}.match(v)
|
126
|
-
raise Vpim::InvalidEncodingError, "time not valid (#{v})"
|
127
|
-
end
|
128
|
-
hour, min, sec, secfrac, tz = match.to_a[1..5]
|
129
|
-
|
130
|
-
[hour.to_i, min.to_i, sec.to_i, secfrac ? secfrac.to_f : 0, tz]
|
131
|
-
end
|
132
|
-
|
133
|
-
# Convert a RFC 2425 date-time into an array of [hour,min,sec,secfrac,timezone]
|
134
|
-
def Vpim.decode_date_time(v) # :nodoc:
|
135
|
-
unless match = %r{\s*#{Bnf::DATE}T#{Bnf::TIME}\s*}.match(v)
|
136
|
-
raise Vpim::InvalidEncodingError, "date-time '#{v}' not valid"
|
137
|
-
end
|
138
|
-
year, month, day, hour, min, sec, secfrac, tz = match.to_a[1..8]
|
139
|
-
|
140
|
-
[
|
141
|
-
# date
|
142
|
-
year.to_i, month.to_i, day.to_i,
|
143
|
-
# time
|
144
|
-
hour.to_i, min.to_i, sec.to_i, secfrac ? secfrac.to_f : 0, tz
|
145
|
-
]
|
146
|
-
end
|
147
|
-
|
148
|
-
# Vpim.decode_boolean
|
149
|
-
#
|
150
|
-
# float
|
151
|
-
#
|
152
|
-
# float_list
|
153
|
-
#
|
154
|
-
# integer
|
155
|
-
#
|
156
|
-
# integer_list
|
157
|
-
#
|
158
|
-
# text_list
|
159
|
-
|
160
|
-
# Convert a RFC 2425 date-list into an array of dates.
|
161
|
-
def Vpim.decode_date_list(v) # :nodoc:
|
162
|
-
dates = Vpim.decode_list(v) { |date| Vpim.decode_date(date) }
|
163
|
-
end
|
164
|
-
|
165
|
-
# Convert a RFC 2425 time-list into an array of times.
|
166
|
-
def Vpim.decode_time_list(v) # :nodoc:
|
167
|
-
times = Vpim.decode_list(v) { |time| Vpim.decode_time(time) }
|
168
|
-
end
|
169
|
-
|
170
|
-
# Convert a RFC 2425 date-time-list into an array of date-times.
|
171
|
-
def Vpim.decode_date_time_list(v) # :nodoc:
|
172
|
-
datetimes = Vpim.decode_list(v) { |datetime| Vpim.decode_date_time(datetime) }
|
173
|
-
end
|
174
|
-
|
175
|
-
# Convert RFC 2425 text into a String.
|
176
|
-
# \\ -> \
|
177
|
-
# \n -> NL
|
178
|
-
# \N -> NL
|
179
|
-
# \, -> ,
|
180
|
-
def Vpim.decode_text(v) # :nodoc:
|
181
|
-
v.gsub(/\\[nN]/, "\n").gsub(/\\,/, ",").gsub(/\\\\/) { |m| "\\" }
|
182
|
-
end
|
183
|
-
|
184
|
-
|
185
|
-
# Unfold the lines in +card+, then return an array of one Field object per
|
186
|
-
# line.
|
187
|
-
def Vpim.decode(card) #:nodoc:
|
188
|
-
content = Vpim.unfold(card).collect { |line| DirectoryInfo::Field.decode(line) }
|
189
|
-
end
|
190
|
-
|
191
|
-
|
192
|
-
# Expand an array of fields into its syntactic entities. Each entity is a sequence
|
193
|
-
# of fields where the sequences is delimited by a BEGIN/END field. Since
|
194
|
-
# BEGIN/END delimited entities can be nested, we build a tree. Each entry in
|
195
|
-
# the array is either a Field or an array of entries (where each entry is
|
196
|
-
# either a Field, or an array of entries...).
|
197
|
-
def Vpim.expand(src) #:nodoc:
|
198
|
-
# output array to expand the src to
|
199
|
-
dst = []
|
200
|
-
# stack used to track our nesting level, as we see begin/end we start a
|
201
|
-
# new/finish the current entity, and push/pop that entity from the stack
|
202
|
-
current = [ dst ]
|
203
|
-
|
204
|
-
for f in src
|
205
|
-
if f.name? 'begin'
|
206
|
-
e = [ f ]
|
207
|
-
|
208
|
-
current.last.push(e)
|
209
|
-
current.push(e)
|
210
|
-
|
211
|
-
elsif f.name? 'end'
|
212
|
-
current.last.push(f)
|
213
|
-
|
214
|
-
unless current.last.first.value? current.last.last.value
|
215
|
-
raise "BEGIN/END mismatch (#{current.last.first.value} != #{current.last.last.value})"
|
216
|
-
end
|
217
|
-
|
218
|
-
current.pop
|
219
|
-
|
220
|
-
else
|
221
|
-
current.last.push(f)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
dst
|
226
|
-
end
|
227
|
-
|
228
|
-
# Split an array into an array of all the fields at the outer level, and
|
229
|
-
# an array of all the inner arrays of fields. Return the array [outer,
|
230
|
-
# inner].
|
231
|
-
def Vpim.outer_inner(fields) #:nodoc:
|
232
|
-
# seperate into the outer-level fields, and the arrays of component
|
233
|
-
# fields
|
234
|
-
outer = []
|
235
|
-
inner = []
|
236
|
-
fields.each do |line|
|
237
|
-
case line
|
238
|
-
when Array; inner << line
|
239
|
-
else; outer << line
|
240
|
-
end
|
241
|
-
end
|
242
|
-
return outer, inner
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
|