rubysl-webrick 1.0.0 → 2.0.0

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 (39) hide show
  1. checksums.yaml +14 -6
  2. data/.travis.yml +5 -6
  3. data/lib/rubysl/webrick/version.rb +1 -1
  4. data/lib/rubysl/webrick/webrick.rb +199 -2
  5. data/lib/webrick/accesslog.rb +96 -5
  6. data/lib/webrick/cgi.rb +80 -29
  7. data/lib/webrick/compat.rb +20 -0
  8. data/lib/webrick/config.rb +59 -5
  9. data/lib/webrick/cookie.rb +66 -5
  10. data/lib/webrick/htmlutils.rb +4 -1
  11. data/lib/webrick/httpauth.rb +53 -3
  12. data/lib/webrick/httpauth/authenticator.rb +53 -16
  13. data/lib/webrick/httpauth/basicauth.rb +45 -2
  14. data/lib/webrick/httpauth/digestauth.rb +82 -17
  15. data/lib/webrick/httpauth/htdigest.rb +38 -1
  16. data/lib/webrick/httpauth/htgroup.rb +32 -0
  17. data/lib/webrick/httpauth/htpasswd.rb +40 -2
  18. data/lib/webrick/httpauth/userdb.rb +27 -4
  19. data/lib/webrick/httpproxy.rb +197 -112
  20. data/lib/webrick/httprequest.rb +268 -50
  21. data/lib/webrick/httpresponse.rb +170 -33
  22. data/lib/webrick/https.rb +26 -3
  23. data/lib/webrick/httpserver.rb +75 -7
  24. data/lib/webrick/httpservlet/abstract.rb +88 -6
  25. data/lib/webrick/httpservlet/cgi_runner.rb +5 -4
  26. data/lib/webrick/httpservlet/cgihandler.rb +37 -18
  27. data/lib/webrick/httpservlet/erbhandler.rb +40 -7
  28. data/lib/webrick/httpservlet/filehandler.rb +116 -28
  29. data/lib/webrick/httpservlet/prochandler.rb +17 -4
  30. data/lib/webrick/httpstatus.rb +86 -18
  31. data/lib/webrick/httputils.rb +131 -23
  32. data/lib/webrick/httpversion.rb +28 -2
  33. data/lib/webrick/log.rb +72 -5
  34. data/lib/webrick/server.rb +158 -33
  35. data/lib/webrick/ssl.rb +78 -9
  36. data/lib/webrick/utils.rb +151 -5
  37. data/lib/webrick/version.rb +5 -1
  38. data/rubysl-webrick.gemspec +0 -1
  39. metadata +12 -24
@@ -18,17 +18,88 @@ module WEBrick
18
18
  module HTTPServlet
19
19
  class HTTPServletError < StandardError; end
20
20
 
21
+ ##
22
+ # AbstractServlet allows HTTP server modules to be reused across multiple
23
+ # servers and allows encapsulation of functionality.
24
+ #
25
+ # By default a servlet will respond to GET, HEAD (through an alias to GET)
26
+ # and OPTIONS requests.
27
+ #
28
+ # By default a new servlet is initialized for every request. A servlet
29
+ # instance can be reused by overriding ::get_instance in the
30
+ # AbstractServlet subclass.
31
+ #
32
+ # == A Simple Servlet
33
+ #
34
+ # class Simple < WEBrick::HTTPServlet::AbstractServlet
35
+ # def do_GET request, response
36
+ # status, content_type, body = do_stuff_with request
37
+ #
38
+ # response.status = status
39
+ # response['Content-Type'] = content_type
40
+ # response.body = body
41
+ # end
42
+ #
43
+ # def do_stuff_with request
44
+ # return 200, 'text/plain', 'you got a page'
45
+ # end
46
+ # end
47
+ #
48
+ # This servlet can be mounted on a server at a given path:
49
+ #
50
+ # server.mount '/simple', Simple
51
+ #
52
+ # == Servlet Configuration
53
+ #
54
+ # Servlets can be configured via initialize. The first argument is the
55
+ # HTTP server the servlet is being initialized for.
56
+ #
57
+ # class Configurable < Simple
58
+ # def initialize server, color, size
59
+ # super server
60
+ # @color = color
61
+ # @size = size
62
+ # end
63
+ #
64
+ # def do_stuff_with request
65
+ # content = "<p " \
66
+ # %q{style="color: #{@color}; font-size: #{@size}"} \
67
+ # ">Hello, World!"
68
+ #
69
+ # return 200, "text/html", content
70
+ # end
71
+ # end
72
+ #
73
+ # This servlet must be provided two arguments at mount time:
74
+ #
75
+ # server.mount '/configurable', Configurable, 'red', '2em'
76
+
21
77
  class AbstractServlet
