module Rack # Rack::Directory serves entries below the +root+ given, according to the # path info of the Rack request. If a directory is found, the file's contents # will be presented in an html based index. If a file is found, the env will # be passed to the specified +app+. # # If +app+ is not specified, a Rack::File of the same +root+ will be used. class Directory < Rack::File DIR_FILE = "%s%s%s%s" DIR_PAGE = <<-PAGE %s

%s


%s
Name Size Type Last Modified

PAGE attr_reader :files def initialize(root, app=nil) super(root) @app = app || Rack::File.new(@root) end def call(env) dup._call(env) end def _call(env) if env["PATH_INFO"].include? ".." return [403, {"Content-Type" => "text/plain"}, ["Forbidden\n"]] end @path = F.join(@root, Utils.unescape(env['PATH_INFO'])) if F.exist?(@path) and F.readable?(@path) if F.file?(@path) return @app.call(env) elsif F.directory?(@path) @files = [['../','Parent Directory','','','']] sName, pInfo = env.values_at('SCRIPT_NAME', 'PATH_INFO') Dir.entries(@path).sort.each do |file| next if file[0] == ?. fl = F.join(@path, file) sz = F.size(fl) url = F.join(sName, pInfo, file) type = F.directory?(fl) ? 'directory' : MIME_TYPES.fetch(F.extname(file)[1..-1],'unknown') size = (type!='directory' ? (sz<10240 ? "#{sz}B" : "#{sz/1024}KB") : '-') mtime = F.mtime(fl).httpdate @files << [ url, file, size, type, mtime ] end return [ 200, {'Content-Type'=>'text/html'}, self ] end end return [404, {"Content-Type" => "text/plain"}, ["Directory not found: #{env["PATH_INFO"]}\n"]] end def each files = @files.map{|f| p f; DIR_FILE % f }*"\n" page = DIR_PAGE % [ @path, @path , files ] page.each_line{|l| yield l } end def each_entry @files.each{|e| yield e } end end end