Class WEBrick::HTTPServlet::FileHandler
In: lib/webrick/httpservlet/filehandler.rb
Parent: AbstractServlet

Methods

Constants

HandlerTable = Hash.new

Public Class methods

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 131
131:       def self.add_handler(suffix, handler)
132:         HandlerTable[suffix] = handler
133:       end

[Source]

     # 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

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 135
135:       def self.remove_handler(suffix)
136:         HandlerTable.delete(suffix)
137:       end

Public Instance methods

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

Private Instance methods

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 351
351:       def call_callback(callback_name, req, res)
352:         if cb = @options[callback_name]
353:           cb.call(req, res)
354:         end
355:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 304
304:       def check_filename(req, res, name)
305:         if nondisclosure_name?(name) || windows_ambiguous_name?(name)
306:           @logger.warn("the request refers nondisclosure name `#{name}'.")
307:           raise HTTPStatus::NotFound, "`#{req.path}' not found."
308:         end
309:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 246
246:       def exec_handler(req, res)
247:         raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
248:         if set_filename(req, res)
249:           handler = get_handler(req, res)
250:           call_callback(:HandlerCallback, req, res)
251:           h = handler.get_instance(@config, res.filename)
252:           h.service(req, res)
253:           return true
254:         end
255:         call_callback(:HandlerCallback, req, res)
256:         return false
257:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 259
259:       def get_handler(req, res)
260:         suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
261:         if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
262:           if @options[:AcceptableLanguages].include?($2.downcase)
263:             suffix2 = $1.downcase
264:           end
265:         end
266:         handler_table = @options[:HandlerTable]
267:         return handler_table[suffix1] || handler_table[suffix2] ||
268:                HandlerTable[suffix1] || HandlerTable[suffix2] ||
269:                DefaultFileHandler
270:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 363
363:       def nondisclosure_name?(name)
364:         @options[:NondisclosureName].each{|pattern|
365:           if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
366:             return true
367:           end
368:         }
369:         return false
370:       end

RFC3253: Versioning Extensions to WebDAV

         (Web Distributed Authoring and Versioning)

VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE ACTIVITY

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 202
202:       def prevent_directory_traversal(req, res)
203:         # Preventing directory traversal on DOSISH platforms;
204:         # Backslashes (0x5c) in path_info are not interpreted as special
205:         # character in URI notation. So the value of path_info should be
206:         # normalize before accessing to the filesystem.
207:         if File::ALT_SEPARATOR
208:           # File.expand_path removes the trailing path separator.
209:           # Adding a character is a workaround to save it.
210:           #  File.expand_path("/aaa/")        #=> "/aaa"
211:           #  File.expand_path("/aaa/" + "x")  #=> "/aaa/x"
212:           expanded = File.expand_path(req.path_info + "x")
213:           expanded[-1, 1] = ""  # remove trailing "x"
214:           req.path_info = expanded
215:         end
216:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 227
227:       def prevent_directory_traversal(req, res)
228:         # Preventing directory traversal on Windows platforms;
229:         # Backslashes (0x5c) in path_info are not interpreted as special
230:         # character in URI notation. So the value of path_info should be
231:         # normalize before accessing to the filesystem.
232: 
233:         if trailing_pathsep?(req.path_info)
234:           # File.expand_path removes the trailing path separator.
235:           # Adding a character is a workaround to save it.
236:           #  File.expand_path("/aaa/")        #=> "/aaa"
237:           #  File.expand_path("/aaa/" + "x")  #=> "/aaa/x"
238:           expanded = File.expand_path(req.path_info + "x")
239:           expanded.chop!  # remove trailing "x"
240:         else
241:           expanded = File.expand_path(req.path_info)
242:         end
243:         req.path_info = expanded
244:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 329
329:       def search_file(req, res, basename)
330:         langs = @options[:AcceptableLanguages]
331:         path = res.filename + basename
332:         if File.file?(path)
333:           return basename
334:         elsif langs.size > 0
335:           req.accept_language.each{|lang|
336:             path_with_lang = path + ".#{lang}"
337:             if langs.member?(lang) && File.file?(path_with_lang)
338:               return basename + ".#{lang}"
339:             end
340:           }
341:           (langs - req.accept_language).each{|lang|
342:             path_with_lang = path + ".#{lang}"
343:             if File.file?(path_with_lang)
344:               return basename + ".#{lang}"
345:             end
346:           }
347:         end
348:         return nil
349:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 320
320:       def search_index_file(req, res)
321:         @config[:DirectoryIndex].each{|index|
322:           if file = search_file(req, res, "/"+index)
323:             return file
324:           end
325:         }
326:         return nil
327:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 372
372:       def set_dir_list(req, res)
373:         redirect_to_directory_uri(req, res)
374:         unless @options[:FancyIndexing]
375:           raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'"
376:         end
377:         local_path = res.filename
378:         list = Dir::entries(local_path).collect{|name|
379:           next if name == "." || name == ".."
380:           next if nondisclosure_name?(name)
381:           next if windows_ambiguous_name?(name)
382:           st = (File::stat(File.join(local_path, name)) rescue nil)
383:           if st.nil?
384:             [ name, nil, -1 ]
385:           elsif st.directory?
386:             [ name + "/", st.mtime, -1 ]
387:           else
388:             [ name, st.mtime, st.size ]
389:           end
390:         }
391:         list.compact!
392: 
393:         if    d0 = req.query["N"]; idx = 0
394:         elsif d0 = req.query["M"]; idx = 1
395:         elsif d0 = req.query["S"]; idx = 2
396:         else  d0 = "A"           ; idx = 0
397:         end
398:         d1 = (d0 == "A") ? "D" : "A"
399: 
400:         if d0 == "A"
401:           list.sort!{|a,b| a[idx] <=> b[idx] }
402:         else
403:           list.sort!{|a,b| b[idx] <=> a[idx] }
404:         end
405: 
406:         res['content-type'] = "text/html"
407: 
408:         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"
409: 
410:         res.body << "<PRE>\n"
411:         res.body << " <A HREF=\"?N=#{d1}\">Name</A>                          "
412:         res.body << "<A HREF=\"?M=#{d1}\">Last modified</A>         "
413:         res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
414:         res.body << "<HR>\n"
415:        
416:         list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
417:         list.each{ |name, time, size|
418:           if name == ".."
419:             dname = "Parent Directory"
420:           elsif name.size > 25
421:             dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
422:           else
423:             dname = name
424:           end
425:           s =  " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
426:           s << " " * (30 - dname.size)
427:           s << (time ? time.strftime("%Y/%m/%d %H:%M      ") : " " * 22)
428:           s << (size >= 0 ? size.to_s : "-") << "\n"
429:           res.body << s
430:         }
431:         res.body << "</PRE><HR>"
432: 
433:         res.body << "<ADDRESS>\n\#{HTMLUtils::escape(@config[:ServerSoftware])}<BR>\nat \#{req.host}:\#{req.port}\n</ADDRESS>\n</BODY>\n</HTML>\n"    
434:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 272
272:       def set_filename(req, res)
273:         res.filename = @root.dup
274:         path_info = req.path_info.scan(%r|/[^/]*|)
275: 
276:         path_info.unshift("")  # dummy for checking @root dir
277:         while base = path_info.first
278:           break if base == "/"
279:           break unless File.directory?(File.expand_path(res.filename + base))
280:           shift_path_info(req, res, path_info)
281:           call_callback(:DirectoryCallback, req, res)
282:         end
283: 
284:         if base = path_info.first
285:           if base == "/"
286:             if file = search_index_file(req, res)
287:               shift_path_info(req, res, path_info, file)
288:               call_callback(:FileCallback, req, res)
289:               return true
290:             end
291:             shift_path_info(req, res, path_info)
292:           elsif file = search_file(req, res, base)
293:             shift_path_info(req, res, path_info, file)
294:             call_callback(:FileCallback, req, res)
295:             return true
296:           else
297:             raise HTTPStatus::NotFound, "`#{req.path}' not found."
298:           end
299:         end
300: 
301:         return false
302:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 311
311:       def shift_path_info(req, res, path_info, base=nil)
312:         tmp = path_info.shift
313:         base = base || tmp
314:         req.path_info = path_info.join
315:         req.script_name << base
316:         res.filename = File.expand_path(res.filename + base)
317:         check_filename(req, res, File.basename(res.filename))
318:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 218
218:       def trailing_pathsep?(path)
219:         # check for trailing path separator:
220:         #   File.dirname("/aaaa/bbbb/")      #=> "/aaaa")
221:         #   File.dirname("/aaaa/bbbb/x")     #=> "/aaaa/bbbb")
222:         #   File.dirname("/aaaa/bbbb")       #=> "/aaaa")
223:         #   File.dirname("/aaaa/bbbbx")      #=> "/aaaa")
224:         return File.dirname(path) != File.dirname(path+"x")
225:       end

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 357
357:       def windows_ambiguous_name?(name)
358:         return true if /[. ]+\z/ =~ name
359:         return true if /::\$DATA\z/ =~ name
360:         return false
361:       end

[Validate]