22
- def self.get_instance(config, *options)
23
- self.new(config, *options)
78
+
79
+ ##
80
+ # Factory for servlet instances that will handle a request from +server+
81
+ # using +options+ from the mount point. By default a new servlet
82
+ # instance is created for every call.
83
+
84
+ def self.get_instance(server, *options)
85
+ self.new(server, *options)
24
86
  end
25
87
 
88
+ ##
89
+ # Initializes a new servlet for +server+ using +options+ which are
90
+ # stored as-is in +@options+. +@logger+ is also provided.
91
+
26
92
  def initialize(server, *options)
27
93
  @server = @config = server
28
94
  @logger = @server[:Logger]
29
95
  @options = options
30
96
  end
31
97
 
98
+ ##
99
+ # Dispatches to a +do_+ method based on +req+ if such a method is
100
+ # available. (+do_GET+ for a GET request). Raises a MethodNotAllowed
101
+ # exception if the method is not implemented.
102
+
32
103
  def service(req, res)
33
104
  method_name = "do_" + req.request_method.gsub(/-/, "_")
34
105
  if respond_to?(method_name)
@@ -39,27 +110,38 @@ module WEBrick
39
110
  end
40
111
  end
41
112
 
113
+ ##
114
+ # Raises a NotFound exception
115
+
42
116
  def do_GET(req, res)
43
117
  raise HTTPStatus::NotFound, "not found."
44
118
  end
45
119
 
120
+ ##
121
+ # Dispatches to do_GET
122
+
46
123
  def do_HEAD(req, res)
47
124
  do_GET(req, res)
48
125
  end
49
126
 
127
+ ##
128
+ # Returns the allowed HTTP request methods
129
+
50
130
  def do_OPTIONS(req, res)
51
- m = self.methods.grep(/^do_[A-Z]+$/)
52
- m.collect!{|i| i.sub(/do_/, "") }
131
+ m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1}
53
132
  m.sort!
54
133
  res["allow"] = m.join(",")
55
134
  end
56
135
 
57
136
  private
58
137
 
138
+ ##
139
+ # Redirects to a path ending in /
140
+
59
141
  def redirect_to_directory_uri(req, res)
60
142
  if req.path[-1] != ?/
61
- location = req.path + "/"
62
- if req.query_string && req.query_string.size > 0
143
+ location = WEBrick::HTTPUtils.escape_path(req.path + "/")
144
+ if req.query_string && req.query_string.bytesize > 0
63
145
  location << "?" << req.query_string
64
146
  end
65
147
  res.set_redirect(HTTPStatus::MovedPermanently, location)
@@ -13,14 +13,13 @@ def sysread(io, size)
13
13
  while size > 0
14
14
  tmp = io.sysread(size)
15
15
  buf << tmp
16
- size -= tmp.size
16
+ size -= tmp.bytesize
17
17
  end
18
18
  return buf
19
19
  end
20
20
 
21
21
  STDIN.binmode
22
22
 
23
- buf = ""
24
23
  len = sysread(STDIN, 8).to_i
25
24
  out = sysread(STDIN, len)
