vpim 0.597 → 0.602

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.occurences.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
+
@@ -0,0 +1,29 @@
1
+ require 'vpim/vcard'
2
+
3
+ module Vpim
4
+ class Vcard
5
+ class Maker
6
+ # Add a user-defined field, X-MY-OWN:.
7
+ #
8
+ # This can be done both to encode custom fields, or to add support for
9
+ # fields that Vcard::Maker doesn't support. In the latter case, please
10
+ # submit your methods so I can add them to vPim.
11
+ def add_my_own(value)
12
+ @card << Vpim::DirectoryInfo::Field.create( 'X-MY-OWN', value.to_str );
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ card = Vpim::Vcard.create # ... or load from somewhere
20
+
21
+ Vpim::Vcard::Maker.make2(card) do |m|
22
+ m.add_name do |n|
23
+ n.given = 'Given'
24
+ end
25
+ m.add_my_own 'my value'
26
+ end
27
+
28
+ puts card
29
+
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Calendars are in ~/Library/Calendars/
4
+
5
+ $-w = true
6
+ $:.unshift File.dirname($0) + '/../lib'
7
+
8
+ require 'getoptlong'
9
+ require 'pp'
10
+ require 'open-uri'
11
+
12
+ require 'vpim/icalendar'
13
+ require 'vpim/duration'
14
+
15
+ include Vpim
16
+
17
+ HELP =<<EOF
18
+ Usage: #{$0} <vcard>...
19
+
20
+ Options
21
+ -h,--help Print this helpful message.
22
+ -n,--node Dump as nodes.
23
+ -d,--debug Print debug information.
24
+ -m,--metro Convert metro.
25
+
26
+ Examples:
27
+ EOF
28
+
29
+ opt_debug = nil
30
+ opt_node = false
31
+
32
+ opts = GetoptLong.new(
33
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
34
+ [ "--node", "-n", GetoptLong::NO_ARGUMENT ],
35
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
36
+ )
37
+
38
+ opts.each do |opt, arg|
39
+ case opt
40
+ when "--help" then
41
+ puts HELP
42
+ exit 0
43
+
44
+ when "--node" then
45
+ opt_node = true
46
+
47
+ when "--debug" then
48
+ opt_debug = true
49
+ end
50
+ end
51
+
52
+ if ARGV.length < 1
53
+ puts "no input files specified, try -h!\n"
54
+ exit 1
55
+ end
56
+
57
+ if opt_node
58
+
59
+ ARGV.each do |file|
60
+ tree = Vpim.expand(Vpim.decode(File.open(file).read(nil)))
61
+ pp tree
62
+ end
63
+
64
+ exit 0
65
+ end
66
+
67
+ def v2s(v)
68
+ case v
69
+ when Vpim::Icalendar::Attachment
70
+ if v.binary
71
+ "#{v.format.inspect} binary #{v.binary.inspect}"
72
+ else
73
+ s = "#{v.format.inspect} uri #{v.uri.inspect}"
74
+ begin
75
+ s << " #{v.value.gets.inspect}..."
76
+ rescue
77
+ s << " (#{$!.class})"
78
+ end
79
+ end
80
+ else
81
+ v #.inspect
82
+ end
83
+ end
84
+
85
+
86
+ def puts_properties(c)
87
+ [
88
+ :access_class,
89
+ :attachments,
90
+ :categories,
91
+ :comments,
92
+ :completed,
93
+ :contacts,
94
+ :created,
95
+ :description,
96
+ :dtend,
97
+ :dtstamp,
98
+ :dtstart,
99
+ :due,
100
+ :geo,
101
+ :location,
102
+ :organizer,
103
+ :percent_complete,
104
+ :priority,
105
+ :sequence,
106
+ :status,
107
+ :summary,
108
+ :transparency,
109
+ :uid,
110
+ :url,
111
+ ].each do |m|
112
+ if c.respond_to? m
113
+ v = c.send(m)
114
+ case v
115
+ when Array
116
+ v.each_with_index do |v,i|
117
+ puts " #{m}[#{i}]=<#{v2s v}>"
118
+ end
119
+ else
120
+ if v
121
+ puts " #{m}=<#{v2s v}>"
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ begin
128
+ if c.duration; puts " duration=#{Duration.secs(c.duration).to_s}"; end
129
+ rescue NoMethodError
130
+ end
131
+
132
+ c.attendees.each_with_index do |a,i|
133
+ puts " attendee[#{i}]=#{a.to_s}"
134
+ puts " role=#{a.role.upcase} participation-status=#{a.partstat.upcase} rsvp?=#{a.rsvp ? 'yes' : 'no'}"
135
+ end
136
+
137
+ [
138
+ 'RRULE',
139
+ 'RDATE',
140
+ 'EXRULE',
141
+ 'EXDATE',
142
+ ].each do |m|
143
+ c.propvaluearray(m).each_with_index do |v,i|
144
+ puts " #{m}[#{i}]=<#{v.to_s}>"
145
+
146
+ case
147
+ when i == 1 && m != 'RRULE'
148
+ # Anything that isn't an RRULE isn't supported at all.
149
+ puts " ==> #{m} is unsupported!"
150
+ when i == 2 && m == 'RRULE'
151
+ # If there was more than 1 RRULE, its not supported.
152
+ puts " ==> More than one RRULE is unsupported!"
153
+ end
154
+ end
155
+ end
156
+
157
+ begin
158
+ c.occurences.each_with_index do |t, i|
159
+ if(i < 10)
160
+ puts " #{i+1} -> #{t}"
161
+ else
162
+ puts " ..."
163
+ break;
164
+ end
165
+ end
166
+ rescue ArgumentError
167
+ # No occurences.
168
+ end
169
+
170
+ end
171
+
172
+ ARGV.each do |file|
173
+ puts "===> ", file
174
+ cals = Vpim::Icalendar.decode(
175
+ (file == "-") ? $stdin : open(file)
176
+ )
177
+
178
+ cals.each_with_index do |cal, i|
179
+ if i > 0
180
+ puts
181
+ end
182
+
183
+ puts "Icalendar[#{i}]:"
184
+ puts " version=#{cal.version/10.0}"
185
+ puts " producer=#{cal.producer}"
186
+
187
+ if cal.protocol; puts " protocol=#{cal.protocol}"; end
188
+
189
+ events = cal.events
190
+
191
+ cal.components.each_with_index do |c, i|
192
+ puts " #{c.class.to_s.sub(/.*::/,'')}[#{i}]:"
193
+
194
+ begin
195
+ puts_properties(c)
196
+ rescue => e
197
+ cb = e.backtrace
198
+ pp e
199
+ print cb.shift, ":", e.message, " (", e.class, ")\n"
200
+ cb.each{|c| print "\tfrom ", c, "\n"}
201
+ exit 1
202
+ end
203
+ end
204
+
205
+ if opt_debug
206
+ pp cals
207
+ end
208
+ end
209
+ end
210
+
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Call with --print to print RSS to stdout, otherwise it runs as a WEBrick
4
+ # servelet on port 8080.
5
+ #
6
+ # This comes from an idea of Dave Thomas' that he described here:
7
+ #
8
+ # http://pragprog.com/pragdave/Tech/Blog/ToDos.rdoc
9
+ #
10
+ # He generously sent me his code, and I reimplemented it with vPim and rss/maker.
11
+ #
12
+ # RSS Content-Types:
13
+ #
14
+ # RSS 1.0 -> application/rdf+xml
15
+ # RSS 2.0 -> text/xml
16
+ # RSS 0.9 -> text/xml
17
+ # ATOM -> application/xml
18
+
19
+ require 'rss/maker'
20
+ require 'vpim/icalendar'
21
+
22
+ class IcalToRss
23
+ def initialize(calendars, title, link, language = 'en-us')
24
+ @rss = RSS::Maker.make("0.9") do |maker|
25
+ maker.channel.title = title
26
+ maker.channel.link = link
27
+ maker.channel.description = title
28
+ maker.channel.language = language
29
+
30
+ # These are required, or RSS::Maker silently returns nil!
31
+ maker.image.url = "maker.image.url"
32
+ maker.image.title = "maker.image.title"
33
+
34
+ calendars.each do |file|
35
+ Vpim::Icalendar.decode(File.open(file)).each do |cal|
36
+ cal.todos.each do |todo|
37
+ if !todo.status || todo.status.upcase != "COMPLETED"
38
+ item = maker.items.new_item
39
+ item.title = todo.summary
40
+ item.link = todo.properties['url'] || link
41
+ item.description = todo.description || todo.summary
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def to_rss
50
+ @rss.to_s
51
+ end
52
+ end
53
+
54
+ TITLE = "Sam's ToDo List"
55
+ LINK = "http://ensemble.local/~sam"
56
+
57
+ if ARGV[0] == "--print"
58
+
59
+ puts IcalToRss.new( Dir[ "/Users/sam/Library/Calendars/*.ics" ], TITLE, LINK ).to_rss
60
+
61
+ else
62
+
63
+ require 'webrick'
64
+
65
+ class IcalRssTodoServlet < WEBrick::HTTPServlet::AbstractServlet
66
+ def do_GET(req, resp)
67
+ resp.body = IcalToRss.new( Dir[ "/Users/sam/Library/Calendars/*.ics" ], TITLE, LINK ).to_rss
68
+ resp['content-type'] = 'text/xml'
69
+ raise WEBrick::HTTPStatus::OK
70
+ end
71
+ end
72
+
73
+ server = WEBrick::HTTPServer.new( :Port => 8080 )
74
+
75
+ server.mount( '/', IcalRssTodoServlet )
76
+
77
+ ['INT', 'TERM'].each { |signal|
78
+ trap(signal) { server.shutdown }
79
+ }
80
+
81
+ server.start
82
+
83
+ end
84
+
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $-w = true
4
+ $:.unshift File.dirname($0) + '/../lib'
5
+
6
+ require 'vpim/vcard'
7
+
8
+ ARGV.each do |file|
9
+
10
+ File.open(file).each do |line|
11
+
12
+ if line =~ /\s*alias\s+(\w+)\s+(.*)/
13
+ nick = $1
14
+ rhs = $2
15
+ email = nil
16
+ name = nil
17
+
18
+ case rhs
19
+ when /(.*)<(.*)>/
20
+ email = $2
21
+ name = $1
22
+ else
23
+ email = rhs
24
+ name = nick
25
+ nick = nil
26
+ end
27
+
28
+ card = Vpim::Vcard::Maker.make2 do |maker|
29
+ # don't have the broken-down name, we'll have to leave it blank
30
+ maker.name { |n| n.fullname = name }
31
+
32
+ # Set preferred, its the only one...
33
+ maker.add_email( email ) { |e| e.preferred = true }
34
+
35
+ maker.nickname = nick if nick
36
+
37
+ # mark as auto-generated, it makes it easier to see them
38
+ maker.add_field( Vpim::DirectoryInfo::Field.create('note', "auto-generated-from-mutt-aliases") )
39
+ end
40
+
41
+ puts card.to_s
42
+ end
43
+ end
44
+ end
45
+