vpim 0.658 → 0.695
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.
- data/CHANGES +6 -0
- data/bin/reminder +17 -11
- data/bin/rrule +0 -0
- data/lib/vpim/agent/app.rb +194 -0
- data/lib/vpim/agent/atomize.rb +101 -0
- data/lib/vpim/agent/calendars.rb +173 -0
- data/lib/vpim/agent/main.rb +327 -0
- data/lib/vpim/agent/scraps.rb +292 -0
- data/lib/vpim/repo.rb +81 -1
- data/lib/vpim/rrule.rb +0 -0
- data/lib/vpim/version.rb +2 -2
- data/samples/cmd-itip.rb +0 -0
- data/samples/ics-dump.rb +0 -0
- data/samples/ics-to-rss.rb +0 -0
- data/samples/mutt-aliases-to-vcf.rb +0 -0
- data/samples/reminder.rb +17 -11
- data/samples/rrule.rb +0 -0
- data/samples/vcf-dump.rb +0 -0
- data/samples/vcf-lines.rb +0 -0
- data/samples/vcf-to-ics.rb +0 -0
- data/samples/vcf-to-mutt.rb +0 -0
- data/test/test_agent_app.rb +74 -0
- data/test/test_agent_atomize.rb +84 -0
- data/test/test_agent_calendars.rb +128 -0
- data/test/test_all.rb +1 -1
- data/test/test_date.rb +0 -0
- data/test/test_dur.rb +0 -0
- data/test/test_field.rb +0 -0
- data/test/test_ical.rb +22 -0
- data/test/test_repo.rb +44 -92
- data/test/test_rrule.rb +0 -0
- data/test/test_vcard.rb +0 -0
- metadata +14 -10
- data/lib/atom.rb +0 -728
- data/lib/atom/pub.rb +0 -206
- data/lib/atom/version.rb +0 -9
- data/lib/atom/xml/parser.rb +0 -305
- data/lib/plist.rb +0 -22
- data/lib/plist/generator.rb +0 -224
- data/lib/plist/parser.rb +0 -225
@@ -0,0 +1,327 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'pp'
|
3
|
+
require 'rss/maker'
|
4
|
+
require 'socket'
|
5
|
+
require 'webrick'
|
6
|
+
require 'vpim/icalendar'
|
7
|
+
require 'vpim/vcard'
|
8
|
+
|
9
|
+
#--------------------------------------------------------------------------------
|
10
|
+
# Load DNSSD support, if possible.
|
11
|
+
# TODO - should be in net/dns/dnssd
|
12
|
+
# FIXME - doesn't work with dnssd
|
13
|
+
@avoid_native = true
|
14
|
+
begin
|
15
|
+
if @avoid_native
|
16
|
+
raise LoadError
|
17
|
+
else
|
18
|
+
require 'dnssd'
|
19
|
+
end
|
20
|
+
rescue LoadError
|
21
|
+
begin
|
22
|
+
require 'net/dns/mdns-sd'
|
23
|
+
DNSSD = Net::DNS::MDNSSD
|
24
|
+
rescue LoadError
|
25
|
+
DNSSD = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#--------------------------------------------------------------------------------
|
30
|
+
# Set up server environment.
|
31
|
+
|
32
|
+
# Find the user's name and host.
|
33
|
+
|
34
|
+
require 'etc'
|
35
|
+
|
36
|
+
$user = Etc.getpwuid(Process.uid).gecos
|
37
|
+
$host = Socket.gethostname
|
38
|
+
|
39
|
+
$services = []
|
40
|
+
|
41
|
+
$stuff = []
|
42
|
+
|
43
|
+
def register(name, path, protocol = 'http')
|
44
|
+
# $services << DNSSD.register(name, '_http._tcp', 'local', $port, 'path' => path )
|
45
|
+
|
46
|
+
puts "register #{name.inspect} on path #{path.inspect} with #{protocol}"
|
47
|
+
|
48
|
+
$stuff << [name, path, protocol]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a HTTP server, possibly on a dynamic port.
|
52
|
+
# - Dynamic ports don't actually work well. While it is true we can advertise them,
|
53
|
+
# non-DNSSD aware clients will store the hostname and port when they subscribe to
|
54
|
+
# a calendar, but when the server restarts the port will be different. Not good.
|
55
|
+
$port = 8191
|
56
|
+
|
57
|
+
server = WEBrick::HTTPServer.new( :Port => $port )
|
58
|
+
|
59
|
+
if $port == 0
|
60
|
+
# Server may have created multiple listeners, all on a different dynamically
|
61
|
+
# assigned port.
|
62
|
+
families = Socket.getaddrinfo(nil, 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
|
63
|
+
|
64
|
+
listeners = []
|
65
|
+
|
66
|
+
families.each do |af, one, dns, addr|
|
67
|
+
listeners << TCPServer.new(addr, $port)
|
68
|
+
$port = listeners.first.addr[1] unless $port != 0
|
69
|
+
end
|
70
|
+
|
71
|
+
listeners.each do |s|
|
72
|
+
puts "listen on #{s.addr.inspect}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# So we replace them with our TCPServer sockets which are all on the same
|
76
|
+
# (dynamically assigned) port.
|
77
|
+
server.listeners.each do |s| s.close end
|
78
|
+
server.listeners.replace listeners
|
79
|
+
server.config[:Port] = $port
|
80
|
+
end
|
81
|
+
|
82
|
+
server.config[:MimeTypes]['ics'] = 'text/calendar'
|
83
|
+
server.config[:MimeTypes]['vcf'] = 'text/directory'
|
84
|
+
|
85
|
+
#--------------------------------------------------------------------------------
|
86
|
+
# Mount services
|
87
|
+
|
88
|
+
##### Vcard Birthdays as iCalendar
|
89
|
+
=begin
|
90
|
+
$vcf_bday_file = 'vpim-bday.vcf'
|
91
|
+
$vcf_bday_path = '/vcf/bday.ics'
|
92
|
+
|
93
|
+
class VcfBdayIcsServlet < WEBrick::HTTPServlet::AbstractServlet
|
94
|
+
def do_GET(req, resp)
|
95
|
+
cal = Vpim::Icalendar.create
|
96
|
+
|
97
|
+
open($vcf_bday_file) do |vcf|
|
98
|
+
Vpim::Vcard.decode(vcf).each do |card|
|
99
|
+
begin
|
100
|
+
bday = card.birthday
|
101
|
+
if bday
|
102
|
+
cal.push Vpim::Icalendar::Vevent.create_yearly(
|
103
|
+
card.birthday,
|
104
|
+
"Birthday for #{card['fn'].strip}"
|
105
|
+
)
|
106
|
+
$stderr.puts "#{card['fn']} -> bday #{cal.events.last.dtstart}"
|
107
|
+
end
|
108
|
+
rescue
|
109
|
+
$stderr.puts $!
|
110
|
+
$stderr.puts $!.backtrace.join("\n")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
resp.body = cal.encode
|
116
|
+
resp['content-type'] = 'text/calendar'
|
117
|
+
|
118
|
+
# Is this necessary, or is it default?
|
119
|
+
raise WEBrick::HTTPStatus::OK
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
server.mount( $vcf_bday_path, VcfBdayIcsServlet )
|
124
|
+
|
125
|
+
register( "Calendar for all the Birthdays in my vCards", $vcf_bday_path, 'webcal' )
|
126
|
+
=end
|
127
|
+
|
128
|
+
##### iCalendar as calendars
|
129
|
+
# Export local calendars two different ways
|
130
|
+
=begin
|
131
|
+
$ical_folder = File.expand_path( "~/Library/Calendars" )
|
132
|
+
|
133
|
+
#
|
134
|
+
# Here we write a servlet to display all the allowed calendars with
|
135
|
+
# webcal links, so they open in iCal.
|
136
|
+
#
|
137
|
+
$ical_include = /^[A-Z]/
|
138
|
+
$ical_title = "My Calendars"
|
139
|
+
|
140
|
+
class IcalIcsServlet < WEBrick::HTTPServlet::AbstractServlet
|
141
|
+
def do_GET(req, resp)
|
142
|
+
body = ''
|
143
|
+
# body << @options.inspect
|
144
|
+
|
145
|
+
folder, include, exclude = *@options
|
146
|
+
|
147
|
+
path = req.path_info
|
148
|
+
|
149
|
+
# body << "\n"
|
150
|
+
# body << "path=#{path.inspect}\n"
|
151
|
+
|
152
|
+
all = Dir.entries(folder).select do |f| f =~ /\.ics$/ end
|
153
|
+
|
154
|
+
if include
|
155
|
+
all.reject! do |f| !(f =~ include) end
|
156
|
+
end
|
157
|
+
if exclude
|
158
|
+
all.reject! do |f| f =~ exclude end
|
159
|
+
end
|
160
|
+
|
161
|
+
# body << "#{all.inspect}\n"
|
162
|
+
|
163
|
+
if(path == '')
|
164
|
+
body << "<ul>\n"
|
165
|
+
all.each do |f|
|
166
|
+
n = f.sub('.ics', '')
|
167
|
+
body << "<li><a href=\"webcal://#{$host}:#{$port}/calfile/#{f}\">#{n}</a>\n"
|
168
|
+
end
|
169
|
+
body << "</ul>\n"
|
170
|
+
end
|
171
|
+
|
172
|
+
resp.body = body
|
173
|
+
resp['content-type'] = 'text/html'
|
174
|
+
raise WEBrick::HTTPStatus::OK
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
server.mount( '/calweb', IcalIcsServlet, $ical_folder, $ical_include)
|
179
|
+
|
180
|
+
register( "My Calendars as webcal:// Feeds", '/calweb' )
|
181
|
+
|
182
|
+
#
|
183
|
+
# We use the WEBrick file servlet to actually serve calendar files.
|
184
|
+
# FIXME - this means that if you guess someone's calendar name, you can
|
185
|
+
# download it, despite the rudimentary security.
|
186
|
+
#
|
187
|
+
server.mount( '/calfile', WEBrick::HTTPServlet::FileHandler, $ical_folder, :FancyIndexing=>true )
|
188
|
+
|
189
|
+
# For debugging...
|
190
|
+
register( 'My Calendar Folder', '/calfile' )
|
191
|
+
|
192
|
+
=end
|
193
|
+
|
194
|
+
##### iCalendar/todo as RSS
|
195
|
+
=begin
|
196
|
+
$ics_todo_title = 'My Todo Items as an RSS Feed'
|
197
|
+
$ics_todo_path = "/ics/todo.rss"
|
198
|
+
|
199
|
+
class IcalTodoRssServlet < WEBrick::HTTPServlet::AbstractServlet
|
200
|
+
def do_GET(req, resp)
|
201
|
+
rss = RSS::Maker.make("0.9") do |maker|
|
202
|
+
title = $ics_todo_title
|
203
|
+
link = 'http:///'
|
204
|
+
maker.channel.title = title
|
205
|
+
maker.channel.link = link
|
206
|
+
maker.channel.description = title
|
207
|
+
maker.channel.language = 'en-us'
|
208
|
+
|
209
|
+
# These are required, or RSS::Maker silently returns nil!
|
210
|
+
maker.image.url = "maker.image.url"
|
211
|
+
maker.image.title = "maker.image.title"
|
212
|
+
|
213
|
+
Dir[ $ical_folder + "/*.ics" ].each do |file|
|
214
|
+
# TODO: use the open with a block variant
|
215
|
+
Vpim::Icalendar.decode(File.open(file)).each do |cal|
|
216
|
+
cal.todos.each do |todo|
|
217
|
+
if !todo.status || todo.status.upcase != "COMPLETED"
|
218
|
+
item = maker.items.new_item
|
219
|
+
item.title = todo.summary
|
220
|
+
item.link = todo.properties['url'] || link
|
221
|
+
item.description = todo.description || todo.summary
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
resp.body = rss.to_s
|
229
|
+
resp['content-type'] = 'text/xml'
|
230
|
+
|
231
|
+
raise WEBrick::HTTPStatus::OK
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
server.mount( $ics_todo_path, IcalTodoRssServlet )
|
236
|
+
|
237
|
+
register( $ics_todo_title, $ics_todo_path )
|
238
|
+
=end
|
239
|
+
|
240
|
+
##### Local calendars
|
241
|
+
|
242
|
+
require "vpim/agent/calendars"
|
243
|
+
|
244
|
+
$ical_folder = File.expand_path( "~/Library/Calendars" )
|
245
|
+
|
246
|
+
class CalendarsServlet < WEBrick::HTTPServlet::AbstractServlet
|
247
|
+
def do_GET(req, resp)
|
248
|
+
body = ''
|
249
|
+
# body << @options.inspect
|
250
|
+
|
251
|
+
folder = *@options
|
252
|
+
|
253
|
+
# TODO Should be longer lived
|
254
|
+
repo = Vpim::Repo::Apple3.new($ical_folder)
|
255
|
+
rest = Vpim::Agent::Calendars.new(repo)
|
256
|
+
path = Vpim::Agent::Path.new(req.request_uri, req.path)
|
257
|
+
|
258
|
+
begin
|
259
|
+
body, form = rest.get(path)
|
260
|
+
status = 200
|
261
|
+
rescue Vpim::Agent::NotFound
|
262
|
+
body = $!.to_s
|
263
|
+
form = "text/plain" # should be HTML!
|
264
|
+
status = 404
|
265
|
+
end
|
266
|
+
|
267
|
+
resp.status = status
|
268
|
+
resp.body = body
|
269
|
+
resp['content-type'] = form
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
server.mount( '/calendars', CalendarsServlet, $ical_folder )
|
274
|
+
|
275
|
+
register( "Calendars", '/calendars' )
|
276
|
+
|
277
|
+
#--------------------------------------------------------------------------------
|
278
|
+
## Top-level page.
|
279
|
+
|
280
|
+
$vpim_title = "vAgent for #{$user}"
|
281
|
+
|
282
|
+
class VpimServlet < WEBrick::HTTPServlet::AbstractServlet
|
283
|
+
def do_GET(req, resp)
|
284
|
+
body = <<"EOF"
|
285
|
+
<h1>#{$vpim_title}</h1>
|
286
|
+
|
287
|
+
See:
|
288
|
+
<ul>
|
289
|
+
EOF
|
290
|
+
|
291
|
+
$stuff.each do |name,path,protocol|
|
292
|
+
#body << "<li><a href=\"#{protocol}://#{$host}:#{$port}#{path}\">#{name}</a>\n"
|
293
|
+
#
|
294
|
+
# $host is wrong, it lacks the domain - we should derive this from how
|
295
|
+
# we are called, or just leave it out.
|
296
|
+
body << "<li><a href=\"#{path}\">#{name}</a>\n"
|
297
|
+
end
|
298
|
+
|
299
|
+
body << "</ul>\n"
|
300
|
+
|
301
|
+
resp.body = body
|
302
|
+
resp['content-type'] = 'text/html'
|
303
|
+
raise WEBrick::HTTPStatus::OK
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
server.mount( '/', VpimServlet )
|
308
|
+
|
309
|
+
#--------------------------------------------------------------------------------
|
310
|
+
# Run server
|
311
|
+
|
312
|
+
if DNSSD
|
313
|
+
$services << DNSSD.register($vpim_title, '_http._tcp', 'local', $port, 'path' => '/' )
|
314
|
+
end
|
315
|
+
|
316
|
+
['INT', 'TERM'].each do |signal|
|
317
|
+
trap(signal) do
|
318
|
+
server.shutdown
|
319
|
+
$services.each do |s|
|
320
|
+
s.stop
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
server.start
|
326
|
+
|
327
|
+
|
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'rss/maker'
|
3
|
+
require 'socket'
|
4
|
+
require 'webrick'
|
5
|
+
require 'vpim/icalendar'
|
6
|
+
require 'vpim/vcard'
|
7
|
+
|
8
|
+
# Notes on debugging with dnssd API: check system.log, it should give info.
|
9
|
+
|
10
|
+
#require 'pp'
|
11
|
+
#module Kernel
|
12
|
+
# def to_pp
|
13
|
+
# s = PP.pp(self, '')
|
14
|
+
# s.chomp!
|
15
|
+
# s
|
16
|
+
# end
|
17
|
+
#end
|
18
|
+
|
19
|
+
|
20
|
+
#--------------------------------------------------------------------------------
|
21
|
+
# Load DNSSD support, if possible.
|
22
|
+
# TODO - should be in net/dns/dnssd
|
23
|
+
# FIXME - doesn't work with dnssd
|
24
|
+
@avoid_native = true
|
25
|
+
begin
|
26
|
+
if @avoid_native
|
27
|
+
raise LoadError
|
28
|
+
else
|
29
|
+
require 'dnssd'
|
30
|
+
end
|
31
|
+
rescue LoadError
|
32
|
+
begin
|
33
|
+
require 'net/dns/mdns-sd'
|
34
|
+
DNSSD = Net::DNS::MDNSSD
|
35
|
+
rescue LoadError
|
36
|
+
DNSSD = nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#--------------------------------------------------------------------------------
|
41
|
+
# Set up server environment.
|
42
|
+
|
43
|
+
# Find the user's name and host.
|
44
|
+
|
45
|
+
require 'etc'
|
46
|
+
|
47
|
+
$user = Etc.getpwuid(Process.uid).gecos
|
48
|
+
$host = Socket.gethostname
|
49
|
+
|
50
|
+
$services = []
|
51
|
+
|
52
|
+
$stuff = []
|
53
|
+
|
54
|
+
def register(name, path, protocol = 'http')
|
55
|
+
# $services << DNSSD.register(name, '_http._tcp', 'local', $port, 'path' => path )
|
56
|
+
|
57
|
+
puts "register #{name.inspect} on path #{path.inspect} with #{protocol}"
|
58
|
+
|
59
|
+
$stuff << [name, path, protocol]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create a HTTP server, possibly on a dynamic port.
|
63
|
+
# - Dynamic ports don't actually work well. While it is true we can advertise them,
|
64
|
+
# non-DNSSD aware clients will store the hostname and port when they subscribe to
|
65
|
+
# a calendar, but when the server restarts the port will be different. Not good.
|
66
|
+
$port = 8191
|
67
|
+
|
68
|
+
server = WEBrick::HTTPServer.new( :Port => $port )
|
69
|
+
|
70
|
+
if $port == 0
|
71
|
+
# Server may have created multiple listeners, all on a different dynamically
|
72
|
+
# assigned port.
|
73
|
+
families = Socket.getaddrinfo(nil, 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
|
74
|
+
|
75
|
+
listeners = []
|
76
|
+
|
77
|
+
families.each do |af, one, dns, addr|
|
78
|
+
listeners << TCPServer.new(addr, $port)
|
79
|
+
$port = listeners.first.addr[1] unless $port != 0
|
80
|
+
end
|
81
|
+
|
82
|
+
listeners.each do |s|
|
83
|
+
puts "listen on #{s.addr.inspect}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# So we replace them with our TCPServer sockets which are all on the same
|
87
|
+
# (dynamically assigned) port.
|
88
|
+
server.listeners.each do |s| s.close end
|
89
|
+
server.listeners.replace listeners
|
90
|
+
server.config[:Port] = $port
|
91
|
+
end
|
92
|
+
|
93
|
+
server.config[:MimeTypes]['ics'] = 'text/calendar'
|
94
|
+
server.config[:MimeTypes]['vcf'] = 'text/directory'
|
95
|
+
|
96
|
+
#--------------------------------------------------------------------------------
|
97
|
+
# Mount services
|
98
|
+
|
99
|
+
##### Vcard Birthdays as iCalendar
|
100
|
+
|
101
|
+
$vcf_bday_file = 'vpim-bday.vcf'
|
102
|
+
$vcf_bday_path = '/vcf/bday.ics'
|
103
|
+
|
104
|
+
class VcfBdayIcsServlet < WEBrick::HTTPServlet::AbstractServlet
|
105
|
+
def do_GET(req, resp)
|
106
|
+
cal = Vpim::Icalendar.create
|
107
|
+
|
108
|
+
open($vcf_bday_file) do |vcf|
|
109
|
+
Vpim::Vcard.decode(vcf).each do |card|
|
110
|
+
begin
|
111
|
+
bday = card.birthday
|
112
|
+
if bday
|
113
|
+
cal.push Vpim::Icalendar::Vevent.create_yearly(
|
114
|
+
card.birthday,
|
115
|
+
"Birthday for #{card['fn'].strip}"
|
116
|
+
)
|
117
|
+
$stderr.puts "#{card['fn']} -> bday #{cal.events.last.dtstart}"
|
118
|
+
end
|
119
|
+
rescue
|
120
|
+
$stderr.puts $!
|
121
|
+
$stderr.puts $!.backtrace.join("\n")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
resp.body = cal.encode
|
127
|
+
resp['content-type'] = 'text/calendar'
|
128
|
+
|
129
|
+
# Is this necessary, or is it default?
|
130
|
+
raise WEBrick::HTTPStatus::OK
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
server.mount( $vcf_bday_path, VcfBdayIcsServlet )
|
135
|
+
|
136
|
+
register( "Calendar for all the Birthdays in my vCards", $vcf_bday_path, 'webcal' )
|
137
|
+
|
138
|
+
##### iCalendar as calendars
|
139
|
+
# Export local calendars two different ways
|
140
|
+
|
141
|
+
$ical_folder = File.expand_path( "~/Library/Calendars" )
|
142
|
+
|
143
|
+
#
|
144
|
+
# Here we write a servlet to display all the allowed calendars with
|
145
|
+
# webcal links, so they open in iCal.
|
146
|
+
#
|
147
|
+
$ical_include = /^[A-Z]/
|
148
|
+
$ical_title = "My Calendars"
|
149
|
+
|
150
|
+
class IcalIcsServlet < WEBrick::HTTPServlet::AbstractServlet
|
151
|
+
def do_GET(req, resp)
|
152
|
+
body = ''
|
153
|
+
# body << @options.inspect
|
154
|
+
|
155
|
+
folder, include, exclude = *@options
|
156
|
+
|
157
|
+
path = req.path_info
|
158
|
+
|
159
|
+
# body << "\n"
|
160
|
+
# body << "path=#{path.inspect}\n"
|
161
|
+
|
162
|
+
all = Dir.entries(folder).select do |f| f =~ /\.ics$/ end
|
163
|
+
|
164
|
+
if include
|
165
|
+
all.reject! do |f| !(f =~ include) end
|
166
|
+
end
|
167
|
+
if exclude
|
168
|
+
all.reject! do |f| f =~ exclude end
|
169
|
+
end
|
170
|
+
|
171
|
+
# body << "#{all.inspect}\n"
|
172
|
+
|
173
|
+
if(path == '')
|
174
|
+
body << "<ul>\n"
|
175
|
+
all.each do |f|
|
176
|
+
n = f.sub('.ics', '')
|
177
|
+
body << "<li><a href=\"webcal://#{$host}:#{$port}/calfile/#{f}\">#{n}</a>\n"
|
178
|
+
end
|
179
|
+
body << "</ul>\n"
|
180
|
+
end
|
181
|
+
|
182
|
+
resp.body = body
|
183
|
+
resp['content-type'] = 'text/html'
|
184
|
+
raise WEBrick::HTTPStatus::OK
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
server.mount( '/calweb', IcalIcsServlet, $ical_folder, $ical_include)
|
189
|
+
|
190
|
+
register( "My Calendars as webcal:// Feeds", '/calweb' )
|
191
|
+
|
192
|
+
#
|
193
|
+
# We use the WEBrick file servlet to actually serve calendar files.
|
194
|
+
# FIXME - this means that if you guess someone's calendar name, you can
|
195
|
+
# download it, despite the rudimentary security.
|
196
|
+
#
|
197
|
+
server.mount( '/calfile', WEBrick::HTTPServlet::FileHandler, $ical_folder, :FancyIndexing=>true )
|
198
|
+
|
199
|
+
# For debugging...
|
200
|
+
register( 'My Calendar Folder', '/calfile' )
|
201
|
+
|
202
|
+
##### iCalendar/todo as RSS
|
203
|
+
|
204
|
+
$ics_todo_title = 'My Todo Items as an RSS Feed'
|
205
|
+
$ics_todo_path = "/ics/todo.rss"
|
206
|
+
|
207
|
+
class IcalTodoRssServlet < WEBrick::HTTPServlet::AbstractServlet
|
208
|
+
def do_GET(req, resp)
|
209
|
+
rss = RSS::Maker.make("0.9") do |maker|
|
210
|
+
title = $ics_todo_title
|
211
|
+
link = 'http:///'
|
212
|
+
maker.channel.title = title
|
213
|
+
maker.channel.link = link
|
214
|
+
maker.channel.description = title
|
215
|
+
maker.channel.language = 'en-us'
|
216
|
+
|
217
|
+
# These are required, or RSS::Maker silently returns nil!
|
218
|
+
maker.image.url = "maker.image.url"
|
219
|
+
maker.image.title = "maker.image.title"
|
220
|
+
|
221
|
+
Dir[ $ical_folder + "/*.ics" ].each do |file|
|
222
|
+
# TODO: use the open with a block variant
|
223
|
+
Vpim::Icalendar.decode(File.open(file)).each do |cal|
|
224
|
+
cal.todos.each do |todo|
|
225
|
+
if !todo.status || todo.status.upcase != "COMPLETED"
|
226
|
+
item = maker.items.new_item
|
227
|
+
item.title = todo.summary
|
228
|
+
item.link = todo.properties['url'] || link
|
229
|
+
item.description = todo.description || todo.summary
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
resp.body = rss.to_s
|
237
|
+
resp['content-type'] = 'text/xml'
|
238
|
+
|
239
|
+
raise WEBrick::HTTPStatus::OK
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
server.mount( $ics_todo_path, IcalTodoRssServlet )
|
244
|
+
|
245
|
+
register( $ics_todo_title, $ics_todo_path )
|
246
|
+
|
247
|
+
|
248
|
+
#--------------------------------------------------------------------------------
|
249
|
+
## Top-level page.
|
250
|
+
|
251
|
+
$vpim_title = "vPim for #{$user}"
|
252
|
+
|
253
|
+
class VpimServlet < WEBrick::HTTPServlet::AbstractServlet
|
254
|
+
def do_GET(req, resp)
|
255
|
+
body = <<"EOF"
|
256
|
+
<h1>#{$vpim_title}</h1>
|
257
|
+
|
258
|
+
You can access:
|
259
|
+
<ul>
|
260
|
+
EOF
|
261
|
+
|
262
|
+
$stuff.each do |name,path,protocol|
|
263
|
+
body << "<li><a href=\"#{protocol}://#{$host}:#{$port}#{path}\">#{name}</a>\n"
|
264
|
+
end
|
265
|
+
|
266
|
+
body << "</ul>\n"
|
267
|
+
|
268
|
+
resp.body = body
|
269
|
+
resp['content-type'] = 'text/html'
|
270
|
+
raise WEBrick::HTTPStatus::OK
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
server.mount( '/', VpimServlet )
|
275
|
+
|
276
|
+
#--------------------------------------------------------------------------------
|
277
|
+
# Run server
|
278
|
+
|
279
|
+
$services << DNSSD.register($vpim_title, '_http._tcp', 'local', $port, 'path' => '/' )
|
280
|
+
|
281
|
+
['INT', 'TERM'].each do |signal|
|
282
|
+
trap(signal) do
|
283
|
+
server.shutdown
|
284
|
+
$services.each do |s|
|
285
|
+
s.stop
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
server.start
|
291
|
+
|
292
|
+
|