26
25
  STDOUT.reopen(open(out, "w"))
@@ -38,8 +37,10 @@ hash.each{|k, v| ENV[k] = v if v }
38
37
  dir = File::dirname(ENV["SCRIPT_FILENAME"])
39
38
  Dir::chdir dir
40
39
 
41
- if interpreter = ARGV[0]
42
- exec(interpreter, ENV["SCRIPT_FILENAME"])
40
+ if ARGV[0]
41
+ argv = ARGV.dup
42
+ argv << ENV["SCRIPT_FILENAME"]
43
+ exec(*argv)
43
44
  # NOTREACHED
44
45
  end
45
46
  exec ENV["SCRIPT_FILENAME"]
@@ -1,11 +1,11 @@
1
- #
1
+ #
2
2
  # cgihandler.rb -- CGIHandler Class
3
- #
3
+ #
4
4
  # Author: IPR -- Internet Programming with Ruby -- writers
5
5
  # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
6
  # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
7
  # reserved.
8
- #
8
+ #
9
9
  # $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
10
10
 
11
11
  require 'rbconfig'
@@ -16,26 +16,39 @@ require 'webrick/httpservlet/abstract'
16
16
  module WEBrick
17
17
  module HTTPServlet
18
18
 
19
+ ##
20
+ # Servlet for handling CGI scripts
21
+ #
22
+ # Example:
23
+ #
24
+ # server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler,
25
+ # '/path/to/my_script')
26
+
19
27
  class CGIHandler < AbstractServlet
20
- Ruby = File::join(::Config::CONFIG['bindir'],
21
- ::Config::CONFIG['ruby_install_name'])
22
- Ruby << ::Config::CONFIG['EXEEXT']
23
- CGIRunner = "\"#{Ruby}\" \"#{Config::LIBDIR}/httpservlet/cgi_runner.rb\""
28
+ Ruby = RbConfig.ruby # :nodoc:
29
+ CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
30
+
31
+ ##
32
+ # Creates a new CGI script servlet for the script at +name+
24
33
 
25
34
  def initialize(server, name)
26
- super
35
+ super(server, name)
27
36
  @script_filename = name
28
37
  @tempdir = server[:TempDir]
29
38
  @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
30
39
  end
31
40
 
41
+ # :stopdoc:
42
+
32
43
  def do_GET(req, res)
33
44
  data = nil
34
45
  status = -1
35
46
 
36
47
  cgi_in = IO::popen(@cgicmd, "wb")
37
- cgi_out = Tempfile.new("webrick.cgiout.", @tempdir)
38
- cgi_err = Tempfile.new("webrick.cgierr.", @tempdir)
48
+ cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
49
+ cgi_out.set_encoding("ASCII-8BIT")
50
+ cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY)
51
+ cgi_err.set_encoding("ASCII-8BIT")
39
52
  begin
40
53
  cgi_in.sync = true
41
54
  meta = req.meta_vars
@@ -46,14 +59,14 @@ module WEBrick
46
59
  end
47
60
  dump = Marshal.dump(meta)
48
61
 
49
- cgi_in.write("%8d" % cgi_out.path.size)
62
+ cgi_in.write("%8d" % cgi_out.path.bytesize)
50
63
  cgi_in.write(cgi_out.path)
51
- cgi_in.write("%8d" % cgi_err.path.size)
64
+ cgi_in.write("%8d" % cgi_err.path.bytesize)
52
65
  cgi_in.write(cgi_err.path)
53
- cgi_in.write("%8d" % dump.size)
66
+ cgi_in.write("%8d" % dump.bytesize)
54
67
  cgi_in.write(dump)
55
68
 
56
- if req.body and req.body.size > 0
69
+ if req.body and req.body.bytesize > 0
57
70
  cgi_in.write(req.body)
58
71
  end
59
72
  ensure
@@ -63,19 +76,19 @@ module WEBrick
63
76
  data = cgi_out.read
