vpim2 0.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +504 -0
  3. data/COPYING +58 -0
  4. data/README +182 -0
  5. data/lib/atom.rb +728 -0
  6. data/lib/plist.rb +22 -0
  7. data/lib/vpim.rb +13 -0
  8. data/lib/vpim/address.rb +219 -0
  9. data/lib/vpim/attachment.rb +102 -0
  10. data/lib/vpim/date.rb +222 -0
  11. data/lib/vpim/dirinfo.rb +277 -0
  12. data/lib/vpim/duration.rb +119 -0
  13. data/lib/vpim/enumerator.rb +32 -0
  14. data/lib/vpim/field.rb +614 -0
  15. data/lib/vpim/icalendar.rb +381 -0
  16. data/lib/vpim/maker/vcard.rb +16 -0
  17. data/lib/vpim/property/base.rb +193 -0
  18. data/lib/vpim/property/common.rb +315 -0
  19. data/lib/vpim/property/location.rb +38 -0
  20. data/lib/vpim/property/priority.rb +43 -0
  21. data/lib/vpim/property/recurrence.rb +69 -0
  22. data/lib/vpim/property/resources.rb +24 -0
  23. data/lib/vpim/repo.rb +181 -0
  24. data/lib/vpim/rfc2425.rb +367 -0
  25. data/lib/vpim/rrule.rb +591 -0
  26. data/lib/vpim/vcard.rb +1430 -0
  27. data/lib/vpim/version.rb +18 -0
  28. data/lib/vpim/vevent.rb +187 -0
  29. data/lib/vpim/view.rb +90 -0
  30. data/lib/vpim/vjournal.rb +58 -0
  31. data/lib/vpim/vpim.rb +65 -0
  32. data/lib/vpim/vtodo.rb +103 -0
  33. data/samples/README.mutt +93 -0
  34. data/samples/ab-query.rb +57 -0
  35. data/samples/cmd-itip.rb +156 -0
  36. data/samples/ex_cpvcard.rb +55 -0
  37. data/samples/ex_get_vcard_photo.rb +22 -0
  38. data/samples/ex_mkv21vcard.rb +34 -0
  39. data/samples/ex_mkvcard.rb +64 -0
  40. data/samples/ex_mkyourown.rb +29 -0
  41. data/samples/ics-dump.rb +210 -0
  42. data/samples/ics-to-rss.rb +84 -0
  43. data/samples/mutt-aliases-to-vcf.rb +45 -0
  44. data/samples/osx-wrappers.rb +86 -0
  45. data/samples/reminder.rb +203 -0
  46. data/samples/rrule.rb +71 -0
  47. data/samples/tabbed-file-to-vcf.rb +390 -0
  48. data/samples/vcf-dump.rb +86 -0
  49. data/samples/vcf-lines.rb +61 -0
  50. data/samples/vcf-to-ics.rb +22 -0
  51. data/samples/vcf-to-mutt.rb +121 -0
  52. data/test/test_all.rb +17 -0
  53. data/test/test_date.rb +120 -0
  54. data/test/test_dur.rb +41 -0
  55. data/test/test_field.rb +156 -0
  56. data/test/test_ical.rb +415 -0
  57. data/test/test_repo.rb +158 -0
  58. data/test/test_rrule.rb +1030 -0
  59. data/test/test_vcard.rb +973 -0
  60. data/test/test_view.rb +79 -0
  61. metadata +117 -0
