6 # Rack::Directory serves entries below the +root+ given, according to the
7 # path info of the Rack request. If a directory is found, the file's contents
8 # will be presented in an html based index. If a file is found, the env will
9 # be passed to the specified +app+.
11 # If +app+ is not specified, a Rack::File of the same +root+ will be used.
14 DIR_FILE
= "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
18 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
19 <style type='text/css'>
20 table { width:100%%; }
21 .name { text-align:left; }
22 .size, .mtime { text-align:right; }
24 .mtime { width:15em; }
31 <th class='name'>Name</th>
32 <th class='size'>Size</th>
33 <th class='type'>Type</th>
34 <th class='mtime'>Last Modified</th>
43 attr_accessor
:root, :path
45 def initialize(root
, app
=nil)
46 @root = F
.expand_path(root
)
47 @app = app
|| Rack
::File.new(@root)
58 @script_name = env['SCRIPT_NAME']
59 @path_info = Utils
.unescape(env['PATH_INFO'])
61 if forbidden
= check_forbidden
64 @path = F
.join(@root, @path_info)
70 return unless @path_info.include? ".."
73 size
= Rack
::Utils.bytesize(body
)
74 return [403, {"Content-Type" => "text/plain","Content-Length" => size
.to_s
}, [body
]]
78 @files = [['../','Parent Directory','','','']]
79 glob
= F
.join(@path, '*')
81 Dir
[glob
].sort
.each
do |node
|
84 basename
= F
.basename(node
)
87 url
= F
.join(@script_name, @path_info, basename
)
89 type
= stat
.directory
? ? 'directory' : Mime
.mime_type(ext
)
90 size
= stat
.directory
? ? '-' : filesize_format(size
)
91 mtime
= stat
.mtime
.httpdate
92 url
<< '/' if stat
.directory
?
93 basename
<< '/' if stat
.directory
?
95 @files << [ url
, basename
, size
, type
, mtime
]
98 return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
101 def stat(node
, max
= 10)
103 rescue Errno
::ENOENT, Errno
::ELOOP
107 # TODO: add correct response if not readable, not sure if 404 is the best
110 @stat = F
.stat(@path)
113 return @app.call(@env) if @stat.file
?
114 return list_directory
if @stat.directory
?
116 raise Errno
::ENOENT, 'No such file or directory'
119 rescue Errno
::ENOENT, Errno
::ELOOP
120 return entity_not_found
124 body
= "Entity not found: #{@path_info}\n"
125 size
= Rack
::Utils.bytesize(body
)
126 return [404, {"Content-Type" => "text/plain", "Content-Length" => size
.to_s
}, [body
]]
130 show_path
= @path.sub(/^#{@root}/,'')
131 files
= @files.map
{|f
| DIR_FILE
% f
}*"\n"
132 page
= DIR_PAGE
% [ show_path
, show_path
, files
]
133 page
.each_line
{|l
| yield l
}
145 def filesize_format(int
)
146 FILESIZE_FORMAT
.each
do |format
, size
|
147 return format
% (int
.to_f
/ size
) if int
>= size