modules/freifunk: set ipv4 broadcast to 255.255.255.255 by default
[project/luci.git] / libs / httpd / luasrc / httpd / handler / file.lua
1 --[[
2
3 HTTP server implementation for LuCI - file handler
4 (c) 2008 Steven Barth <steven@midlink.org>
5 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11         http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 module("luci.httpd.handler.file", package.seeall)
18
19 require("luci.httpd.module")
20 require("luci.http.protocol.date")
21 require("luci.http.protocol.mime")
22 require("luci.http.protocol.conditionals")
23 require("luci.fs")
24 local ltn12 = require("luci.ltn12")
25
26 Simple = luci.util.class(luci.httpd.module.Handler)
27 Response = luci.httpd.module.Response
28
29 function Simple.__init__(self, docroot, dirlist)
30         luci.httpd.module.Handler.__init__(self)
31         self.docroot = docroot
32         self.dirlist = dirlist and true or false
33         self.proto   = luci.http.protocol
34         self.mime    = luci.http.protocol.mime
35         self.date    = luci.http.protocol.date
36         self.cond    = luci.http.protocol.conditionals
37 end
38
39 function Simple.getfile(self, uri)
40         local file = self.docroot .. uri:gsub("%.%./+", "")
41         local stat = luci.fs.stat(file)
42
43         return file, stat
44 end
45
46 function Simple.handle_get(self, request, sourcein, sinkerr)
47         local file, stat = self:getfile( self.proto.urldecode( request.env.PATH_INFO, true ) )
48
49         if stat then
50                 if stat.type == "regular" then
51
52                         -- Generate Entity Tag
53                         local etag = self.cond.mk_etag( stat )
54
55                         -- Check conditionals
56                         local ok, code, hdrs
57
58                         ok, code, hdrs = self.cond.if_modified_since( request, stat )
59                         if ok then
60                                 ok, code, hdrs = self.cond.if_match( request, stat )
61                                 if ok then
62                                         ok, code, hdrs = self.cond.if_unmodified_since( request, stat )
63                                         if ok then
64                                                 ok, code, hdrs = self.cond.if_none_match( request, stat )
65                                                 if ok then
66                                                         local f, err = io.open(file)
67
68                                                         if f then
69                                                                 -- Send Response
70                                                                 return Response(
71                                                                         200, {
72                                                                                 ["Date"]           = self.date.to_http( os.time() );
73                                                                                 ["Last-Modified"]  = self.date.to_http( stat.mtime );
74                                                                                 ["Content-Type"]   = self.mime.to_mime( file );
75                                                                                 ["Content-Length"] = stat.size;
76                                                                                 ["ETag"]           = etag;
77                                                                         }
78                                                                 ), ltn12.source.file(f)
79                                                         else
80                                                                 return self:failure( 403, err:gsub("^.+: ", "") )
81                                                         end
82                                                 else
83                                                         return Response( code, hdrs or { } )
84                                                 end
85                                         else
86                                                 return Response( code, hdrs or { } )
87                                         end
88                                 else
89                                         return Response( code, hdrs or { } )
90                                 end
91                         else
92                                 return Response( code, hdrs or { } )
93                         end
94
95                 elseif stat.type == "directory" then
96
97                         local ruri = request.request_uri:gsub("/$","")
98                         local duri = self.proto.urldecode( ruri, true )
99                         local root = self.docroot:gsub("/$","")
100
101                         -- check for index files
102                         local index_candidates = {
103                                 "index.html", "index.htm", "default.html", "default.htm",
104                                 "index.txt", "default.txt"
105                         }
106
107                         -- try to find an index file and redirect to it
108                         for i, candidate in ipairs( index_candidates ) do
109                                 local istat = luci.fs.stat(
110                                         root .. "/" .. duri .. "/" .. candidate
111                                 )
112
113                                 if istat ~= nil and istat.type == "regular" then
114                                         return Response( 301, {
115                                                 ["Location"] = ruri .. "/" .. candidate
116                                         } ), ltn12.source.empty()
117                                 end
118                         end
119
120
121                         local html = string.format(
122                                 '<?xml version="1.0" encoding="ISO-8859-15"?>\n' ..
123                                 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" '  ..
124                                         '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' ..
125                                 '<html xmlns="http://www.w3.org/1999/xhtml" '                ..
126                                         'xml:lang="en" lang="en">\n'                             ..
127                                 '<head>\n'                                                   ..
128                                 '<title>Index of %s/</title>\n'                              ..
129                                 '<style type="text/css"><!--\n'                              ..
130                                         'body { background-color:#FFFFFF; color:#000000 } '      ..
131                                         'li { border-bottom:1px dotted #CCCCCC; padding:3px } '  ..
132                                         'small { font-size:60%%; color:#999999 } '               ..
133                                         'p { margin:0 }'                                         ..
134                                 '\n--></style></head><body><h1>Index of %s/</h1><hr /><ul>',
135                                         duri, duri
136                         )
137
138                         local entries = luci.fs.dir( file )
139
140                         if type(entries) == "table" then
141                                 for i, e in luci.util.spairs(
142                                         entries, function(a,b)
143                                                 if entries[a] == '..' then
144                                                         return true
145                                                 elseif entries[b] == '..' then
146                                                         return false
147                                                 else
148                                                         return ( entries[a] < entries[b] )
149                                                 end
150                                         end
151                                 ) do
152                                         if e ~= '.' and ( e == '..' or e:sub(1,1) ~= '.' ) then
153                                                 local estat = luci.fs.stat( file .. "/" .. e )
154
155                                                 if estat.type == "directory" then
156                                                         html = html .. string.format(
157                                                                 '<li><p><a href="%s/%s/">%s/</a> '           ..
158                                                                 '<small>(directory)</small><br />'           ..
159                                                                 '<small>Changed: %s</small></li>',
160                                                                         ruri, self.proto.urlencode( e ), e,
161                                                                         self.date.to_http( estat.mtime )
162                                                         )
163                                                 else
164                                                         html = html .. string.format(
165                                                                 '<li><p><a href="%s/%s">%s</a> '             ..
166                                                                 '<small>(%s)</small><br />'                  ..
167                                                                 '<small>Size: %i Bytes | '                   ..
168                                                                         'Changed: %s</small></li>',
169                                                                         ruri, self.proto.urlencode( e ), e,
170                                                                         self.mime.to_mime( e ),
171                                                                         estat.size, self.date.to_http( estat.mtime )
172                                                         )
173                                                 end
174                                         end
175                                 end
176
177                                 html = html .. '</ul><hr /></body></html>'
178
179                                 return Response(
180                                         200, {
181                                                 ["Date"]         = self.date.to_http( os.time() );
182                                                 ["Content-Type"] = "text/html; charset=ISO-8859-15";
183                                         }
184                                 ), ltn12.source.string(html)
185                         else
186                                 return self:failure(403, "Permission denied")
187                         end
188                 else
189                         return self:failure(403, "Unable to transmit " .. stat.type .. " " .. file)
190                 end
191         else
192                 return self:failure(404, "No such file: " .. file)
193         end
194 end
195
196 function Simple.handle_head(self, ...)
197         local response, sourceout = self:handle_get(...)
198         return response
199 end