scashin133-vpim 9.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/CHANGES +510 -0
  4. data/COPYING +58 -0
  5. data/LICENSE +20 -0
  6. data/Makefile +135 -0
  7. data/README +182 -0
  8. data/README.rdoc +17 -0
  9. data/Rakefile +54 -0
  10. data/THANKS +2 -0
  11. data/VERSION +1 -0
  12. data/ex_fmt_convert.rb +25 -0
  13. data/ex_ics_api.rb +54 -0
  14. data/lib/vpim/address.rb +219 -0
  15. data/lib/vpim/agent/atomize.rb +104 -0
  16. data/lib/vpim/agent/base.rb +73 -0
  17. data/lib/vpim/agent/calendars.rb +173 -0
  18. data/lib/vpim/agent/handler.rb +26 -0
  19. data/lib/vpim/agent/ics.rb +161 -0
  20. data/lib/vpim/attachment.rb +102 -0
  21. data/lib/vpim/date.rb +222 -0
  22. data/lib/vpim/dirinfo.rb +277 -0
  23. data/lib/vpim/duration.rb +119 -0
  24. data/lib/vpim/enumerator.rb +32 -0
  25. data/lib/vpim/field.rb +618 -0
  26. data/lib/vpim/icalendar.rb +384 -0
  27. data/lib/vpim/maker/vcard.rb +16 -0
  28. data/lib/vpim/property/base.rb +193 -0
  29. data/lib/vpim/property/common.rb +315 -0
  30. data/lib/vpim/property/location.rb +38 -0
  31. data/lib/vpim/property/priority.rb +43 -0
  32. data/lib/vpim/property/recurrence.rb +69 -0
  33. data/lib/vpim/property/resources.rb +24 -0
  34. data/lib/vpim/repo.rb +261 -0
  35. data/lib/vpim/rfc2425.rb +371 -0
  36. data/lib/vpim/rrule.rb +591 -0
  37. data/lib/vpim/time.rb +40 -0
  38. data/lib/vpim/vcard.rb +1426 -0
  39. data/lib/vpim/version.rb +19 -0
  40. data/lib/vpim/vevent.rb +187 -0
  41. data/lib/vpim/view.rb +90 -0
  42. data/lib/vpim/vjournal.rb +58 -0
  43. data/lib/vpim/vpim.rb +65 -0
  44. data/lib/vpim/vtodo.rb +103 -0
  45. data/lib/vpim.rb +13 -0
  46. data/mbox2vcard.rb +89 -0
  47. data/outline.sh +4 -0
  48. data/profile.rb +6 -0
  49. data/profile.txt +276 -0
  50. data/samples/README.mutt +93 -0
  51. data/samples/ab-query.rb +57 -0
  52. data/samples/agent.ru +10 -0
  53. data/samples/cmd-itip.rb +156 -0
  54. data/samples/ex_cpvcard.rb +55 -0
  55. data/samples/ex_get_vcard_photo.rb +22 -0
  56. data/samples/ex_mkv21vcard.rb +34 -0
  57. data/samples/ex_mkvcard.rb +64 -0
  58. data/samples/ex_mkyourown.rb +29 -0
  59. data/samples/ics-dump.rb +210 -0
  60. data/samples/ics-to-rss.rb +84 -0
  61. data/samples/mutt-aliases-to-vcf.rb +45 -0
  62. data/samples/osx-wrappers.rb +86 -0
  63. data/samples/reminder.rb +209 -0
  64. data/samples/rrule.rb +71 -0
  65. data/samples/tabbed-file-to-vcf.rb +390 -0
  66. data/samples/vcf-dump.rb +86 -0
  67. data/samples/vcf-lines.rb +61 -0
  68. data/samples/vcf-to-ics.rb +22 -0
  69. data/samples/vcf-to-mutt.rb +121 -0
  70. data/setup.rb +1585 -0
  71. data/stamp.rb +29 -0
  72. data/test/calendars/weather.calendar/Events/1205042405-0-0.ics +17 -0
  73. data/test/calendars/weather.calendar/Events/1205128857-1-1205128857.ics +21 -0
  74. data/test/calendars/weather.calendar/Events/1205215257-2--1884536782.ics +22 -0
  75. data/test/calendars/weather.calendar/Events/1205301657-3--679062325.ics +21 -0
  76. data/test/calendars/weather.calendar/Events/1205388057-4-526584932.ics +20 -0
  77. data/test/calendars/weather.calendar/Events/1205474457-5-1732404989.ics +21 -0
  78. data/test/calendars/weather.calendar/Events/1205560857-6--1356569450.ics +21 -0
  79. data/test/calendars/weather.calendar/Events/1205647257-7--150403793.ics +22 -0
  80. data/test/calendars/weather.calendar/Events/1205712057-8-1055761864.ics +20 -0
  81. data/test/calendars/weather.calendar/Info.plist +20 -0
  82. data/test/common.rb +51 -0
  83. data/test/test_agent_atomize.rb +84 -0
  84. data/test/test_agent_calendars.rb +128 -0
  85. data/test/test_agent_ics.rb +96 -0
  86. data/test/test_all.rb +14 -0
  87. data/test/test_date.rb +120 -0
  88. data/test/test_dur.rb +41 -0
  89. data/test/test_field.rb +156 -0
  90. data/test/test_ical.rb +437 -0
  91. data/test/test_misc.rb +13 -0
  92. data/test/test_repo.rb +129 -0
  93. data/test/test_rrule.rb +1030 -0
  94. data/test/test_vcard.rb +1017 -0
  95. data/test/test_view.rb +79 -0
  96. data/test/weekly.ics +40 -0
  97. data/vpim.gemspec +157 -0
  98. metadata +206 -0
