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.
- checksums.yaml +7 -0
- data/CHANGES +504 -0
- data/COPYING +58 -0
- data/README +182 -0
- data/lib/atom.rb +728 -0
- data/lib/plist.rb +22 -0
- data/lib/vpim.rb +13 -0
- data/lib/vpim/address.rb +219 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/date.rb +222 -0
- data/lib/vpim/dirinfo.rb +277 -0
- data/lib/vpim/duration.rb +119 -0
- data/lib/vpim/enumerator.rb +32 -0
- data/lib/vpim/field.rb +614 -0
- data/lib/vpim/icalendar.rb +381 -0
- data/lib/vpim/maker/vcard.rb +16 -0
- data/lib/vpim/property/base.rb +193 -0
- data/lib/vpim/property/common.rb +315 -0
- data/lib/vpim/property/location.rb +38 -0
- data/lib/vpim/property/priority.rb +43 -0
- data/lib/vpim/property/recurrence.rb +69 -0
- data/lib/vpim/property/resources.rb +24 -0
- data/lib/vpim/repo.rb +181 -0
- data/lib/vpim/rfc2425.rb +367 -0
- data/lib/vpim/rrule.rb +591 -0
- data/lib/vpim/vcard.rb +1430 -0
- data/lib/vpim/version.rb +18 -0
- data/lib/vpim/vevent.rb +187 -0
- data/lib/vpim/view.rb +90 -0
- data/lib/vpim/vjournal.rb +58 -0
- data/lib/vpim/vpim.rb +65 -0
- data/lib/vpim/vtodo.rb +103 -0
- data/samples/README.mutt +93 -0
- data/samples/ab-query.rb +57 -0
- data/samples/cmd-itip.rb +156 -0
- data/samples/ex_cpvcard.rb +55 -0
- data/samples/ex_get_vcard_photo.rb +22 -0
- data/samples/ex_mkv21vcard.rb +34 -0
- data/samples/ex_mkvcard.rb +64 -0
- data/samples/ex_mkyourown.rb +29 -0
- data/samples/ics-dump.rb +210 -0
- data/samples/ics-to-rss.rb +84 -0
- data/samples/mutt-aliases-to-vcf.rb +45 -0
- data/samples/osx-wrappers.rb +86 -0
- data/samples/reminder.rb +203 -0
- data/samples/rrule.rb +71 -0
- data/samples/tabbed-file-to-vcf.rb +390 -0
- data/samples/vcf-dump.rb +86 -0
- data/samples/vcf-lines.rb +61 -0
- data/samples/vcf-to-ics.rb +22 -0
- data/samples/vcf-to-mutt.rb +121 -0
- data/test/test_all.rb +17 -0
- data/test/test_date.rb +120 -0
- data/test/test_dur.rb +41 -0
- data/test/test_field.rb +156 -0
- data/test/test_ical.rb +415 -0
- data/test/test_repo.rb +158 -0
- data/test/test_rrule.rb +1030 -0
- data/test/test_vcard.rb +973 -0
- data/test/test_view.rb +79 -0
- metadata +117 -0
data/samples/README.mutt
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
** cmd-itip.rb
|
3
|
+
|
4
|
+
This script pretty-prints iTIP calendar invitations, often sent by email using iMIP
|
5
|
+
as text/calendar objects.
|
6
|
+
|
7
|
+
Download the latest vPim from:
|
8
|
+
|
9
|
+
http://rubyforge.org/projects/vpim/
|
10
|
+
|
11
|
+
It requires Ruby to be installed.
|
12
|
+
|
13
|
+
Install vpim:
|
14
|
+
|
15
|
+
tar -xzf vpim-XX.tgz
|
16
|
+
cd vpim-XX
|
17
|
+
ruby install.rb config
|
18
|
+
ruby install.rb setup
|
19
|
+
sudo ruby install.rb install
|
20
|
+
|
21
|
+
Install cmd-itip.rb into your path, perhaps without the extension.
|
22
|
+
|
23
|
+
cp samples/cmd-itip.rb ~/bin/cmd-itip
|
24
|
+
chmod +x ~/bin/cmd-itip
|
25
|
+
|
26
|
+
Modify your ~/.mailcap or /etc/mailcap files to call cmd-itip, add a line like:
|
27
|
+
|
28
|
+
text/calendar; cmd-itip --myaddr "sroberts@" %s; copiousoutput
|
29
|
+
|
30
|
+
If you give a REGEX to --myaddr to tell cmd-itip your email addresses, cmd-itip
|
31
|
+
will avoid printing some information on the attendees to an invitation.
|
32
|
+
|
33
|
+
Modify muttrc to autoview calendars with a command like:
|
34
|
+
|
35
|
+
auto_view text/calendar
|
36
|
+
|
37
|
+
Notes on Notes;
|
38
|
+
|
39
|
+
Because Domino sends a close-to-unreadable text/plain attachment along with the
|
40
|
+
text/calendar in a multipart/alternative, and the text/plain is first in the
|
41
|
+
alternatives, the garbage will be at the top, and the nicely printed calendar
|
42
|
+
at the bottom. Because of this, I reorder the view preference so the calendar
|
43
|
+
invitation is clearly printed at the top of the message with a muttrc command
|
44
|
+
like:
|
45
|
+
|
46
|
+
alternative_order text/calendar text/plain
|
47
|
+
|
48
|
+
Domino also includes the calendar twice in the mail message, so you'll see it
|
49
|
+
twice, I don't know what to do about that.
|
50
|
+
|
51
|
+
Notes on application/octet-stream:
|
52
|
+
|
53
|
+
Some calendar programs, such as Apple's Mail.app, wrongly send iCalendar attachments
|
54
|
+
with a content-type of application/octet-stream. In order to be processed correctly, use
|
55
|
+
the mutt 1.5 or later capability to lookup the correct MIME content-type based on the
|
56
|
+
file extension. Put this in your muttrc file:
|
57
|
+
|
58
|
+
mime_lookup application/octet-stream
|
59
|
+
|
60
|
+
and ensure /etc/mime.types or ~/.mime.types contains:
|
61
|
+
|
62
|
+
text/calendar ics
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
** vcf-to-mutt.rb
|
67
|
+
|
68
|
+
This script searches a set of vCards can output the results as a Mutt query response,
|
69
|
+
or a Mutt aliases file.
|
70
|
+
|
71
|
+
It used to support querying the OS X Address Book, but that is better done with lbdb, see
|
72
|
+
http://www.spinnaker.de/lbdb/.
|
73
|
+
|
74
|
+
To install, you must:
|
75
|
+
|
76
|
+
1 - install vPim (see README)
|
77
|
+
|
78
|
+
3 - copy vcf-to-mutt into a directory in your path, such
|
79
|
+
as ~/bin, and chmod +x vcf-to-mutt.rb to make it executable.
|
80
|
+
|
81
|
+
4 - Put in your muttrc file (either ~/.muttrc or ~/.mutt/muttrc) a line such as:
|
82
|
+
|
83
|
+
set query_command = "vcf-to-mutt.rb '%s'"
|
84
|
+
|
85
|
+
5 - The query command ("Q") will query the address book, control-t will give you auto-completion
|
86
|
+
of email addresses, see the Mutt manual page.
|
87
|
+
|
88
|
+
|
89
|
+
** mutt-aliases-to-vcf.rb
|
90
|
+
|
91
|
+
This script converts a mutt aliases file into a vCard file.
|
92
|
+
|
93
|
+
|
data/samples/ab-query.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$-w = true
|
4
|
+
$:.unshift File.dirname($0) + '/../lib'
|
5
|
+
|
6
|
+
require 'osx-wrappers'
|
7
|
+
|
8
|
+
require 'getoptlong'
|
9
|
+
require 'vpim/vcard'
|
10
|
+
require 'osx-wrappers'
|
11
|
+
|
12
|
+
HELP =<<EOF
|
13
|
+
Usage: ab-query.rb [--me] [--all]
|
14
|
+
|
15
|
+
Queries the OS X Address Book for vCards.
|
16
|
+
|
17
|
+
-h, --help print this helpful message
|
18
|
+
-m, --me list my vCard
|
19
|
+
-a, --all list all vCards
|
20
|
+
EOF
|
21
|
+
|
22
|
+
opts = GetoptLong.new(
|
23
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
24
|
+
[ "--me", "-m", GetoptLong::NO_ARGUMENT ],
|
25
|
+
[ "--all", "-a", GetoptLong::NO_ARGUMENT ]
|
26
|
+
)
|
27
|
+
|
28
|
+
abook = nil
|
29
|
+
|
30
|
+
opts.each do |opt, arg|
|
31
|
+
case opt
|
32
|
+
when "--help" then
|
33
|
+
puts HELP
|
34
|
+
exit 0
|
35
|
+
|
36
|
+
when "--all" then
|
37
|
+
abook = OSX::ABAddressBook.sharedAddressBook unless abook
|
38
|
+
|
39
|
+
abook.people.to_a.each {
|
40
|
+
|person|
|
41
|
+
|
42
|
+
puts person.vCard
|
43
|
+
}
|
44
|
+
|
45
|
+
when "--me" then
|
46
|
+
abook = OSX::ABAddressBook.sharedAddressBook unless abook
|
47
|
+
|
48
|
+
puts abook.me.vCard
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
unless abook
|
54
|
+
puts HELP
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
data/samples/cmd-itip.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$-w = true
|
4
|
+
$:.unshift File.dirname($0) + '/../lib'
|
5
|
+
|
6
|
+
require 'getoptlong'
|
7
|
+
|
8
|
+
require 'vpim/icalendar'
|
9
|
+
require 'vpim/duration'
|
10
|
+
|
11
|
+
include Vpim
|
12
|
+
|
13
|
+
# TODO - $0 is the full path, fix it.
|
14
|
+
HELP =<<EOF
|
15
|
+
Usage: #{$0} <invitation.ics>
|
16
|
+
|
17
|
+
Options
|
18
|
+
-h,--help Print this helpful message.
|
19
|
+
-d,--debug Print debug information.
|
20
|
+
|
21
|
+
-m,--my-addrs My email addresses, a REGEX.
|
22
|
+
Examples:
|
23
|
+
EOF
|
24
|
+
|
25
|
+
opt_debug = nil
|
26
|
+
opt_print = true
|
27
|
+
|
28
|
+
# Ways to get this:
|
29
|
+
# Use a --mutt option, and steal it from muttrc,
|
30
|
+
# from $USER, $LOGNAME,, from /etc/passwd...
|
31
|
+
opt_myaddrs = nil
|
32
|
+
|
33
|
+
opts = GetoptLong.new(
|
34
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
35
|
+
|
36
|
+
[ "--myaddrs", "-m", GetoptLong::REQUIRED_ARGUMENT ],
|
37
|
+
|
38
|
+
[ "--accept", "-a", GetoptLong::REQUIRED_ARGUMENT ],
|
39
|
+
[ "--reject", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
40
|
+
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
|
41
|
+
)
|
42
|
+
|
43
|
+
opts.each do |opt, arg|
|
44
|
+
case opt
|
45
|
+
when "--help" then
|
46
|
+
puts HELP
|
47
|
+
exit 0
|
48
|
+
|
49
|
+
when "--debug" then
|
50
|
+
require 'pp'
|
51
|
+
opt_debug = true
|
52
|
+
|
53
|
+
when "--myaddrs" then
|
54
|
+
opt_myaddrs = Regexp.new(arg, 'i')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if ARGV.length < 1
|
59
|
+
puts "no input files specified, try -h!\n"
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
|
63
|
+
ARGV.each do |file|
|
64
|
+
cals = Vpim::Icalendar.decode(File.open(file))
|
65
|
+
|
66
|
+
cals.each do |cal|
|
67
|
+
if opt_debug
|
68
|
+
puts "vCalendar: version=#{cal.version/10.0} producer='#{cal.producer}'"
|
69
|
+
if cal.protocol; puts " protocol-method=#{cal.protocol}"; end
|
70
|
+
end
|
71
|
+
|
72
|
+
events = cal.events
|
73
|
+
|
74
|
+
if events.size != 1
|
75
|
+
raise "!! #{events.size} calendar events is more than 1!"
|
76
|
+
end
|
77
|
+
|
78
|
+
events.each do |e|
|
79
|
+
summary = e.summary || e.comment || ''
|
80
|
+
|
81
|
+
case cal.protocol.upcase
|
82
|
+
when 'PUBLISH'
|
83
|
+
puts "Notification of: #{summary}"
|
84
|
+
|
85
|
+
when 'REQUEST'
|
86
|
+
puts "Request for: #{summary}"
|
87
|
+
|
88
|
+
when 'REPLY'
|
89
|
+
|
90
|
+
else
|
91
|
+
raise "!! unhandled protocol type #{cal.protocol}!"
|
92
|
+
end
|
93
|
+
|
94
|
+
puts "Organized by: #{e.organizer.to_s}"
|
95
|
+
|
96
|
+
# TODO - spec as hours/mins/secs
|
97
|
+
e.occurrences.each_with_index do |t, i|
|
98
|
+
if(i < 1)
|
99
|
+
puts "At time: #{t}" +( e.duration ? " for #{Duration.secs(e.duration).to_s}" : '' )
|
100
|
+
else
|
101
|
+
puts "... and others"
|
102
|
+
break
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if e.location; puts "Located at: #{e.location}"; end
|
107
|
+
|
108
|
+
if e.description
|
109
|
+
puts finish="-- Description --"
|
110
|
+
puts e.description
|
111
|
+
end
|
112
|
+
|
113
|
+
if e.comments
|
114
|
+
puts finish="-- Comment --"
|
115
|
+
puts " comment=#{e.comments}"
|
116
|
+
end
|
117
|
+
|
118
|
+
if e.attendees.first
|
119
|
+
|
120
|
+
puts finish="-- Attendees --"
|
121
|
+
|
122
|
+
e.attendees.each_with_index do |a,i|
|
123
|
+
puts "#{i} #{a.to_s}"
|
124
|
+
if !opt_myaddrs || a.uri =~ opt_myaddrs
|
125
|
+
puts " participation-status: #{a.partstat ? a.partstat.downcase : 'unknown'}"
|
126
|
+
puts " response-requested? #{a.rsvp ? 'yes' : 'no'}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
if finish
|
132
|
+
puts '-' * finish.length
|
133
|
+
end
|
134
|
+
|
135
|
+
if opt_debug
|
136
|
+
if e.status; puts " status=#{e.status}"; end
|
137
|
+
puts " uid=#{e.uid}"
|
138
|
+
puts " dtstamp=#{e.dtstamp.to_s}"
|
139
|
+
puts " dtstart=#{e.dtstart.to_s}"
|
140
|
+
if e.dtend; puts " dtend=#{e.dtend.to_s}"; end
|
141
|
+
if e.rrule; puts " rrule=#{e.rrule}"; end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
todos = cal.todos
|
146
|
+
todos.each do |e|
|
147
|
+
s = e.status ? " (#{e.status})" : ''
|
148
|
+
puts "Todo#{s}: #{e.summary}"
|
149
|
+
end
|
150
|
+
|
151
|
+
if opt_debug
|
152
|
+
pp cals
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'vpim/vcard'
|
2
|
+
|
3
|
+
ORIGINAL =<<'---'
|
4
|
+
BEGIN:VCARD
|
5
|
+
VERSION:3.0
|
6
|
+
FN:Jimmy Death
|
7
|
+
N:Death;Jimmy;;Dr.;
|
8
|
+
TEL:+416 123 1111
|
9
|
+
TEL;type=home,pref:+416 123 2222
|
10
|
+
TEL;type=work,fax:+416+123+3333
|
11
|
+
EMAIL;type=work:drdeath@work.com
|
12
|
+
EMAIL;type=pref:drdeath@home.net
|
13
|
+
NOTE:Do not call.
|
14
|
+
END:VCARD
|
15
|
+
---
|
16
|
+
|
17
|
+
original = Vpim::Vcard.decode(ORIGINAL).first
|
18
|
+
|
19
|
+
puts original
|
20
|
+
|
21
|
+
modified = Vpim::Vcard::Maker.make2 do |maker|
|
22
|
+
# Set the fullname field to use family-given name order.
|
23
|
+
maker.name do |n|
|
24
|
+
n.fullname = "#{original.name.family} #{original.name.given}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Copy original fields, with some changes:
|
28
|
+
# - set only work email addresses and telephone numbers to be preferred.
|
29
|
+
# - don't copy notes
|
30
|
+
maker.copy(original) do |field|
|
31
|
+
if field.name? 'EMAIL'
|
32
|
+
field = field.copy
|
33
|
+
field.pref = field.type? 'work'
|
34
|
+
end
|
35
|
+
if field.name? 'TEL'
|
36
|
+
field = field.copy
|
37
|
+
field.pref = field.type? 'work'
|
38
|
+
end
|
39
|
+
if field.name? 'NOTE'
|
40
|
+
field = nil
|
41
|
+
end
|
42
|
+
field
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
puts '---'
|
47
|
+
puts modified
|
48
|
+
|
49
|
+
Vpim::Vcard::Maker.make2(modified) do |maker|
|
50
|
+
maker.nickname = "Your Last Friend"
|
51
|
+
end
|
52
|
+
|
53
|
+
puts '---'
|
54
|
+
puts modified
|
55
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
require 'vpim/vcard'
|
4
|
+
|
5
|
+
vcf = open(ARGV[0] || 'data/vcf/Sam Roberts.vcf')
|
6
|
+
|
7
|
+
card = Vpim::Vcard.decode(vcf).first
|
8
|
+
|
9
|
+
card.photos.each_with_index do |photo, i|
|
10
|
+
file = "_photo_#{i}."
|
11
|
+
|
12
|
+
if photo.format
|
13
|
+
file += photo.format.gsub('/', '_')
|
14
|
+
else
|
15
|
+
# You are your own if PHOTO doesn't include a format. AddressBook.app
|
16
|
+
# exports TIFF, for example, but doesn't specify that.
|
17
|
+
file += 'tiff'
|
18
|
+
end
|
19
|
+
|
20
|
+
open(file, 'w').write photo.to_s
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Note that while most version 3.0 vCards should be valid 2.1 vCards, they
|
2
|
+
# aren't guaranteed to be. vCard 2.1 is reasonably well supported on decode,
|
3
|
+
# I'm not sure how well it works on encode.
|
4
|
+
#
|
5
|
+
# Most things should work, but you should test whether this works with your 2.1
|
6
|
+
# vCard decoder. Also, avoid base64 encoding, or do it manually.
|
7
|
+
require 'vpim/vcard'
|
8
|
+
|
9
|
+
# Create a new 2.1 vCard.
|
10
|
+
card21 = Vpim::DirectoryInfo.create(
|
11
|
+
[
|
12
|
+
Vpim::DirectoryInfo::Field.create('VERSION', '2.1')
|
13
|
+
], 'VCARD')
|
14
|
+
|
15
|
+
Vpim::Vcard::Maker.make2(card21) do |maker|
|
16
|
+
maker.name do |n|
|
17
|
+
n.prefix = 'Dr.'
|
18
|
+
n.given = 'Jimmy'
|
19
|
+
n.family = 'Death'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
puts card21
|
25
|
+
|
26
|
+
# Copy and modify a 2.1 vCard, preserving it's version.
|
27
|
+
mod21 = Vpim::Vcard::Maker.make2(Vpim::DirectoryInfo.create([], 'VCARD')) do |maker|
|
28
|
+
maker.copy card21
|
29
|
+
maker.nickname = 'some name'
|
30
|
+
end
|
31
|
+
|
32
|
+
puts '---'
|
33
|
+
puts mod21
|
34
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'vpim/vcard'
|
2
|
+
|
3
|
+
card = Vpim::Vcard::Maker.make2 do |maker|
|
4
|
+
maker.add_name do |name|
|
5
|
+
name.prefix = 'Dr.'
|
6
|
+
name.given = 'Jimmy'
|
7
|
+
name.family = 'Death'
|
8
|
+
end
|
9
|
+
|
10
|
+
maker.add_addr do |addr|
|
11
|
+
addr.preferred = true
|
12
|
+
addr.location = 'work'
|
13
|
+
addr.street = '12 Last Row, 13th Section'
|
14
|
+
addr.locality = 'City of Lost Children'
|
15
|
+
addr.country = 'Cinema'
|
16
|
+
end
|
17
|
+
|
18
|
+
maker.add_addr do |addr|
|
19
|
+
addr.location = [ 'home', 'zoo' ]
|
20
|
+
addr.delivery = [ 'snail', 'stork', 'camel' ]
|
21
|
+
addr.street = '12 Last Row, 13th Section'
|
22
|
+
addr.locality = 'City of Lost Children'
|
23
|
+
addr.country = 'Cinema'
|
24
|
+
end
|
25
|
+
|
26
|
+
maker.nickname = "The Good Doctor"
|
27
|
+
|
28
|
+
maker.birthday = Date.today
|
29
|
+
|
30
|
+
maker.add_photo do |photo|
|
31
|
+
photo.link = 'http://example.com/image.png'
|
32
|
+
end
|
33
|
+
|
34
|
+
maker.add_photo do |photo|
|
35
|
+
photo.image = "File.open('drdeath.jpg').read # a fake string, real data is too large :-)"
|
36
|
+
photo.type = 'jpeg'
|
37
|
+
end
|
38
|
+
|
39
|
+
maker.add_tel('416 123 1111')
|
40
|
+
|
41
|
+
maker.add_tel('416 123 2222') { |t| t.location = 'home'; t.preferred = true }
|
42
|
+
|
43
|
+
maker.add_impp('joe') do |impp|
|
44
|
+
impp.preferred = 'yes'
|
45
|
+
impp.location = 'mobile'
|
46
|
+
end
|
47
|
+
|
48
|
+
maker.add_x_aim('example') do |xaim|
|
49
|
+
xaim.location = 'row12'
|
50
|
+
end
|
51
|
+
|
52
|
+
maker.add_tel('416-123-3333') do |tel|
|
53
|
+
tel.location = 'work'
|
54
|
+
tel.capability = 'fax'
|
55
|
+
end
|
56
|
+
|
57
|
+
maker.add_email('drdeath@work.com') { |e| e.location = 'work' }
|
58
|
+
|
59
|
+
maker.add_email('drdeath@home.net') { |e| e.preferred = 'yes' }
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
puts card
|
64
|
+
|