mumboe-vpim 0.7

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.
Files changed (69) hide show
  1. data/CHANGES +510 -0
  2. data/COPYING +58 -0
  3. data/README +185 -0
  4. data/lib/vpim/address.rb +219 -0
  5. data/lib/vpim/agent/atomize.rb +104 -0
  6. data/lib/vpim/agent/base.rb +73 -0
  7. data/lib/vpim/agent/calendars.rb +173 -0
  8. data/lib/vpim/agent/handler.rb +26 -0
  9. data/lib/vpim/agent/ics.rb +161 -0
  10. data/lib/vpim/attachment.rb +102 -0
  11. data/lib/vpim/date.rb +222 -0
  12. data/lib/vpim/dirinfo.rb +277 -0
  13. data/lib/vpim/duration.rb +119 -0
  14. data/lib/vpim/enumerator.rb +32 -0
  15. data/lib/vpim/field.rb +614 -0
  16. data/lib/vpim/icalendar.rb +384 -0
  17. data/lib/vpim/maker/vcard.rb +16 -0
  18. data/lib/vpim/property/base.rb +193 -0
  19. data/lib/vpim/property/common.rb +315 -0
  20. data/lib/vpim/property/location.rb +38 -0
  21. data/lib/vpim/property/priority.rb +43 -0
  22. data/lib/vpim/property/recurrence.rb +69 -0
  23. data/lib/vpim/property/resources.rb +24 -0
  24. data/lib/vpim/repo.rb +261 -0
  25. data/lib/vpim/rfc2425.rb +367 -0
  26. data/lib/vpim/rrule.rb +591 -0
  27. data/lib/vpim/time.rb +40 -0
  28. data/lib/vpim/vcard.rb +1456 -0
  29. data/lib/vpim/version.rb +18 -0
  30. data/lib/vpim/vevent.rb +187 -0
  31. data/lib/vpim/view.rb +90 -0
  32. data/lib/vpim/vjournal.rb +58 -0
  33. data/lib/vpim/vpim.rb +65 -0
  34. data/lib/vpim/vtodo.rb +103 -0
  35. data/lib/vpim.rb +13 -0
  36. data/samples/README.mutt +93 -0
  37. data/samples/ab-query.rb +57 -0
  38. data/samples/agent.ru +10 -0
  39. data/samples/cmd-itip.rb +156 -0
  40. data/samples/ex_cpvcard.rb +55 -0
  41. data/samples/ex_get_vcard_photo.rb +22 -0
  42. data/samples/ex_mkv21vcard.rb +34 -0
  43. data/samples/ex_mkvcard.rb +64 -0
  44. data/samples/ex_mkyourown.rb +29 -0
  45. data/samples/ics-dump.rb +210 -0
  46. data/samples/ics-to-rss.rb +84 -0
  47. data/samples/mutt-aliases-to-vcf.rb +45 -0
  48. data/samples/osx-wrappers.rb +86 -0
  49. data/samples/reminder.rb +209 -0
  50. data/samples/rrule.rb +71 -0
  51. data/samples/tabbed-file-to-vcf.rb +390 -0
  52. data/samples/vcf-dump.rb +86 -0
  53. data/samples/vcf-lines.rb +61 -0
  54. data/samples/vcf-to-ics.rb +22 -0
  55. data/samples/vcf-to-mutt.rb +121 -0
  56. data/test/test_agent_atomize.rb +84 -0
  57. data/test/test_agent_calendars.rb +128 -0
  58. data/test/test_agent_ics.rb +96 -0
  59. data/test/test_all.rb +17 -0
  60. data/test/test_date.rb +120 -0
  61. data/test/test_dur.rb +41 -0
  62. data/test/test_field.rb +156 -0
  63. data/test/test_ical.rb +437 -0
  64. data/test/test_misc.rb +13 -0
  65. data/test/test_repo.rb +129 -0
  66. data/test/test_rrule.rb +1030 -0
  67. data/test/test_vcard.rb +973 -0
  68. data/test/test_view.rb +79 -0
  69. metadata +140 -0
