rubysl-webrick 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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