@@ -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.occurrences.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 occurrences.
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
+
@@ -0,0 +1,86 @@
1
+ # OSX wrapper methods.
2
+ #
3
+ # The OSX classes are mirrored fairly directly into ruby by ruby/cocoa. Too
4
+ # directly for ease, this is a start at convenient ruby APIs on top of the low-level cocoa
5
+ # methods.
6
+
7
+ =begin
8
+
9
+ Ideas for things to add:
10
+
11
+ + each for the addressbook
12
+
13
+ + ABRecord#[] <- valueForProperty
14
+
15
+ + [] and each for NSCFArray (which is actually an instance of OCObject)
16
+
17
+ + [] and each for NSCFDictionary (which is actually an instance of OCObject)
18
+
19
+ + Can I add methods to OCObject, and have them implement themselves based on the the 'class'?
20
+
21
+ + ABMultiValue#[index]
22
+
23
+ if index
24
+
25
+ is a :token, then its the identifier,
26
+ is a string, its a label
27
+ is a number, its an array index
28
+
29
+ return a Struct, so you can do
30
+
31
+ mvalue["work"].value
32
+
33
+ =end
34
+
35
+ require 'osx/addressbook'
36
+
37
+ # put into osx/ocobject?
38
+ module OSX
39
+ # When an NSData object is returned by an objective/c API (such as
40
+ # ABPerson.vCardRepresentation, I actually get a OCObject back, who's class
41
+ # instance points to either a NSData, or a related class.
42
+ #
43
+ # This is a convenience method to get the NSData data back as a ruby string.
44
+ # Is it the right place to put this?
45
+ class OCObject
46
+ def bytes
47
+ s = ' ' * length
48
+ getBytes(s)
49
+ s
50
+ end
51
+ end
52
+ end
53
+
54
+ # put into osx/abperson?
55
+ module OSX
56
+ class ABPerson
57
+ def vCard
58
+ card = self.vCardRepresentation.bytes
59
+
60
+ # The card representation appears to be either ASCII, or UCS-2. If its
61
+ # UCS-2, then the first byte will be 0, so check for this, and convert
62
+ # if necessary.
63
+ #
64
+ # We know it's 0, because the first character in a vCard must be the 'B'
65
+ # of "BEGIN:VCARD", and in UCS-2 all ascii are encoded as a 0 byte
66
+ # followed by the ASCII byte, UNICODE is great.
67
+ if card[0] == 0
68
+ nsstring = OSX::NSString.alloc.initWithCharacters(card, :length, card.size/2)
69
+ card = nsstring.UTF8String
70
+
71
+ # TODO: is nsstring.UTF8String == nsstring.to_s ?
72
+ end
73
+ card
74
+ end
75
+ end
76
+ end
77
+
78
+ # put into osx/group?
79
+ module OSX
80
+ class ABGroup
81
+ def name
82
+ self.valueForProperty(OSX::kABGroupNameProperty).to_s
83
+ end
84
+ end
85
+ end
86
+