data/README ADDED
@@ -0,0 +1,185 @@
1
+ Author:: Sam Roberts <vieuxtech@gmail.com>
2
+ Copyright:: Copyright (C) 2008 Sam Roberts
3
+ License:: May be distributed under the same terms as Ruby
4
+ Homepage:: http://vpim.rubyforge.org
5
+ Download:: http://rubyforge.org/projects/vpim
6
+ Install:: sudo gem install vpim
7
+
8
+ vPim provides calendaring, scheduling, and contact support for Ruby through the
9
+ standard iCalendar and vCard data formats for "personal information" exchange.
10
+
11
+ = Mumboe
12
+ This code has been modified by Mumboe, Inc. for purposes of Ruby 1.9 compatibility and adding some useful methods for vCard 3.0 specs
13
+
14
+ = Thanks
15
+
16
+ - http://ZipDX.com: for sponsoring development of FREQ=weekly and BYSETPOS in
17
+ recurrence rules.
18
+ - http://RubyForge.org: for their generous hosting of this project.
19
+
20
+ = Installation
21
+
22
+ There is a vPim package installable using ruby-gems:
23
+
24
+ # sudo gem install vpim (may require root privilege)
25
+
26
+ It is also installable in the standard way. Untar the package, and do:
27
+
28
+ $ ruby setup.rb --help
29
+
30
+ or do:
31
+
32
+ $ ruby setup.rb config
33
+ $ ruby setup.rb setup
34
+ # ruby setup.rb install (may require root privilege)
35
+
36
+ = Overview
37
+
38
+ vCard (RFC 2426) is a format for personal information, see Vpim::Vcard and
39
+ Vpim::Maker::Vcard.
40
+
41
+ iCalendar (RFC 2445) is a format for calendar related information, see
42
+ Vpim::Icalendar.
43
+
44
+ vCard and iCalendar support is built on top of an implementation of the MIME
45
+ Content-Type for Directory Information (RFC 2425). The basic RFC 2425 format is
46
+ implemented by Vpim::DirectoryInfo and Vpim::DirectoryInfo::Field.
47
+
48
+ The libary is quite useful, but improvements are ongoing. If you find
49
+ something missing or have suggestions, please contact me. I can't promise
50
+ instantaneous turnaround, but I might be able to suggest another approach, and
51
+ features requested by users of vPim go to the top of the todo list. If you need
52
+ a feature for a commercial project, consider sponsoring development.
53
+
54
+ = Examples
55
+
56
+ Here's an example to give a sense for how iCalendars are encoded and decoded:
57
+
58
+ require 'vpim/icalendar'
59
+
60
+ cal = Vpim::Icalendar.create2
61
+
62
+ cal.add_event do |e|
63
+ e.dtstart Date.new(2005, 04, 28)
64
+ e.dtend Date.new(2005, 04, 29)
65
+ e.summary "Monthly meet-the-CEO day"
66
+ e.description <<'---'
67
+ Unlike last one, this meeting will change your life because
68
+ we are going to discuss your likely demotion if your work isn't
69
+ done soon.
70
+ ---
71
+ e.categories [ 'APPOINTMENT' ]
72
+ e.categories do |c| c.push 'EDUCATION' end
73
+ e.url 'http://www.example.com'
74
+ e.sequence 0
75
+ e.access_class "PRIVATE"
76
+ e.transparency 'OPAQUE'
77
+
78
+ now = Time.now
79
+ e.created now
80
+ e.lastmod now
81
+
82
+
83
+ e.organizer do |o|
84
+ o.cn = "Example Organizer, Mr."
85
+ o.uri = "mailto:organizer@example.com"
86
+ end
87
+
88
+ attendee = Vpim::Icalendar::Address.create("mailto:attendee@example.com")
89
+ attendee.rsvp = true
90
+ e.add_attendee attendee
91
+ end
92
+
93
+ icsfile = cal.encode
94
+
95
+ puts '--- Encode:'
96
+
97
+ puts icsfile
98
+
99
+ puts '--- Decode:'
100
+
101
+ cal = Vpim::Icalendar.decode(icsfile).first
102
+
103
+ cal.components do |e|
104
+ puts e.summary
105
+ puts e.description
106
+ puts e.dtstart.to_s
107
+ puts e.dtend.to_s
108
+ end
109
+
110
+ This produces:
111
+
112
+ --- Encode:
113
+ BEGIN:VCALENDAR
114
+ VERSION:2.0
115
+ PRODID:-//Ensemble Independent//vPim 0.357//EN
116
+ CALSCALE:Gregorian
117
+ BEGIN:VEVENT
118
+ DTSTART;VALUE=DATE:20050428
119
+ DTEND;VALUE=DATE:20050429
120
+ SUMMARY:Monthly meet-the-CEO day
121
+ DESCRIPTION:Unlike last one, this meeting will change your life because\nwe
122
+ are going to discuss your likely demotion if your work isn't\ndone soon.\n
123
+ CATEGORIES:APPOINTMENT,EDUCATION
124
+ URL:http://www.example.com
125
+ SEQUENCE:0
126
+ CLASS:PRIVATE
127
+ CREATED:20060402T231755
128
+ LAST-MODIFIED:20060402T231755
129
+ ORGANIZER;CN="Example Organizer, Mr.":mailto:organizer@example.com
130
+ ATTENDEE;RSVP=true:mailto:attendee@example.com
131
+ END:VEVENT
132
+ END:VCALENDAR
133
+ --- Decode:
134
+ Monthly meet-the-CEO day
135
+ Unlike last one, this meeting will change your life because
136
+ we are going to discuss your likely demotion if your work isn't
137
+ done soon.
138
+ Thu Apr 28 00:00:00 UTC 2005
139
+ Fri Apr 29 00:00:00 UTC 2005
140
+
141
+
142
+ More examples of using vPim are provided in samples/.
143
+
144
+ vCard examples are:
145
+ - link:ex_mkvcard.txt: example of creating a vCard
146
+ - link:ex_cpvcard.txt: example of copying and them modifying a vCard
147
+ - link:ex_mkv21vcard.txt: example of creating version 2.1 vCard
148
+ - link:mutt-aliases-to-vcf.txt: convert a mutt aliases file to vCards
149
+ - link:ex_get_vcard_photo.txt: pull photo data from a vCard
150
+ - link:ab-query.txt: query the OS X Address Book to find vCards
151
+ - link:vcf-to-mutt.txt: query vCards for matches, output in formats useful
152
+ with Mutt (see link:README.mutt for details)
153
+ - link:tabbed-file-to-vcf.txt: convert a tab-delimited file to vCards, a
154
+ (small but) complete application contributed by Dane G. Avilla, thanks!
155
+ - link:vcf-to-ics.txt: example of how to create calendars of birthdays from vCards
156
+ - link:vcf-dump.txt: utility for dumping contents of .vcf files
157
+
158
+ iCalendar examples are:
159
+ - link:ics-to-rss.txt: prints todos as RSS, or starts a WEBrick servlet
160
+ that publishes todos as a RSS feed. Thanks to Dave Thomas for this idea,
161
+ from http://pragprog.com/pragdave/Tech/Blog/ToDos.rdoc.
162
+ - link:cmd-itip.txt: prints emailed iCalendar invitations in human-readable
163
+ form, and see link:README.mutt for instruction on mutt integration. I get
164
+ a lot of meeting invitations from Lotus Notes/Domino users at work, and
165
+ this is pretty useful in figuring out where and when I am supposed to be.
166
+ - link:reminder.txt: prints upcoming events and todos, by default from
167
+ Apple's iCal calendars
168
+ - link:rrule.txt: utility for printing recurrence rules
169
+ - link:ics-dump.txt: utility for dumping contents of .ics files
170
+
171
+ = Project Information
172
+
173
+ vPim can be downloaded from the Ruby Forge project page:
174
+
175
+ - http://rubyforge.org/projects/vpim
176
+
177
+ or installed as a gem:
178
+
179
+ - sudo gem install vpim
180
+
181
+ For notifications about new releases, or to ask questions about vPim, please
182
+ subscribe to "vpim-talk":
183
+
184
+ - http://rubyforge.org/mailman/listinfo/vpim-talk
185
+
@@ -0,0 +1,219 @@
1
+ =begin
2
+ Copyright (C) 2008 Sam Roberts
3
+
4
+ This library is free software; you can redistribute it and/or modify it
5
+ under the same terms as the ruby language itself, see the file COPYING for
6
+ details.
7
+ =end
8
+
9
+ =begin
10
+
11
+ Notes on a CAL-ADDRESS
12
+
13
+ When used with ATTENDEE, the parameters are:
14
+ CN
15
+ CUTYPE
16
+ DELEGATED-FROM
17
+ DELEGATED-TO
18
+ DIR
19
+ LANGUAGE
20
+ MEMBER
21
+ PARTSTAT
22
+ ROLE
23
+ RSVP
24
+ SENT-BY
25
+
26
+ When used with ORGANIZER, the parameters are:
27
+ CN
28
+ DIR
29
+ LANGUAGE
30
+ SENT-BY
31
+
32
+
33
+ What I've seen in Notes invitations, and iCal responses:
34
+ ROLE
35
+ PARTSTAT
36
+ RSVP
37
+ CN
38
+
39
+ Support these last 4, for now.
40
+
41
+ =end
42
+
43
+ module Vpim
44
+ class Icalendar
45
+ # Used to represent calendar fields containing CAL-ADDRESS values.
46
+ # The organizer or the attendees of a calendar event are examples of such
47
+ # a field.
48
+ #
49
+ # Example:
50
+ #
51
+ # ORGANIZER;CN="A. Person":mailto:a_person@example.com
52
+ #
53
+ # ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
54
+ # ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
55
+ #
56
+ class Address
57
+
58
+ # Create a copy of Address. If the original Address was frozen, this one
59
+ # won't be.
60
+ def copy
61
+ #Marshal.load(Marshal.dump(self))
62
+ self.dup.dirty
63
+ end
64
+
65
+ def dirty #:nodoc:
66
+ @field = nil
67
+ self
68
+ end
69
+
70
+ # Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
71
+ attr_accessor :uri
72
+ # The common or displayable name associated with the calendar address, or
73
+ # nil if there is none.
74
+ attr_accessor :cn
75
+ # The participation role for the calendar user specified by the address.
76
+ #
77
+ # The standard roles are:
78
+ # - CHAIR Indicates chair of the calendar entity
79
+ # - REQ-PARTICIPANT Indicates a participant whose participation is required
80
+ # - OPT-PARTICIPANT Indicates a participant whose participation is optional
81
+ # - NON-PARTICIPANT Indicates a participant who is copied for information purposes only
82
+ #
83
+ # The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
84
+ # specified.
85
+ attr_accessor :role
86
+ # The participation status for the calendar user specified by the
87
+ # property PARTSTAT, a String.
88
+ #
89
+ # These are the participation statuses for an Event:
90
+ # - NEEDS-ACTION Event needs action
91
+ # - ACCEPTED Event accepted
92
+ # - DECLINED Event declined
93
+ # - TENTATIVE Event tentatively accepted
94
+ # - DELEGATED Event delegated
95
+ #
96
+ # Default is NEEDS-ACTION.
97
+ #
98
+ # FIXME - make the default depend on the component type.
99
+ attr_accessor :partstat
100
+ # The value of the RSVP field, either +true+ or +false+. It is used to
101
+ # specify whether there is an expectation of a reply from the calendar
102
+ # user specified by the property value.
103
+ attr_accessor :rsvp
104
+
105
+ def initialize(field=nil) #:nodoc:
106
+ @field = field
107
+ @uri = ''
108
+ @cn = ''
109
+ @role = "REQ-PARTICIPANT"
110
+ @partstat = "NEEDS-ACTION"
111
+ @rsvp = false
112
+ end
113
+
114
+ # Create a new Address. It will encode as a +name+ property.
115
+ def self.create(uri='')
116
+ adr = new
117
+ adr.uri = uri.to_str
118
+ adr
119
+ end
120
+
121
+ def self.decode(field)
122
+ adr = new(field)
123
+ adr.uri = field.value
124
+
125
+ cn = field.param('CN')
126
+
127
+ if cn
128
+ adr.cn = cn.first
129
+ end
130
+
131
+ role = field.param('ROLE')
132
+
133
+ if role
134
+ adr.role = role.first.strip.upcase
135
+ end
136
+
137
+ partstat = field.param('PARTSTAT')
138
+
139
+ if partstat
140
+ adr.partstat = partstat.first.strip.upcase
141
+ end
142
+
143
+ rsvp = field.param('RSVP')
144
+
145
+ if rsvp
146
+ adr.rsvp = case rsvp.first
147
+ when /TRUE/i then true
148
+ when /FALSE/i then false
149
+ else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{rsvp}"
150
+ end
151
+ end
152
+
153
+ adr.freeze
154
+ end
155
+
156
+ # Return a representation of this Address as a DirectoryInfo::Field.
157
+ def encode(name) #:nodoc:
158
+ if @field
159
+ # FIXME - set the field name, it could be different from cached
160
+ return @field
161
+ end
162
+
163
+ value = uri.to_str.strip
164
+
165
+ if value.empty?
166
+ raise Uencodeable, "Address#uri is zero-length"
167
+ end
168
+
169
+ params = {}
170
+
171
+ if cn.length > 0
172
+ params['CN'] = Vpim::encode_paramvalue(cn)
173
+ end
174
+
175
+ # FIXME - default value is different for non-vEvent
176
+ if role.length > 0 && role != 'REQ-PARTICIPANT'
177
+ params['ROLE'] = Vpim::encode_paramtext(role)
178
+ end
179
+
180
+ # FIXME - default value is different for non-vEvent
181
+ if partstat.length > 0 && partstat != 'NEEDS-ACTION'
182
+ params['PARTSTAT'] = Vpim::encode_paramtext(partstat)
183
+ end
184
+
185
+ if rsvp
186
+ params['RSVP'] = 'true'
187
+ end
188
+
189
+ Vpim::DirectoryInfo::Field.create(name, value, params)
190
+ end
191
+
192
+ # Return true if the +uri+ is == to this address' URI. The comparison
193
+ # is case-insensitive (because email addresses and domain names are).
194
+ def ==(uri)
195
+ # TODO - could I use a URI library?
196
+ Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
197
+ end
198
+
199
+ # A string representation of an address, using the common name, and the
200
+ # URI. The URI protocol is stripped if it's "mailto:".
201
+ def to_s
202
+ u = uri
203
+ u = u.gsub(/^mailto: */i, '')
204
+
205
+ if cn.length > 0
206
+ "#{cn.inspect} <#{uri}>"
207
+ else
208
+ uri
209
+ end
210
+ end
211
+
212
+ def inspect #:nodoc:
213
+ "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp=#{rsvp} #{uri.inspect}>"
214
+ end
215
+
216
+ end
217
+ end
218
+ end
219
+
@@ -0,0 +1,104 @@
1
+ =begin
2
+ Copyright (C) 2009 Sam Roberts
3
+
4
+ This library is free software; you can redistribute it and/or modify it
5
+ under the same terms as the ruby language itself, see the file COPYING for
6
+ details.
7
+ =end
8
+
9
+ require "atom"
10
+
11
+ module Vpim
12
+ module Agent
13
+
14
+ module Atomize
15
+ MIME = "application/atom+xml"
16
+
17
+ # +ical+, an icalendar, or at least a Repo calendar's subset of an Icalendar
18
+ # +feeduri+, the atom xml should know the URI of where the feed is available from.
19
+ # +caluri+, optionally, the URI of the calendar its converted from.
20
+ #
21
+ # TODO - and the URI of an alternative/html representation of this feed?
22
+ def self.calendar(ical, feeduri, caluri = nil, calname = nil)
23
+ mime = MIME
24
+
25
+ feeduri = feeduri.to_str
26
+ caluri = caluri
27
+ calname = (calname or caluri or "Unknown").to_str
28
+
29
+ f = Atom::Feed.new
30
+ # Mandatory attributes:
31
+ # For ID, we should use http://.../ics/atom?....., or just the URL of the ics?
32
+ # I think it can be a full URI... or maybe a sha-1 of our full URI?
33
+ # or like gmail, no id for feed,
34
+ # <id>tag:gmail.google.com,2004:1295062805013769502</id>
35
+ #
36
+ f.id = feeduri
37
+ f.title = calname
38
+ f.updated = Time.now
39
+ f.authors << Atom::Person.new(:name => (caluri or calname))
40
+ f.generator = Atom::Generator.new do |g|
41
+ g.name = Vpim::PRODID
42
+ g.uri = "http://vpim.rubyforge.org"
43
+ g.version = Vpim::VERSION
44
+ end
45
+
46
+ f.links << Atom::Link.new do |l|
47
+ l.href = feeduri
48
+ l.type = mime
49
+ l.rel = :self
50
+ end
51
+
52
+ if caluri
53
+ # This is maybe better described as :via, but with :alternate being
54
+ # an html view of this feed.
55
+ #
56
+ # TODO should I change the scheme to be webcal?
57
+ # TODO should I extend URI to support webcal?
58
+ f.links << Atom::Link.new do |l|
59
+ l.href = caluri
60
+ l.type = "text/calendar"
61
+ l.rel = :alternate
62
+ end
63
+ end
64
+
65
+ # .icon = uri to the vAgent icon
66
+ entry_id = 0
67
+ ical.events do |ve|
68
+ # TODO - infinite?
69
+ ve.occurrences do |t|
70
+ f.entries << Atom::Entry.new do |e|
71
+ # iCalendar -> atom
72
+ # -----------------
73
+ # summary -> title
74
+ # description -> text/content
75
+ # uid -> id
76
+ # created -> published?
77
+ # organizer -> author?
78
+ # contact -> author?
79
+ # last-mod -> semantically, this is updated, but atom doesn't
80
+ # have the notion that an entry has a relationship to a time,
81
+ # other than the time the entry itself was published, and when
82
+ # the entry gets updated. We'll abuse updated for the event's time.
83
+ # categories -> where do "tags" go in atom, if anywhere?
84
+ # attachment -> into a link?
85
+ e.title = ve.summary if ve.summary
86
+ e.content = Atom::Content::Text.new(ve.description) if ve.description
87
+ e.updated = t
88
+
89
+ # Use "tag:", as defined by RFC4151, and use event UID if possible. Otherwise,
90
+ # construct something. Maybe I should mix something in to make it unique for
91
+ # each time a feed is generated for the calendar?
92
+ entry_id += 1
93
+ tag = ve.uid || "#{entry_id}@#{feeduri}"
94
+ e.id = "tag:vpim.rubyforge.org,2009:#{tag}"
95
+ end
96
+ end
97
+ end
98
+ return f
99
+ end
100
+ end # Atomize
101
+
102
+ end # Agent
103
+ end # Vpim
104
+
@@ -0,0 +1,73 @@
1
+ =begin
2
+ Copyright (C) 2009 Sam Roberts
3
+
4
+ This library is free software; you can redistribute it and/or modify it
5
+ under the same terms as the ruby language itself, see the file COPYING for
6
+ details.
7
+ =end
8
+
9
+ require 'cgi'
10
+
11
+ require 'sinatra/base'
12
+
13
+ # TODO Pasting of webcal links, conversion to webcal links?
14
+
15
+ module Vpim
16
+ module Agent
17
+
18
+ class Base < Sinatra::Base
19
+ # Ensure that this happens...
20
+ set :haml, :format=>:html4 # Appears to do nothing, but maybe it will some day...
21
+
22
+ def css(template)
23
+ render :css, template, {}
24
+ end
25
+
26
+ def render_css(template, data, options) # :nodoc:
27
+ data
28
+ end
29
+
30
+ # Complete path, as requested by the client. Take care about CGI path rewriting.
31
+ def request_path
32
+ # Using .to_s because rack/request.rb does, though I think the Rack
33
+ # spec requires these to be strings already.
34
+ begin
35
+ URI.parse(env["SCRIPT_URI"].to_s).path
36
+ rescue
37
+ env["SCRIPT_NAME"].to_s + env["PATH_INFO"].to_s
38
+ end
39
+ end
40
+
41
+ # Complete path, as requested by the client, without the env's PATH_INFO.
42
+ # This is the path to whatever is "handling" the request.
43
+ #
44
+ # Recent discussions on how PATH_INFO must be decoded leads me to think
45
+ # this might not work if the path had any URL encoded characters in it.
46
+ def script_path
47
+ request_path.sub(/#{env["PATH_INFO"]}$/, "")
48
+ end
49
+
50
+ # URL-ready form of the host and port, where the port isn't specified if
51
+ # it is the default for the URL scheme.
52
+ def host_port
53
+ r = request
54
+ host_port = r.host
55
+
56
+ if r.scheme == "https" && r.port != 443 ||
57
+ r.scheme == "http" && r.port != 80
58
+ host_port << ":#{r.port}"
59
+ end
60
+
61
+ host_port
62
+ end
63
+
64
+ # URL to the script
65
+ def script_url
66
+ request.scheme + "://" + host_port + script_path
67
+ end
68
+
69
+ end # Base
70
+
71
+ end # Agent
72
+ end # Vpim
73
+