* libs/core: Moved ltn12 inside LuCI to avoid package conflicts with luasocket
[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.mime = luci.http.protocol.mime
34 self.date = luci.http.protocol.date
35 self.cond = luci.http.protocol.conditionals
36 end
37
38 function Simple.getfile(self, uri)
39 local file = self.docroot .. uri:gsub("%.%./+", "")
40 local stat = luci.fs.stat(file)
41
42 return file, stat
43 end
44
45 function Simple.handle_get(self, request, sourcein, sinkerr)
46 local file, stat = self:getfile(request.env.PATH_INFO)
47
48 if stat then
49 if stat.type == "regular" then
50
51 -- Generate Entity Tag
52 local etag = self.cond.mk_etag( stat )
53
54 -- Check conditionals
55 local ok, code, hdrs
56
57 ok, code, hdrs = self.cond.if_modified_since( request, stat )
58 if ok then
59 ok, code, hdrs = self.cond.if_match( request, stat )
60 if ok then
61 ok, code, hdrs = self.cond.if_unmodified_since( request, stat )
62 if ok then
63 ok, code, hdrs = self.cond.if_none_match( request, stat )
64 if ok then
65 -- Send Response
66 return Response(
67 200, {
68 ["Date"] = self.date.to_http( os.time() );
69 ["Last-Modified"] = self.date.to_http( stat.mtime );
70 ["Content-Type"] = self.mime.to_mime( file );
71 ["Content-Length"] = stat.size;
72 ["ETag"] = etag;
73 }
74 ), ltn12.source.file(io.open(file))
75 else
76 return Response( code, hdrs or { } )
77 end
78 else
79 return Response( code, hdrs or { } )
80 end
81 else
82 return Response( code, hdrs or { } )
83 end
84 else
85 return Response( code, hdrs or { } )
86 end
87
88 elseif stat.type == "directory" then
89
90 local ruri = request.request_uri:gsub("/$","")
91 local root = self.docroot:gsub("/$","")
92
93 -- check for index files
94 local index_candidates = {
95 "index.html", "index.htm", "default.html", "default.htm",
96 "index.txt", "default.txt"
97 }
98
99 -- try to find an index file and redirect to it
100 for i, candidate in ipairs( index_candidates ) do
101 local istat = luci.fs.stat(
102 root .. "/" .. ruri .. "/" .. candidate
103 )
104
105 if istat ~= nil and istat.type == "regular" then
106 return Response( 301, {
107 ["Location"] = ruri .. "/" .. candidate
108 } ), ltn12.source.empty()
109 end
110 end
111
112
113 local html = string.format(
114 '<?xml version="1.0" encoding="UTF-8"?>\n' ..
115 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' ..
116 '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' ..
117 '<html xmlns="http://www.w3.org/1999/xhtml" ' ..
118 'xml:lang="en" lang="en">\n' ..
119 '<head>\n' ..
120 '<title>Index of %s/</title>\n' ..
121 '</head><body><h1>Index of %s/</h1><hr /><ul>',
122 ruri, ruri
123 )
124
125 for i, e in luci.util.vspairs( luci.fs.dir( file ) ) do
126
127 if e ~= '.' then
128 local estat = luci.fs.stat( file .. "/" .. e )
129
130 if estat.type == "directory" then
131 html = html .. string.format(
132 '<li><p><a href="%s/%s/">%s/</a> ' ..
133 '<small>(directory)</small><br />' ..
134 '<small>Changed: %s</small></li>',
135 ruri, e, e,
136 self.date.to_http( estat.mtime )
137 )
138 else
139 html = html .. string.format(
140 '<li><p><a href="%s/%s">%s</a> ' ..
141 '<small>(%s)</small><br />' ..
142 '<small>Size: %i Bytes | Changed: %s</small></li>',
143 ruri, e, e, self.mime.to_mime( e ),
144 estat.size, self.date.to_http( estat.mtime )
145 )
146 end
147 end
148 end
149
150 html = html .. '</ul><hr /></body></html>'
151
152 return Response(
153 200, {
154 ["Date"] = self.date.to_http( os.time() );
155 ["Content-Type"] = "text/html";
156 }
157 ), ltn12.source.string(html)
158 else
159 return self:failure(403, "Unable to transmit " .. stat.type .. " " .. file)
160 end
161 else
162 return self:failure(404, "No such file: " .. file)
163 end
164 end
165
166 function Simple.handle_head(self, ...)
167 local response, sourceout = self:handle_get(...)
168 return response
169 end