vpim2 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|