64
77
  cgi_out.close(true)
65
78
  if errmsg = cgi_err.read
66
- if errmsg.size > 0
79
+ if errmsg.bytesize > 0
67
80
  @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
68
81
  end
69
- end
82
+ end
70
83
  cgi_err.close(true)
71
84
  end
72
-
85
+
73
86
  if status != 0
74
87
  @logger.error("CGIHandler: #{@script_filename} exit with #{status}")
75
88
  end
76
89
 
77
90
  data = "" unless data
78
- raw_header, body = data.split(/^[\xd\xa]+/on, 2)
91
+ raw_header, body = data.split(/^[\xd\xa]+/, 2)
79
92
  raise HTTPStatus::InternalServerError,
80
93
  "Premature end of script headers: #{@script_filename}" if body.nil?
81
94
 
@@ -85,6 +98,10 @@ module WEBrick
85
98
  res.status = $1.to_i
86
99
  header.delete('status')
87
100
  end
101
+ if header.has_key?('location')
102
+ # RFC 3875 6.2.3, 6.2.4
103
+ res.status = 302 unless (300...400) === res.status
104
+ end
88
105
  if header.has_key?('set-cookie')
89
106
  header['set-cookie'].each{|k|
90
107
  res.cookies << Cookie.parse_set_cookie(k)
@@ -98,6 +115,8 @@ module WEBrick
98
115
  res.body = body
99
116
  end
100
117
  alias do_POST do_GET
118
+
119
+ # :startdoc:
101
120
  end
102
121
 
103
122
  end
@@ -1,11 +1,11 @@
1
- #
1
+ #
2
2
  # erbhandler.rb -- ERBHandler Class
3
- #
3
+ #
4
4
  # Author: IPR -- Internet Programming with Ruby -- writers
5
5
  # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
6
  # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
7
  # reserved.
8
- #
8
+ #
9
9
  # $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
10
10
 
11
11
  require 'webrick/httpservlet/abstract.rb'
@@ -15,12 +15,37 @@ require 'erb'
15
15
  module WEBrick
16
16
  module HTTPServlet
17
17
 
18
+ ##
19
+ # ERBHandler evaluates an ERB file and returns the result. This handler
20
+ # is automatically used if there are .rhtml files in a directory served by
21
+ # the FileHandler.
22
+ #
23
+ # ERBHandler supports GET and POST methods.
24
+ #
25
+ # The ERB file is evaluated with the local variables +servlet_request+ and
26
+ # +servlet_response+ which are a WEBrick::HTTPRequest and
27
+ # WEBrick::HTTPResponse respectively.
28
+ #
29
+ # Example .rhtml file:
30
+ #
31
+ # Request to <%= servlet_request.request_uri %>
32
+ #
33
+ # Query params <%= servlet_request.query.inspect %>
34
+
18
35
  class ERBHandler < AbstractServlet
36
+
37
+ ##
38
+ # Creates a new ERBHandler on +server+ that will evaluate and serve the
39
+ # ERB file +name+
40
+
19
41
  def initialize(server, name)
20
- super
42
+ super(server, name)
21
43
  @script_filename = name
22
44
  end
23
45
 
46
+ ##
47
+ # Handles GET requests
48
+
24
49
  def do_GET(req, res)
25
50
  unless defined?(ERB)
26
51
  @logger.warn "#{self.class}: ERB not defined."
@@ -29,7 +54,7 @@ module WEBrick
29
54
  begin
30
55
  data = open(@script_filename){|io| io.read }
31
56
  res.body = evaluate(ERB.new(data), req, res)
32
- res['content-type'] =
57
+ res['content-type'] ||=
33
58
  HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
34
59
  rescue StandardError => ex
35
60
  raise
@@ -39,13 +64,21 @@ module WEBrick
39
64
  end
40
65
  end
41
66
 
67
+ ##
68
+ # Handles POST requests
69
+
42
70
  alias do_POST do_GET
43
71
 
44
72
  private
73
+
74
+ ##
75
+ # Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as
76
+ # local variables.
77
+
45
78
  def evaluate(erb, servlet_request, servlet_response)
46
79
  Module.new.module_eval{
47
- meta_vars = servlet_request.meta_vars
48
- query = servlet_request.query
80
+ servlet_request.meta_vars
81
+ servlet_request.query
49
82
  erb.result(binding)
50
83
  }
51
84
  end
@@ -18,12 +18,29 @@ require 'webrick/httpstatus'
18
18
  module WEBrick
19
19
  module HTTPServlet
20
20
 
21
+ ##
22
+ # Servlet for serving a single file. You probably want to use the
23
+ # FileHandler servlet instead as it handles directories and fancy indexes.
24
+ #
25
+ # Example:
26
+ #
27
+ # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
28
+ # '/path/to/my_page.txt')
29
+ #
30
+ # This servlet handles If-Modified-Since and Range requests.
31
+
21
32
  class DefaultFileHandler < AbstractServlet
33
+
34
+ ##
35
+ # Creates a DefaultFileHandler instance for the file at +local_path+.
36
+
22
37
  def initialize(server, local_path)
23
- super
38
+ super(server, local_path)
24
39
  @local_path = local_path
25
40
  end
26
41
 
42
+ # :stopdoc:
43
+
27
44
  def do_GET(req, res)
28
45
  st = File::stat(@local_path)
29
46
  mtime = st.mtime
@@ -32,7 +49,7 @@ module WEBrick
32
49
  if not_modified?(req, res, mtime, res['etag'])
33
50
  res.body = ''
34
51
  raise HTTPStatus::NotModified
35
- elsif req['range']
52
+ elsif req['range']
36
53
  make_partial_content(req, res, @local_path, st.size)
37
54
  raise HTTPStatus::PartialContent
38
55
  else
@@ -87,7 +104,7 @@ module WEBrick
87
104
  content = io.read(last-first+1)
88
105
  body << "--" << boundary << CRLF
89
106
  body << "Content-Type: #{mtype}" << CRLF
90
- body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF
107
+ body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
91
108
  body << CRLF
92
109
  body << content
93
110
  body << CRLF
@@ -107,7 +124,7 @@ module WEBrick
107
124
  content = io.read(last-first+1)
108
125
  end
109
126
  res['content-type'] = mtype
110
- res['content-range'] = "#{first}-#{last}/#{filesize}"
127
+ res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
111
128
  res['content-length'] = last - first + 1
112
129
  res.body = content
113
130
  else
@@ -123,19 +140,46 @@ module WEBrick
123
140
  last = filesize - 1 if last >= filesize
124
141
  return first, last
125
142
  end
143
+
144
+ # :startdoc:
126
145
  end
127
146
 
147
+ ##
148
+ # Serves a directory including fancy indexing and a variety of other
149
+ # options.
150
+ #
151
+ # Example:
152
+ #
153
+ # server.mount '/assets', WEBrick::FileHandler, '/path/to/assets'
154
+
128
155
  class FileHandler < AbstractServlet
129
- HandlerTable = Hash.new
156
+ HandlerTable = Hash.new # :nodoc:
157
+
158
+ ##
159
+ # Allow custom handling of requests for files with +suffix+ by class
160
+ # +handler+
130
161
 
131
162
  def self.add_handler(suffix, handler)
132
163
  HandlerTable[suffix] = handler
133
164
  end
134
165
 
166
+ ##
167
+ # Remove custom handling of requests for files with +suffix+
168
+
135
169
  def self.remove_handler(suffix)
136
170
  HandlerTable.delete(suffix)
137
171
  end
138
172
 
173
+ ##
174
+ # Creates a FileHandler servlet on +server+ that serves files starting
175
+ # at directory +root+
176
+ #
177
+ # +options+ may be a Hash containing keys from
178
+ # WEBrick::Config::FileHandler or +true+ or +false+.
179
+ #
180
+ # If +options+ is true or false then +:FancyIndexing+ is enabled or
181
+ # disabled respectively.
182
+
139
183
  def initialize(server, root, options={}, default=Config::FileHandler)
140
184
  @config = server.config
141
185
  @logger = @config[:Logger]
@@ -146,6 +190,8 @@ module WEBrick
146
190
  @options = default.dup.update(options)
147
191
  end
148
192
 
193
+ # :stopdoc:
194
+
149
195
  def service(req, res)
150
196
  # if this class is mounted on "/" and /~username is requested.
151
197
  # we're going to override path informations before invoking service.
@@ -163,6 +209,7 @@ module WEBrick
163
209
  end
164
210
  end
165
211
  end
212
+ prevent_directory_traversal(req, res)
166
213
  super(req, res)
167
214
  end
168
215
 
@@ -198,10 +245,42 @@ module WEBrick
198
245
 
199
246
  private
200
247
 
248
+ def trailing_pathsep?(path)
249
+ # check for trailing path separator:
250
+ # File.dirname("/aaaa/bbbb/") #=> "/aaaa")
251
+ # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb")
252
+ # File.dirname("/aaaa/bbbb") #=> "/aaaa")
253
+ # File.dirname("/aaaa/bbbbx") #=> "/aaaa")
254
+ return File.dirname(path) != File.dirname(path+"x")
255
+ end
256
+
257
+ def prevent_directory_traversal(req, res)
258
+ # Preventing directory traversal on Windows platforms;
259
+ # Backslashes (0x5c) in path_info are not interpreted as special
260
+ # character in URI notation. So the value of path_info should be
261
+ # normalize before accessing to the filesystem.
262
+
263
+ # dirty hack for filesystem encoding; in nature, File.expand_path
264
+ # should not be used for path normalization. [Bug #3345]
265
+ path = req.path_info.dup.force_encoding(Encoding.find("filesystem"))
266
+ if trailing_pathsep?(req.path_info)
267
+ # File.expand_path removes the trailing path separator.
268
+ # Adding a character is a workaround to save it.
269
+ # File.expand_path("/aaa/") #=> "/aaa"
270
+ # File.expand_path("/aaa/" + "x") #=> "/aaa/x"
271
+ expanded = File.expand_path(path + "x")
272
+ expanded.chop! # remove trailing "x"
273
+ else
274
+ expanded = File.expand_path(path)
275
+ end
276
+ expanded.force_encoding(req.path_info.encoding)
277
+ req.path_info = expanded
278
+ end
279
+
201
280
  def exec_handler(req, res)
202
281
  raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
203
282
  if set_filename(req, res)
204
- handler = get_handler(req)
283
+ handler = get_handler(req, res)
205
284
  call_callback(:HandlerCallback, req, res)
206
285
  h = handler.get_instance(@config, res.filename)
207
286
  h.service(req, res)
@@ -211,9 +290,13 @@ module WEBrick
211
290
  return false
212
291
  end
213
292
 
214
- def get_handler(req)
215
- suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase
216
- suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase
293
+ def get_handler(req, res)
294
+ suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
295
+ if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
296
+ if @options[:AcceptableLanguages].include?($2.downcase)
297
+ suffix2 = $1.downcase
298
+ end
299
+ end
217
300
  handler_table = @options[:HandlerTable]
218
301
  return handler_table[suffix1] || handler_table[suffix2] ||
219
302
  HandlerTable[suffix1] || HandlerTable[suffix2] ||
@@ -226,15 +309,13 @@ module WEBrick
226
309
 
227
310
  path_info.unshift("") # dummy for checking @root dir
228
311
  while base = path_info.first
229
- check_filename(req, res, base)
230
312
  break if base == "/"
231
- break unless File.directory?(res.filename + base)
313
+ break unless File.directory?(File.expand_path(res.filename + base))
232
314
  shift_path_info(req, res, path_info)
233
315
  call_callback(:DirectoryCallback, req, res)
234
316
  end
235
317
 
236
318
  if base = path_info.first
237
- check_filename(req, res, base)
238
319
  if base == "/"
239
320
  if file = search_index_file(req, res)
240
321
  shift_path_info(req, res, path_info, file)
@@ -255,12 +336,10 @@ module WEBrick
255
336
  end
256
337
 
257
338
  def check_filename(req, res, name)
258
- @options[:NondisclosureName].each{|pattern|
259
- if File.fnmatch("/#{pattern}", name)
260
- @logger.warn("the request refers nondisclosure name `#{name}'.")
261
- raise HTTPStatus::NotFound, "`#{req.path}' not found."
262
- end
263
- }
339
+ if nondisclosure_name?(name) || windows_ambiguous_name?(name)
340
+ @logger.warn("the request refers nondisclosure name `#{name}'.")
341
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
342
+ end
264
343
  end
265
344
 
266
345
  def shift_path_info(req, res, path_info, base=nil)
@@ -268,7 +347,8 @@ module WEBrick
268
347
  base = base || tmp
269
348
  req.path_info = path_info.join
270
349
  req.script_name << base
271
- res.filename << base
350
+ res.filename = File.expand_path(res.filename + base)
351
+ check_filename(req, res, File.basename(res.filename))
272
352
  end
273
353
 
274
354
  def search_index_file(req, res)
@@ -308,9 +388,15 @@ module WEBrick
308
388
  end
309
389
  end
310
390
 
391
+ def windows_ambiguous_name?(name)
392
+ return true if /[. ]+\z/ =~ name
393
+ return true if /::\$DATA\z/ =~ name
394
+ return false
395
+ end
396
+
311
397
  def nondisclosure_name?(name)
312
398
  @options[:NondisclosureName].each{|pattern|
313
- if File.fnmatch(pattern, name)
399
+ if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
314
400
  return true
315
401
  end
316
402
  }
@@ -326,7 +412,8 @@ module WEBrick
326
412
  list = Dir::entries(local_path).collect{|name|
327
413
  next if name == "." || name == ".."
328
414
  next if nondisclosure_name?(name)
329
- st = (File::stat(local_path + name) rescue nil)
415
+ next if windows_ambiguous_name?(name)
416
+ st = (File::stat(File.join(local_path, name)) rescue nil)
330
417
  if st.nil?
331
418
  [ name, nil, -1 ]
332
419
  elsif st.directory?
@@ -365,25 +452,25 @@ module WEBrick
365
452
  res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
366
453
  res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
367
454
  res.body << "<HR>\n"
368
-
369
- list.unshift [ "..", File::mtime(local_path+".."), -1 ]
455
+
456
+ list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
370
457
  list.each{ |name, time, size|
371
458
  if name == ".."
372
459
  dname = "Parent Directory"
373
- elsif name.size > 25
374
- dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
460
+ elsif name.bytesize > 25
461
+ dname = name.sub(/^(.{23})(?:.*)/, '\1..')
375
462
  else
376
463
  dname = name
377
464
  end
378
- s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
379
- s << " " * (30 - dname.size)
465
+ s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{HTMLUtils::escape(dname)}</A>"
466
+ s << " " * (30 - dname.bytesize)
380
467
  s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
381
468
  s << (size >= 0 ? size.to_s : "-") << "\n"
382
469
  res.body << s
383
470
  }
384
471
  res.body << "</PRE><HR>"
385
472
 
386
- res.body << <<-_end_of_html_
473
+ res.body << <<-_end_of_html_
387
474
  <ADDRESS>
388
475
  #{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
389
476
  at #{req.host}:#{req.port}
@@ -393,6 +480,7 @@ module WEBrick
393
480
  _end_of_html_
394
481
  end
395
482
 
483
+ # :startdoc:
396
484
  end
397
485
  end
398
486
  end