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 335
335:       def call_callback(callback_name, req, res)
336:         if cb = @options[callback_name]
337:           cb.call(req, res)
338:         end
339:       end

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/webrick/httpservlet/filehandler.rb, line 341
341:       def windows_ambiguous_name?(name)
342:         return true if /[. ]+\z/ =~ name
343:         return true if /::\$DATA\z/ =~ name
344:         return false
345:       end

[Validate]