| Class | WEBrick::HTTPServlet::FileHandler |
| In: |
lib/webrick/httpservlet/filehandler.rb
|
| Parent: | AbstractServlet |
| HandlerTable | = | Hash.new |
# File lib/webrick/httpservlet/filehandler.rb, line 131
131: def self.add_handler(suffix, handler)
132: HandlerTable[suffix] = handler
133: end
# File lib/webrick/httpservlet/filehandler.rb, line 139
139: def initialize(server, root, options={}, default=Config::FileHandler)
140: @config = server.config
141: @logger = @config[:Logger]
142: @root = File.expand_path(root)
143: if options == true || options == false
144: options = { :FancyIndexing => options }
145: end
146: @options = default.dup.update(options)
147: end
# File lib/webrick/httpservlet/filehandler.rb, line 135
135: def self.remove_handler(suffix)
136: HandlerTable.delete(suffix)
137: end
# File lib/webrick/httpservlet/filehandler.rb, line 170
170: def do_GET(req, res)
171: unless exec_handler(req, res)
172: set_dir_list(req, res)
173: end
174: end
# File lib/webrick/httpservlet/filehandler.rb, line 182
182: def do_OPTIONS(req, res)
183: unless exec_handler(req, res)
184: super(req, res)
185: end
186: end
# File lib/webrick/httpservlet/filehandler.rb, line 176
176: def do_POST(req, res)
177: unless exec_handler(req, res)
178: raise HTTPStatus::NotFound, "`#{req.path}' not found."
179: end
180: end
# File lib/webrick/httpservlet/filehandler.rb, line 149
149: def service(req, res)
150: # if this class is mounted on "/" and /~username is requested.
151: # we're going to override path informations before invoking service.
152: if defined?(Etc) && @options[:UserDir] && req.script_name.empty?
153: if %r|^(/~([^/]+))| =~ req.path_info
154: script_name, user = $1, $2
155: path_info = $'
156: begin
157: passwd = Etc::getpwnam(user)
158: @root = File::join(passwd.dir, @options[:UserDir])
159: req.script_name = script_name
160: req.path_info = path_info
161: rescue
162: @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed"
163: end
164: end
165: end
166: prevent_directory_traversal(req, res)
167: super(req, res)
168: end
# File lib/webrick/httpservlet/filehandler.rb, line 335
335: def call_callback(callback_name, req, res)
336: if cb = @options[callback_name]
337: cb.call(req, res)
338: end
339: end
# File lib/webrick/httpservlet/filehandler.rb, line 288
288: def check_filename(req, res, name)
289: if nondisclosure_name?(name) || windows_ambiguous_name?(name)
290: @logger.warn("the request refers nondisclosure name `#{name}'.")
291: raise HTTPStatus::NotFound, "`#{req.path}' not found."
292: end
293: end
# File lib/webrick/httpservlet/filehandler.rb, line 230
230: def exec_handler(req, res)
231: raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
232: if set_filename(req, res)
233: handler = get_handler(req, res)
234: call_callback(:HandlerCallback, req, res)
235: h = handler.get_instance(@config, res.filename)
236: h.service(req, res)
237: return true
238: end
239: call_callback(:HandlerCallback, req, res)
240: return false
241: end
# File lib/webrick/httpservlet/filehandler.rb, line 243
243: def get_handler(req, res)
244: suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
245: if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
246: if @options[:AcceptableLanguages].include?($2.downcase)
247: suffix2 = $1.downcase
248: end
249: end
250: handler_table = @options[:HandlerTable]
251: return handler_table[suffix1] || handler_table[suffix2] ||
252: HandlerTable[suffix1] || HandlerTable[suffix2] ||
253: DefaultFileHandler
254: end
# File lib/webrick/httpservlet/filehandler.rb, line 347
347: def nondisclosure_name?(name)
348: @options[:NondisclosureName].each{|pattern|
349: if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
350: return true
351: end
352: }
353: return false
354: end
# File lib/webrick/httpservlet/filehandler.rb, line 211
211: def prevent_directory_traversal(req, res)
212: # Preventing directory traversal on Windows platforms;
213: # Backslashes (0x5c) in path_info are not interpreted as special
214: # character in URI notation. So the value of path_info should be
215: # normalize before accessing to the filesystem.
216:
217: if trailing_pathsep?(req.path_info)
218: # File.expand_path removes the trailing path separator.
219: # Adding a character is a workaround to save it.
220: # File.expand_path("/aaa/") #=> "/aaa"
221: # File.expand_path("/aaa/" + "x") #=> "/aaa/x"
222: expanded = File.expand_path(req.path_info + "x")
223: expanded.chop! # remove trailing "x"
224: else
225: expanded = File.expand_path(req.path_info)
226: end
227: req.path_info = expanded
228: end
# File lib/webrick/httpservlet/filehandler.rb, line 313
313: def search_file(req, res, basename)
314: langs = @options[:AcceptableLanguages]
315: path = res.filename + basename
316: if File.file?(path)
317: return basename
318: elsif langs.size > 0
319: req.accept_language.each{|lang|
320: path_with_lang = path + ".#{lang}"
321: if langs.member?(lang) && File.file?(path_with_lang)
322: return basename + ".#{lang}"
323: end
324: }
325: (langs - req.accept_language).each{|lang|
326: path_with_lang = path + ".#{lang}"
327: if File.file?(path_with_lang)
328: return basename + ".#{lang}"
329: end
330: }
331: end
332: return nil
333: end
# File lib/webrick/httpservlet/filehandler.rb, line 304
304: def search_index_file(req, res)
305: @config[:DirectoryIndex].each{|index|
306: if file = search_file(req, res, "/"+index)
307: return file
308: end
309: }
310: return nil
311: end
# File lib/webrick/httpservlet/filehandler.rb, line 356
356: def set_dir_list(req, res)
357: redirect_to_directory_uri(req, res)
358: unless @options[:FancyIndexing]
359: raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'"
360: end
361: local_path = res.filename
362: list = Dir::entries(local_path).collect{|name|
363: next if name == "." || name == ".."
364: next if nondisclosure_name?(name)
365: next if windows_ambiguous_name?(name)
366: st = (File::stat(File.join(local_path, name)) rescue nil)
367: if st.nil?
368: [ name, nil, -1 ]
369: elsif st.directory?
370: [ name + "/", st.mtime, -1 ]
371: else
372: [ name, st.mtime, st.size ]
373: end
374: }
375: list.compact!
376:
377: if d0 = req.query["N"]; idx = 0
378: elsif d0 = req.query["M"]; idx = 1
379: elsif d0 = req.query["S"]; idx = 2
380: else d0 = "A" ; idx = 0
381: end
382: d1 = (d0 == "A") ? "D" : "A"
383:
384: if d0 == "A"
385: list.sort!{|a,b| a[idx] <=> b[idx] }
386: else
387: list.sort!{|a,b| b[idx] <=> a[idx] }
388: end
389:
390: res['content-type'] = "text/html"
391:
392: res.body = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<HTML>\n<HEAD><TITLE>Index of \#{HTMLUtils::escape(req.path)}</TITLE></HEAD>\n<BODY>\n<H1>Index of \#{HTMLUtils::escape(req.path)}</H1>\n"
393:
394: res.body << "<PRE>\n"
395: res.body << " <A HREF=\"?N=#{d1}\">Name</A> "
396: res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
397: res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
398: res.body << "<HR>\n"
399:
400: list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
401: list.each{ |name, time, size|
402: if name == ".."
403: dname = "Parent Directory"
404: elsif name.size > 25
405: dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
406: else
407: dname = name
408: end
409: s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
410: s << " " * (30 - dname.size)
411: s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
412: s << (size >= 0 ? size.to_s : "-") << "\n"
413: res.body << s
414: }
415: res.body << "</PRE><HR>"
416:
417: res.body << "<ADDRESS>\n\#{HTMLUtils::escape(@config[:ServerSoftware])}<BR>\nat \#{req.host}:\#{req.port}\n</ADDRESS>\n</BODY>\n</HTML>\n"
418: end
# File lib/webrick/httpservlet/filehandler.rb, line 256
256: def set_filename(req, res)
257: res.filename = @root.dup
258: path_info = req.path_info.scan(%r|/[^/]*|)
259:
260: path_info.unshift("") # dummy for checking @root dir
261: while base = path_info.first
262: break if base == "/"
263: break unless File.directory?(File.expand_path(res.filename + base))
264: shift_path_info(req, res, path_info)
265: call_callback(:DirectoryCallback, req, res)
266: end
267:
268: if base = path_info.first
269: if base == "/"
270: if file = search_index_file(req, res)
271: shift_path_info(req, res, path_info, file)
272: call_callback(:FileCallback, req, res)
273: return true
274: end
275: shift_path_info(req, res, path_info)
276: elsif file = search_file(req, res, base)
277: shift_path_info(req, res, path_info, file)
278: call_callback(:FileCallback, req, res)
279: return true
280: else
281: raise HTTPStatus::NotFound, "`#{req.path}' not found."
282: end
283: end
284:
285: return false
286: end
# File lib/webrick/httpservlet/filehandler.rb, line 295
295: def shift_path_info(req, res, path_info, base=nil)
296: tmp = path_info.shift
297: base = base || tmp
298: req.path_info = path_info.join
299: req.script_name << base
300: res.filename = File.expand_path(res.filename + base)
301: check_filename(req, res, File.basename(res.filename))
302: end
RFC3253: Versioning Extensions to WebDAV
(Web Distributed Authoring and Versioning)
VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE ACTIVITY
# File lib/webrick/httpservlet/filehandler.rb, line 202
202: def trailing_pathsep?(path)
203: # check for trailing path separator:
204: # File.dirname("/aaaa/bbbb/") #=> "/aaaa")
205: # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb")
206: # File.dirname("/aaaa/bbbb") #=> "/aaaa")
207: # File.dirname("/aaaa/bbbbx") #=> "/aaaa")
208: return File.dirname(path) != File.dirname(path+"x")
209: end