@@ -0,0 +1,973 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/vcard'
4
+ require 'test/unit'
5
+ require 'date'
6
+
7
+ require 'pp'
8
+
9
+ include Vpim
10
+
11
+ # Test equivalence where whitespace is compressed.
12
+ def assert_equal_nospace(expected, got)
13
+ expected = expected.gsub(/\s+/,'')
14
+ got = expected.gsub(/\s+/,'')
15
+ assert_equal(expected, got)
16
+ end
17
+
18
+
19
+ # Test cases: multiple occurrences of type
20
+ =begin
21
+ begin:VCARD
22
+ version:2.1
23
+ v;x1=a;x2=,a;x3=a,;x4=a,,a;x5=,a,:
24
+ source:ldap://cn=bjorn%20Jensen, o=university%20of%20Michigan, c=US
25
+ fn:Bj=F8rn
26
+ Jensen
27
+ other.name:Jensen;Bj=F8rn
28
+ some.other.value:1.2.3
29
+ some.other.value:some.other
30
+ some.other.value:some.other.value
31
+ v;p-1=;p-2=,,;p-3=a;p-4=a b,"v;p-1=;p-2=,,;p-3=a;p-4=a":v-value
32
+ email;type=internet:
33
+ bjorn@umich.edu
34
+ tel;type=work,voice,msg:+1 313 747-4454
35
+ tel:+...
36
+ key;type=x509;encoding=B:dGhpcyBjb3VsZCBiZSAKbXkgY2VydGlmaWNhdGUK
37
+ end:vcard
38
+ =end
39
+
40
+ class TestVcard < Test::Unit::TestCase
41
+
42
+ # RFC2425 - 8.1. Example 1
43
+ # Note that this is NOT a valid vCard, it lacks BEGIN/END.
44
+ EX1 =<<'EOF'
45
+ cn:
46
+ cn:Babs Jensen
47
+ cn:Barbara J Jensen
48
+ sn:Jensen
49
+ email:babs@umich.edu
50
+ phone:+1 313 747-4454
51
+ x-id:1234567890
52
+ EOF
53
+ def test_ex1
54
+ card = nil
55
+ ex1 = EX1
56
+ assert_nothing_thrown { card = Vpim::DirectoryInfo.decode(ex1) }
57
+ assert_equal_nospace(EX1, card.to_s)
58
+
59
+ assert_equal("Babs Jensen", card["cn"])
60
+ assert_equal("Jensen", card["sn"])
61
+
62
+ assert_equal("babs@umich.edu", card[ "email" ])
63
+
64
+ assert_equal("+1 313 747-4454", card[ "PhOnE" ])
65
+ assert_equal("1234567890", card[ "x-id" ])
66
+ assert_equal([], card.groups)
67
+ end
68
+
69
+ # RFC2425 - 8.2. Example 2
70
+ EX2 = <<-END
71
+ begin:VCARD
72
+ source:ldap://cn=bjorn%20Jensen, o=university%20of%20Michigan, c=US
73
+ name:Bjorn Jensen
74
+ fn:Bj=F8rn Jensen
75
+ n:Jensen;Bj=F8rn
76
+ email;type=internet:bjorn@umich.edu
77
+ tel;type=work,voice,msg:+1 313 747-4454
78
+ key;type=x509;encoding=B:dGhpcyBjb3VsZCBiZSAKbXkgY2VydGlmaWNhdGUK
79
+ end:VCARD
80
+ END
81
+
82
+ def test_ex2
83
+ card = nil
84
+ ex2 = EX2
85
+ assert_nothing_thrown { card = Vpim::Vcard.decode(ex2).first }
86
+ assert_equal(EX2, card.encode(0))
87
+ assert_raises(InvalidEncodingError) { card.version }
88
+
89
+ assert_equal("Bj=F8rn Jensen", card.name.fullname)
90
+ assert_equal("Jensen", card.name.family)
91
+ assert_equal("Bj=F8rn", card.name.given)
92
+ assert_equal("", card.name.prefix)
93
+
94
+ assert_equal("Bj=F8rn Jensen", card[ "fn" ])
95
+ assert_equal("+1 313 747-4454", card[ "tEL" ])
96
+
97
+ assert_equal(nil, card[ "not-a-field" ])
98
+ assert_equal([], card.groups)
99
+
100
+ assert_equal(nil, card.enum_by_name("n").entries[0].param("encoding"))
101
+
102
+ assert_equal(["internet"], card.enum_by_name("Email").entries.first.param("Type"))
103
+ assert_equal(nil, card.enum_by_name("Email").entries[0].param("foo"))
104
+
105
+ assert_equal(["B"], card.enum_by_name("kEy").to_a.first.param("encoding"))
106
+ assert_equal("B", card.enum_by_name("kEy").entries[0].encoding)
107
+
108
+ assert_equal(["work", "voice", "msg"], card.enum_by_name("tel").entries[0].param("Type"))
109
+
110
+ assert_equal([card.fields[6]], card.enum_by_name("tel").entries)
111
+
112
+ assert_equal([card.fields[6]], card.enum_by_name("tel").to_a)
113
+
114
+ assert_equal(nil, card.enum_by_name("tel").entries.first.encoding)
115
+
116
+ assert_equal("B", card.enum_by_name("key").entries.first.encoding)
117
+
118
+ assert_equal("dGhpcyBjb3VsZCBiZSAKbXkgY2VydGlmaWNhdGUK", card.enum_by_name("key").entries.first.value_raw)
119
+
120
+ assert_equal("this could be \nmy certificate\n", card.enum_by_name("key").entries.first.value)
121
+
122
+ card.lines
123
+ end
124
+
125
+ =begin
126
+ EX3 = <<-END
127
+ begin:vcard
128
+ source:ldap://cn=Meister%20Berger,o=Universitaet%20Goerlitz,c=DE
129
+ name:Meister Berger
130
+ fn:Meister Berger
131
+ n:Berger;Meister
132
+ bday;value=date:1963-09-21
133
+ o:Universit=E6t G=F6rlitz
134
+ title:Mayor
135
+ title;language=de;value=text:Burgermeister
136
+ note:The Mayor of the great city of
137
+ Goerlitz in the great country of Germany.
138
+ email;internet:mb@goerlitz.de
139
+ home.tel;type=fax,voice,msg:+49 3581 123456
140
+ home.label:Hufenshlagel 1234\n
141
+ 02828 Goerlitz\n
142
+ Deutschland
143
+ key;type=X509;encoding=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQ
144
+ AwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zI
145
+ ENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQD
146
+ ExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc
147
+ 1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW
148
+ 9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBF
149
+ hJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqG
150
+ SIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXc
151
+ oX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+E
152
+ IBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9
153
+ w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+M
154
+ SlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8V
155
+ UMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==
156
+ end:vcard
157
+ END
158
+ assert_equal(
159
+ ["other", "some.other"],
160
+ card.groups_all
161
+ )
162
+ #a = []
163
+ #card.enum_by_group("some.other").each { |field| a << field }
164
+ #assert_equal(card.fields.indexes(6, 7, 8), a)
165
+ #assert_equal(card.fields.indexes(6, 7, 8), card.fields_by_group("some.other"))
166
+ =end
167
+
168
+ # This is my vCard exported from OS X's AddressBook.app.
169
+ EX_APPLE1 =<<'EOF'
170
+ BEGIN:VCARD
171
+ VERSION:3.0
172
+ N:Roberts;Sam;;;
173
+ FN:Roberts Sam
174
+ EMAIL;type=HOME;type=pref:sroberts@uniserve.com
175
+ TEL;type=WORK;type=pref:905-501-3781
176
+ TEL;type=FAX:905-907-4230
177
+ TEL;type=HOME:416 535 5341
178
+ ADR;type=HOME;type=pref:;;376 Westmoreland Ave.;Toronto;ON;M6H 3
179
+ A6;Canada
180
+ NOTE:CATEGORIES: Amis/Famille
181
+ BDAY;value=date:1970-07-14
182
+ END:VCARD
183
+ EOF
184
+ def test_ex_apple1
185
+ card = nil
186
+ assert_nothing_thrown { card = Vpim::Vcard.decode(EX_APPLE1).first }
187
+
188
+ assert_equal("Roberts Sam", card.name.fullname)
189
+ assert_equal("Roberts", card.name.family)
190
+ assert_equal("Sam", card.name.given)
191
+ assert_equal("", card.name.prefix)
192
+ assert_equal("", card.name.suffix)
193
+
194
+ assert_equal(EX_APPLE1, card.to_s(64))
195
+
196
+ check_ex_apple1(card)
197
+ end
198
+
199
+ NICKNAME0=<<'EOF'
200
+ begin:vcard
201
+ end:vcard
202
+ EOF
203
+ NICKNAME1=<<'EOF'
204
+ begin:vcard
205
+ nickname:
206
+ end:vcard
207
+ EOF
208
+ NICKNAME2=<<'EOF'
209
+ begin:vcard
210
+ nickname:
211
+ end:vcard
212
+ EOF
213
+ NICKNAME3=<<'EOF'
214
+ begin:vcard
215
+ nickname: Big Joey
216
+ end:vcard
217
+ EOF
218
+ NICKNAME4=<<'EOF'
219
+ begin:vcard
220
+ nickname:
221
+ nickname: Big Joey
222
+ end:vcard
223
+ EOF
224
+ NICKNAME5=<<'EOF'
225
+ begin:vcard
226
+ nickname:
227
+ nickname: Big Joey
228
+ nickname:Bob
229
+ end:vcard
230
+ EOF
231
+ def test_nickname
232
+ assert_equal(nil, Vpim::Vcard.decode(NICKNAME0).first.nickname)
233
+ assert_equal(nil, Vpim::Vcard.decode(NICKNAME1).first.nickname)
234
+ assert_equal(nil, Vpim::Vcard.decode(NICKNAME2).first.nickname)
235
+ assert_equal('Big Joey', Vpim::Vcard.decode(NICKNAME3).first.nickname)
236
+ assert_equal('Big Joey', Vpim::Vcard.decode(NICKNAME4).first['nickname'])
237
+ assert_equal(['Big Joey', 'Bob'], Vpim::Vcard.decode(NICKNAME5).first.nicknames)
238
+ end
239
+
240
+
241
+ def check_ex_apple1(card)
242
+ assert_equal("3.0", card[ "version" ])
243
+ assert_equal(30, card.version)
244
+
245
+ assert_equal("sroberts@uniserve.com", card[ "email" ])
246
+ assert_equal(["HOME", "pref"], card.enum_by_name("email").entries.first.param("type"))
247
+ assert_equal(nil, card.enum_by_name("email").entries.first.group)
248
+
249
+ assert_equal(["WORK","pref"], card.enum_by_name("tel").entries[0].param("type"))
250
+ assert_equal(["FAX"], card.enum_by_name("tel").entries[1].param("type"))
251
+ assert_equal(["HOME"], card.enum_by_name("tel").entries[2].param("type"))
252
+
253
+ assert_equal(nil, card.enum_by_name("bday").entries[0].param("type"))
254
+ assert_equal(["date"], card.enum_by_name("bday").entries[0].param("value"))
255
+
256
+ assert_equal( 1970, card.enum_by_name("bday").entries[0].to_time[0].year)
257
+ assert_equal( 7, card.enum_by_name("bday").entries[0].to_time[0].month)
258
+ assert_equal( 14, card.enum_by_name("bday").entries[0].to_time[0].day)
259
+
260
+ assert_equal("CATEGORIES: Amis/Famille", card[ "note" ])
261
+ end
262
+
263
+ # Test data for Vpim.expand
264
+ EX_EXPAND =<<'EOF'
265
+ BEGIN:a
266
+ a1:
267
+ BEGIN:b
268
+ BEGIN:c
269
+ c1:
270
+ c2:
271
+ END:c
272
+ V1:
273
+ V2:
274
+ END:b
275
+ a2:
276
+ END:a
277
+ EOF
278
+ def test_expand
279
+ src = Vpim.decode(EX_EXPAND)
280
+ dst = Vpim.expand(src)
281
+
282
+ assert_equal('a', dst[0][0].value)
283
+ assert_equal('A1', dst[0][1].name)
284
+ assert_equal('b', dst[0][2][0].value)
285
+ assert_equal('c', dst[0][2][1][0].value)
286
+ assert_equal('C1', dst[0][2][1][1].name)
287
+ assert_equal('C2', dst[0][2][1][2].name)
288
+ assert_equal('c', dst[0][2][1][3].value)
289
+
290
+ cards = Vpim::Vcard.decode(EX_APPLE1)
291
+
292
+ assert_equal(1, cards.length)
293
+
294
+ check_ex_apple1(cards[0])
295
+ end
296
+
297
+ # An iCalendar for Vpim.expand
298
+ EX_ICAL_1 =<<'EOF'
299
+ BEGIN:VCALENDAR
300
+ CALSCALE:GREGORIAN
301
+ X-WR-TIMEZONE;VALUE=TEXT:Canada/Eastern
302
+ METHOD:PUBLISH
303
+ PRODID:-//Apple Computer\, Inc//iCal 1.0//EN
304
+ X-WR-RELCALID;VALUE=TEXT:18E75B8C-5722-11D7-AB0B-000393AD088C
305
+ X-WR-CALNAME;VALUE=TEXT:Events
306
+ VERSION:2.0
307
+ BEGIN:VEVENT
308
+ SEQUENCE:14
309
+ UID:18E74C28-5722-11D7-AB0B-000393AD088C
310
+ DTSTAMP:20030301T171521Z
311
+ SUMMARY:Bob Log III
312
+ DTSTART;TZID=Canada/Eastern:20030328T200000
313
+ DTEND;TZID=Canada/Eastern:20030328T230000
314
+ DESCRIPTION:Healey's\n\nLook up exact time.\n
315
+ BEGIN:VALARM
316
+ TRIGGER;VALUE=DURATION:-P2D
317
+ ACTION:DISPLAY
318
+ DESCRIPTION:Event reminder
319
+ END:VALARM
320
+ BEGIN:VALARM
321
+ ATTENDEE:mailto:sroberts@uniserve.com
322
+ TRIGGER;VALUE=DURATION:-P1D
323
+ ACTION:EMAIL
324
+ SUMMARY:Alarm notification
325
+ DESCRIPTION:This is an event reminder
326
+ END:VALARM
327
+ END:VEVENT
328
+ BEGIN:VEVENT
329
+ SEQUENCE:1
330
+ DTSTAMP:20030312T043534Z
331
+ SUMMARY:Small Potatoes 10\nFriday\, March 14th\, 8:00 p.m.\n361 Danforth
332
+ Avenue (at Hampton -- Chester subway)\nInfo:  (416) 480-2802 or (416)
333
+ 323-1715\n
334
+ UID:18E750A8-5722-11D7-AB0B-000393AD088C
335
+ DTSTART;TZID=Canada/Eastern:20030315T000000
336
+ DURATION:PT1H
337
+ BEGIN:VALARM
338
+ ATTENDEE:mailto:sroberts@uniserve.com
339
+ TRIGGER;VALUE=DURATION:-P1D
340
+ ACTION:EMAIL
341
+ SUMMARY:Alarm notification
342
+ DESCRIPTION:This is an event reminder
343
+ END:VALARM
344
+ END:VEVENT
345
+ END:VCALENDAR
346
+ EOF
347
+ def test_ical_1
348
+ src = nil
349
+ dst = nil
350
+ assert_nothing_thrown {
351
+ src = Vpim.decode(EX_ICAL_1)
352
+ dst = Vpim.expand(src)
353
+ }
354
+
355
+ #p dst
356
+ end
357
+
358
+ # Constructed data.
359
+ TST1 =<<'EOF'
360
+ BEGIN:vCard
361
+ DESCRIPTION:Healey's\n\nLook up exact time.\n
362
+ email;type=work:work@example.com
363
+ email;type=internet,home;type=pref:home@example.com
364
+ fax;type=foo,pref;bar:fax
365
+ name:firstname
366
+ name:secondname
367
+ time;value=time:
368
+ END:vCARD
369
+ EOF
370
+ def _test_cons # FIXME
371
+ card = nil
372
+ assert_nothing_thrown { card = Vpim::Vcard.decode(TST1).first }
373
+ assert_equal(TST1, card.to_s)
374
+ assert_equal('Healey\'s\n\nLook up exact time.\n', card[ "description" ])
375
+
376
+ # Test the [] API
377
+ assert_equal(nil, card[ "not-a-field" ])
378
+
379
+ assert_equal('firstname', card[ "name" ])
380
+
381
+ assert_equal('home@example.com', card[ "email" ])
382
+ assert_equal('home@example.com', card[ "email", "pref" ])
383
+ assert_equal('home@example.com', card[ "email", "internet" ])
384
+ assert_equal('work@example.com', card[ "email", "work" ])
385
+
386
+
387
+ # Test the merging of vCard 2.1 type fields.
388
+ #p card
389
+ #p card.enum_by_name('fax').entries[0].each_param { |p,v| puts "#{p} = #{v}\n" }
390
+
391
+ assert_equal('fax', card[ "fax" ])
392
+ assert_equal('fax', card[ "fax", 'bar' ])
393
+ end
394
+
395
+ =begin
396
+ def test_bad
397
+ # FIXME: this should THROW, it's badly encoded!
398
+ assert_nothing_thrown {
399
+ Vpim::Vcard.decode(
400
+ "BEGIN:VCARD\nVERSION:3.0\nKEYencoding=b:this could be \nmy certificate\n\nEND:VCARD\n"
401
+ )
402
+ }
403
+ end
404
+ =end
405
+
406
+ def test_create
407
+ card = Vpim::Vcard.create
408
+
409
+ key = Vpim::DirectoryInfo.decode("key;type=x509;encoding=B:dGhpcyBjb3VsZCBiZSAKbXkgY2VydGlmaWNhdGUK\n")['key']
410
+
411
+ card << Vpim::DirectoryInfo::Field.create('key', key, 'encoding' => :b64)
412
+
413
+ assert_equal(key, card['key'])
414
+
415
+ #p card.to_s
416
+ end
417
+
418
+ def test_values
419
+
420
+ # date
421
+ assert_equal([2002, 4, 22], Vpim.decode_date(" 20020422 "))
422
+ assert_equal([2002, 4, 22], Vpim.decode_date(" 2002-04-22 "))
423
+ assert_equal([2002, 4, 22], Vpim.decode_date(" 2002-04-22 \n"))
424
+
425
+ assert_equal([[2002, 4, 22]],
426
+ Vpim.decode_date_list(" 2002-04-22 "))
427
+
428
+ assert_equal([[2002, 4, 22],[2002, 4, 22]],
429
+ Vpim.decode_date_list(" 2002-04-22, 2002-04-22,"))
430
+
431
+ assert_equal([[2002, 4, 22],[2002, 4, 22]],
432
+ Vpim.decode_date_list(" 2002-04-22,,, , ,2002-04-22, , \n"))
433
+
434
+ assert_equal([],
435
+ Vpim.decode_date_list(" , , "))
436
+
437
+ # time
438
+ assert_equal(
439
+ [4, 53, 22, 0, nil],
440
+ Vpim.decode_time(" 04:53:22 \n")
441
+ )
442
+ assert_equal(
443
+ [4, 53, 22, 0.10, nil],
444
+ Vpim.decode_time(" 04:53:22.10 \n")
445
+ )
446
+ assert_equal(
447
+ [4, 53, 22, 0.10, "Z"],
448
+ Vpim.decode_time(" 04:53:22.10Z \n")
449
+ )
450
+ assert_equal(
451
+ [4, 53, 22, 0, "Z"],
452
+ Vpim.decode_time(" 045322Z \n")
453
+ )
454
+ assert_equal(
455
+ [4, 53, 22, 0, "+0530"],
456
+ Vpim.decode_time(" 04:5322+0530 \n")
457
+ )
458
+ assert_equal(
459
+ [4, 53, 22, 0.10, "Z"],
460
+ Vpim.decode_time(" 045322.10Z \n")
461
+ )
462
+
463
+ # date-time
464
+ assert_equal(
465
+ [2002, 4, 22, 4, 53, 22, 0, nil],
466
+ Vpim.decode_date_time("20020422T04:53:22 \n")
467
+ )
468
+ assert_equal(
469
+ [2002, 4, 22, 4, 53, 22, 0.10, nil],
470
+ Vpim.decode_date_time(" 2002-04-22T04:53:22.10 \n")
471
+ )
472
+ assert_equal(
473
+ [2002, 4, 22, 4, 53, 22, 0.10, "Z"],
474
+ Vpim.decode_date_time(" 20020422T04:53:22.10Z \n")
475
+ )
476
+ assert_equal(
477
+ [2002, 4, 22, 4, 53, 22, 0, "Z"],
478
+ Vpim.decode_date_time(" 20020422T045322Z \n")
479
+ )
480
+ assert_equal(
481
+ [2002, 4, 22, 4, 53, 22, 0, "+0530"],
482
+ Vpim.decode_date_time(" 20020422T04:5322+0530 \n")
483
+ )
484
+ assert_equal(
485
+ [2002, 4, 22, 4, 53, 22, 0.10, "Z"],
486
+ Vpim.decode_date_time(" 20020422T045322.10Z \n")
487
+ )
488
+ assert_equal(
489
+ [2003, 3, 25, 3, 20, 35, 0, "Z"],
490
+ Vpim.decode_date_time("20030325T032035Z")
491
+ )
492
+
493
+ # text
494
+ assert_equal(
495
+ "aa,\n\n,\\,\\a;;b",
496
+ Vpim.decode_text('aa,\\n\\n,\\\\\,\\\\a\;\;b')
497
+ )
498
+ assert_equal(
499
+ ['', "1\n2,3", "bbb", '', "zz", ''],
500
+ Vpim.decode_text_list(',1\\n2\\,3,bbb,,zz,')
501
+ )
502
+ end
503
+
504
+ EX_ENCODE_1 =<<'EOF'
505
+ BEGIN:VCARD
506
+ VERSION:3.0
507
+ N:Roberts;Sam;;;
508
+ FN:Roberts Sam
509
+ EMAIL;type=HOME;type=pref:sroberts@uniserve.com
510
+ TEL;type=HOME:416 535 5341
511
+ ADR;type=HOME;type=pref:;;376 Westmoreland Ave.;Toronto;ON;M6H 3A6;Canada
512
+ NOTE:CATEGORIES: Amis/Famille
513
+ BDAY;value=date:1970-07-14
514
+ END:VCARD
515
+ EOF
516
+
517
+ def test_create_1
518
+ card = Vpim::Vcard.create
519
+
520
+ card << DirectoryInfo::Field.create('n', 'Roberts;Sam;;;')
521
+ card << DirectoryInfo::Field.create('fn', 'Roberts Sam')
522
+ card << DirectoryInfo::Field.create('email', 'sroberts@uniserve.com', 'type' => ['home', 'pref'])
523
+ card << DirectoryInfo::Field.create('tel', '416 535 5341', 'type' => 'home')
524
+ # TODO - allow the value to be an array, in which case it will be
525
+ # concatentated with ';'
526
+ card << DirectoryInfo::Field.create('adr', ';;376 Westmoreland Ave.;Toronto;ON;M6H 3A6;Canada', 'type' => ['home', 'pref'])
527
+ # TODO - allow the date to be a Date, and for value to be set correctly
528
+ card << DirectoryInfo::Field.create('bday', Date.new(1970, 7, 14), 'value' => 'date')
529
+
530
+ # puts card.to_s
531
+ end
532
+
533
+ EX_BDAYS = <<'EOF'
534
+ BEGIN:VCARD
535
+ BDAY;value=date:206-12-15
536
+ END:VCARD
537
+ BEGIN:VCARD
538
+ BDAY;value=date:2003-12-09
539
+ END:VCARD
540
+ BEGIN:VCARD
541
+ END:VCARD
542
+ EOF
543
+
544
+ def test_birthday
545
+ cards = Vpim::Vcard.decode(EX_BDAYS)
546
+
547
+ expected = [
548
+ Date.new(Time.now.year, 12, 15),
549
+ Date.new(2003, 12, 9),
550
+ nil
551
+ ]
552
+
553
+ expected.each_with_index do | d, i|
554
+ #pp d
555
+ #pp i
556
+ #pp cards[i]
557
+ #pp cards[i].birthday.to_s
558
+ #pp cards[i].birthday.class
559
+ assert_equal(d, cards[i].birthday)
560
+ end
561
+
562
+ end
563
+
564
+ EX_ATTACH=<<'---'
565
+ BEGIN:VCARD
566
+ VERSION:3.0
567
+ N:Middle Family;Ny_full
568
+ PHOTO:val\nue
569
+ PHOTO;encoding=8bit:val\nue
570
+ PHOTO;encoding=8bit:val\nue
571
+ PHOTO;encoding=8bit;type=atype:val\nue
572
+ PHOTO;value=binary;encoding=8bit:val\nue
573
+ PHOTO;value=binary;encoding=8bit:val\nue
574
+ PHOTO;value=binary;encoding=8bit;type=atype:val\nue
575
+ PHOTO;value=text;encoding=8bit:val\nue
576
+ PHOTO;value=text;encoding=8bit:val\nue
577
+ PHOTO;value=text;encoding=8bit;type=atype:val\nue
578
+ PHOTO;value=uri:my://
579
+ PHOTO;value=uri;type=atype:my://
580
+ END:VCARD
581
+ ---
582
+ def test_attach
583
+ card = Vpim::Vcard.decode(EX_ATTACH).first
584
+ card.lines # FIXME - assert values are as expected
585
+ end
586
+
587
+ EX_21=<<'---'
588
+ BEGIN:VCARD
589
+ VERSION:2.1
590
+ X-EVOLUTION-FILE-AS:AAA Our Fax
591
+ FN:AAA Our Fax
592
+ N:AAA Our Fax
593
+ ADR;WORK;PREF:
594
+ LABEL;WORK;PREF:
595
+ TEL;WORK;FAX:925 833-7660
596
+ TEL;HOME;FAX:925 833-7660
597
+ TEL;VOICE:1
598
+ TEL;FAX:2
599
+ EMAIL;INTERNET:e@c
600
+ TITLE:
601
+ NOTE:
602
+ UID:pas-id-3F93E22900000001
603
+ END:VCARD
604
+ ---
605
+ def test_v21_modification
606
+ card0 = Vpim::Vcard.decode(EX_21).first
607
+ card1 = Vpim::Vcard::Maker.make2(card0) do |maker|
608
+ maker.nickname = 'nickname'
609
+ end
610
+ card2 = Vpim::Vcard.decode(card1.encode).first
611
+
612
+ assert_equal(card0.version, card1.version)
613
+ assert_equal(card0.version, card2.version)
614
+ end
615
+
616
+ def test_v21_versioned_copy
617
+ card0 = Vpim::Vcard.decode(EX_21).first
618
+ card1 = Vpim::Vcard::Maker.make2(Vpim::DirectoryInfo.create([], 'VCARD')) do |maker|
619
+ maker.copy card0
620
+ end
621
+ card2 = Vpim::Vcard.decode(card1.encode).first
622
+
623
+ assert_equal(card0.version, card2.version)
624
+ end
625
+
626
+ def test_v21_strip_version
627
+ card0 = Vpim::Vcard.decode(EX_21).first
628
+
629
+ card0.delete card0.field('VERSION')
630
+ card0.delete card0.field('TEL')
631
+ card0.delete card0.field('TEL')
632
+ card0.delete card0.field('TEL')
633
+ card0.delete card0.field('TEL')
634
+
635
+ assert_raises(ArgumentError) do
636
+ card0.delete card0.field('END')
637
+ end
638
+ assert_raises(ArgumentError) do
639
+ card0.delete card0.field('BEGIN')
640
+ end
641
+
642
+ card1 = Vpim::Vcard::Maker.make2(Vpim::DirectoryInfo.create([], 'VCARD')) do |maker|
643
+ maker.copy card0
644
+ end
645
+ card2 = Vpim::Vcard.decode(card1.encode).first
646
+
647
+ assert_equal(30, card2.version)
648
+ assert_equal(nil, card2.field('TEL'))
649
+ end
650
+
651
+
652
+ EX_21_CASE0=<<'---'
653
+ BEGIN:VCARD
654
+ VERSION:2.1
655
+ N:Middle Family;Ny_full
656
+ TEL;PREF;HOME;VOICE:0123456789
657
+ TEL;FAX:0123456789
658
+ TEL;CELL;VOICE:0123456789
659
+ TEL;HOME;VOICE:0123456789
660
+ TEL;WORK;VOICE:0123456789
661
+ EMAIL:email@email.com
662
+ EMAIL:work@work.com
663
+ URL:www.email.com
664
+ URL:www.work.com
665
+ LABEL;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:Box 1234=0AWorkv=E4gen =
666
+ 2=0AWorkv=E4gen 1=0AUme=E5=0AV=E4sterbotten=0A12345=0AS
667
+ END:VCARD
668
+ ---
669
+ def test_v21_case0
670
+ card = Vpim::Vcard.decode(EX_21_CASE0).first
671
+ # pp card.field('LABEL').value_raw
672
+ # pp card.field('LABEL').value
673
+ end
674
+
675
+ def test_modify_name
676
+ card = Vcard.decode("begin:vcard\nend:vcard\n").first
677
+
678
+ assert_raises(InvalidEncodingError) do
679
+ card.name
680
+ end
681
+
682
+ assert_raises(Unencodeable) do
683
+ Vcard::Maker.make2(card) {}
684
+ end
685
+
686
+ card.make do |m|
687
+ m.name {}
688
+ end
689
+
690
+ assert_equal('', card.name.given)
691
+ assert_equal('', card.name.fullname)
692
+
693
+ assert_raises(TypeError) do
694
+ card.name.given = 'given'
695
+ end
696
+
697
+ card.make do |m|
698
+ m.name do |n|
699
+ n.given = 'given'
700
+ end
701
+ end
702
+
703
+ assert_equal('given', card.name.given)
704
+ assert_equal('given', card.name.fullname)
705
+ assert_equal('' , card.name.family)
706
+
707
+ card.make do |m|
708
+ m.name do |n|
709
+ n.family = n.given
710
+ n.prefix = ' Ser '
711
+ n.fullname = 'well given'
712
+ end
713
+ end
714
+
715
+ assert_equal('given', card.name.given)
716
+ assert_equal('given', card.name.family)
717
+ assert_equal('Ser given given', card.name.formatted)
718
+ assert_equal('well given', card.name.fullname)
719
+ end
720
+
721
+ def test_add_note
722
+ note = "hi\' \ \"\",,;; \n \n field"
723
+
724
+ card = Vpim::Vcard::Maker.make2 do |m|
725
+ m.add_note(note)
726
+ m.name {}
727
+ end
728
+
729
+ assert_equal(note, card.note)
730
+ end
731
+
732
+ def test_empty_tel
733
+ cin = <<___
734
+ BEGIN:VCARD
735
+ TEL;HOME;FAX:
736
+ END:VCARD
737
+ ___
738
+
739
+ card = Vpim::Vcard.decode(cin).first
740
+ assert_equal(card.telephone, nil)
741
+ assert_equal(card.telephone('HOME'), nil)
742
+ assert_equal([], card.telephones)
743
+
744
+ end
745
+
746
+ def test_slash_in_field_name
747
+ cin = <<___
748
+ BEGIN:VCARD
749
+ X-messaging/xmpp-All:some@jabber.id
750
+ END:VCARD
751
+ ___
752
+
753
+ card = Vpim::Vcard.decode(cin).first
754
+ assert_equal(card.value("X-messaging/xmpp-All"), "some@jabber.id")
755
+ assert_equal(card["X-messaging/xmpp-All"], "some@jabber.id")
756
+ end
757
+
758
+ def test_url_decode
759
+ cin=<<'---'
760
+ BEGIN:VCARD
761
+ URL:www.email.com
762
+ URL:www.work.com
763
+ END:VCARD
764
+ ---
765
+ card = Vpim::Vcard.decode(cin).first
766
+
767
+ assert_equal("www.email.com", card.url.uri)
768
+ assert_equal("www.email.com", card.url.uri.to_s)
769
+ assert_equal("www.email.com", card.urls.first.uri)
770
+ assert_equal("www.work.com", card.urls.last.uri)
771
+ end
772
+
773
+ def test_bday_decode
774
+ cin=<<'---'
775
+ BEGIN:VCARD
776
+ BDAY:1970-07-14
777
+ END:VCARD
778
+ ---
779
+ card = Vpim::Vcard.decode(cin).first
780
+
781
+ card.birthday
782
+
783
+ assert_equal(Date.new(1970, 7, 14), card.birthday)
784
+ assert_equal(1, card.values("bday").size)
785
+
786
+ # Nobody should have multiple bdays, I hope, but its allowed syntactically,
787
+ # so test it, along with some variant forms of BDAY
788
+ cin=<<'---'
789
+ BEGIN:VCARD
790
+ BDAY:1970-07-14
791
+ BDAY:70-7-14
792
+ BDAY:1970-07-15T03:45:12
793
+ BDAY:1970-07-15T03:45:12Z
794
+ END:VCARD
795
+ ---
796
+ card = Vpim::Vcard.decode(cin).first
797
+
798
+ assert_equal(Date.new(1970, 7, 14), card.birthday)
799
+ assert_equal(4, card.values("bday").size)
800
+ assert_equal(Date.new(1970, 7, 14), card.values("bday").first)
801
+ assert_equal(Date.new(Time.now.year, 7, 14), card.values("bday")[1])
802
+ assert_equal(DateTime.new(1970, 7, 15, 3, 45, 12).to_s, card.values("bday")[2].to_s)
803
+ assert_equal(DateTime.new(1970, 7, 15, 3, 45, 12).to_s, card.values("bday").last.to_s)
804
+ end
805
+
806
+ def utf_name_test(c)
807
+
808
+ begin
809
+ card = Vpim::Vcard.decode(c).first
810
+ assert_equal("name", card.name.family)
811
+ rescue
812
+ $!.message << " #{c.inspect}"
813
+ raise
814
+ end
815
+ end
816
+
817
+ def be(s)
818
+ s.unpack('U*').pack('n*')
819
+ end
820
+ def le(s)
821
+ s.unpack('U*').pack('v*')
822
+ end
823
+
824
+ def test_utf_heuristics
825
+ bom = "\xEF\xBB\xBF"
826
+ dat = "BEGIN:VCARD\nN:name\nEND:VCARD\n"
827
+ utf_name_test(bom+dat)
828
+ utf_name_test(bom+dat.downcase)
829
+ utf_name_test(dat)
830
+ utf_name_test(dat.downcase)
831
+
832
+ utf_name_test(be(bom+dat))
833
+ utf_name_test(be(bom+dat.downcase))
834
+ utf_name_test(be(dat))
835
+ utf_name_test(be(dat.downcase))
836
+
837
+ utf_name_test(le(bom+dat))
838
+ utf_name_test(le(bom+dat.downcase))
839
+ utf_name_test(le(dat))
840
+ utf_name_test(le(dat.downcase))
841
+ end
842
+
843
+ # Broken output from Highrise. Report to support@highrisehq.com
844
+ def test_highrises_invalid_google_talk_field
845
+ c = <<'__'
846
+ BEGIN:VCARD
847
+ VERSION:3.0
848
+ REV:20080409T095515Z
849
+ X-YAHOO;TYPE=HOME:yahoo.john
850
+ X-GOOGLE TALK;TYPE=WORK:gtalk.john
851
+ X-SAMETIME;TYPE=WORK:sametime.john
852
+ X-SKYPE;TYPE=WORK:skype.john
853
+ X-MSN;TYPE=WORK:msn.john
854
+ X-JABBER;TYPE=WORK:jabber.john
855
+ N:Doe;John;;;
856
+ ADR;TYPE=WORK:;;456 Grandview Building\, Wide Street;San Diego;CA;90204;
857
+ United States
858
+ ADR;TYPE=HOME:;;123 Sweet Home\, Narrow Street;New York;NY;91102;United
859
+ States
860
+ URL;TYPE=OTHER:http\://www.homepage.com
861
+ URL;TYPE=HOME:http\://www.home.com
862
+ URL;TYPE=WORK:http\://www.work.com
863
+ URL;TYPE=OTHER:http\://www.other.com
864
+ URL;TYPE=OTHER:http\://www.custom.com
865
+ ORG:John Doe & Partners Limited;;
866
+ TEL;TYPE=WORK:11111111
867
+ TEL;TYPE=CELL:22222222
868
+ TEL;TYPE=HOME:33333333
869
+ TEL;TYPE=OTHER:44444444
870
+ TEL;TYPE=FAX:55555555
871
+ TEL;TYPE=FAX:66666666
872
+ TEL;TYPE=PAGER:77777777
873
+ TEL;TYPE=OTHER:88888888
874
+ TEL;TYPE=OTHER:99999999
875
+ UID:cc548e11-569e-3bf5-a9aa-722de4571f4a
876
+ X-ICQ;TYPE=HOME:icq.john
877
+ EMAIL;TYPE=WORK,INTERNET:john.doe@work.com
878
+ EMAIL;TYPE=HOME,INTERNET:john.doe@home.com
879
+ EMAIL;TYPE=OTHER,INTERNET:john.doe@other.com
880
+ EMAIL;TYPE=OTHER,INTERNET:john.doe@custom.com
881
+ TITLE:Sales Manager
882
+ X-OTHER;TYPE=WORK:other.john
883
+ X-AIM;TYPE=WORK:aim.john
884
+ X-QQ;TYPE=WORK:qq.john
885
+ FN:John Doe
886
+ END:VCARD
887
+ __
888
+
889
+ card = Vpim::Vcard.decode(c).first
890
+ assert_equal("Doe", card.name.family)
891
+ assert_equal("456 Grandview Building, Wide Street", card.address('work').street)
892
+ assert_equal("123 Sweet Home, Narrow Street", card.address('home').street)
893
+ assert_equal("John Doe & Partners Limited", card.org.first)
894
+ assert_equal("gtalk.john", card.value("x-google talk"))
895
+ assert_equal("http\\://www.homepage.com", card.url.uri)
896
+
897
+ end
898
+
899
+ def _test_gmail_vcard_export
900
+ # GOOGLE BUG - Whitespace before the LABEL field values is a broken
901
+ # line continuation.
902
+ # GOOGLE BUG - vCards are being exported with embedded "=" in them, so
903
+ # become unparseable.
904
+ c = <<'__'
905
+ BEGIN:VCARD
906
+ VERSION:3.0
907
+ FN:Stepcase TestUser
908
+ N:TestUser;Stepcase;;;
909
+ EMAIL;TYPE=INTERNET:testuser@stepcase.com
910
+ X-GTALK:gtalk.step
911
+ X-AIM:aim.step
912
+ X-YAHOO:yahoo.step
913
+ X-MSN:msn.step
914
+ X-ICQ:icq.step
915
+ X-JABBER:jabber.step
916
+ TEL;TYPE=FAX:44444444
917
+ TEL;TYPE=PAGER:66666666
918
+ TEL;TYPE=HOME:22222222
919
+ TEL;TYPE=CELL:11111111
920
+ TEL;TYPE=FAX:55555555
921
+ TEL;TYPE=WORK:33333333
922
+ LABEL;TYPE=HOME;ENCODING=QUOTED-PRINTABLE:123 Home, Home Street=0D=0A=
923
+ Kowloon, N/A=0D=0A=
924
+ Hong Kong
925
+ LABEL;TYPE=HOME;ENCODING=QUOTED-PRINTABLE:321 Office, Work Road=0D=0A=
926
+ Tsuen Wan NT=0D=0A=
927
+ Hong Kong
928
+ TITLE:CTO
929
+ ORG:Stepcase.com
930
+ NOTE:Stepcase test user is a robot.
931
+ END:VCARD
932
+ __
933
+ card = Vpim::Vcard.decode(c).first
934
+ assert_equal("123 Home, Home Street\r\n Kowloon, N/A\r\n Hong Kong", card.value("label"))
935
+ end
936
+
937
+ def test_title
938
+ title = "She Who Must Be Obeyed"
939
+ card = Vpim::Vcard::Maker.make2 do |m|
940
+ m.name do |n|
941
+ n.given = "Hilda"
942
+ n.family = "Rumpole"
943
+ end
944
+ m.title = title
945
+ end
946
+ assert_equal(title, card.title)
947
+ card = Vpim::Vcard.decode(card.encode).first
948
+ assert_equal(title, card.title)
949
+ end
950
+
951
+ def _test_org(*org)
952
+ card = Vpim::Vcard::Maker.make2 do |m|
953
+ m.name do |n|
954
+ n.given = "Hilda"
955
+ n.family = "Rumpole"
956
+ end
957
+ m.org = org
958
+ end
959
+ assert_equal(org, card.org)
960
+ card = Vpim::Vcard.decode(card.encode).first
961
+ assert_equal(org, card.org)
962
+ end
963
+
964
+ def test_org_single
965
+ _test_org("Megamix Corp.")
966
+ end
967
+
968
+ def test_org_multiple
969
+ _test_org("Megamix Corp.", "Marketing")
970
+ end
971
+
972
+ end
973
+