1 --[==========================================================================[
2 http.lua: HTTP interface module for VLC
3 --[==========================================================================[
4 Copyright (C) 2007 the VideoLAN team
7 Authors: Antoine Cellerier <dionoea at videolan dot org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 --]==========================================================================]
24 --[==========================================================================[
25 Configuration options:
26 * no_error_detail: If set, do not print the Lua error message when generating
28 * no_index: If set, don't build directory indexes
29 --]==========================================================================]
36 vlc.msg.err("Lua HTTP interface")
42 return (string.gsub(s,"([%^%$%%%.%[%]%*%+%-%?])","%%%1"))
45 function process_raw(filename)
46 local input = io.open(filename):read("*a")
47 -- find the longest [===[ or ]=====] type sequence and make sure that
48 -- we use one that's longer.
50 for str2 in string.gmatch(input,"[%[%]]=*[%[%]]") do
51 if #str < #str2 then str = str2 end
53 str=string.rep("=",#str-1)
56 <?xml version="1.0" encoding="charset" standalone="yes" ?> is still a problem. The closing '?>' needs to be printed using '?<?vlc print ">" ?>' to prevent a parse error.
58 local code0 = string.gsub(input,escape(close_tag)," print(["..str.."[")
59 local code1 = string.gsub(code0,escape(open_tag),"]"..str.."]) ")
60 local code = "print(["..str.."["..code1.."]"..str.."])"
61 --[[ Uncomment to debug
62 if string.match(filename,"vlm_cmd.xml$") then
67 return assert(loadstring(code,filename))
69 function process(filename)
70 vlc.msg.dbg("Loading `"..filename.."'")
71 local mtime = 0 -- vlc.fd.stat(filename).modification_time
72 local func = false -- process_raw(filename)
74 local new_mtime = vlc.fd.stat(filename).modification_time
75 if new_mtime ~= mtime then
76 -- Re-read the file if it changed
77 vlc.msg.dbg("Reloading `"..filename.."'")
78 func = process_raw(filename)
86 function callback_error(path,url,msg)
87 local url = url or "<page unknown>"
88 return [[<html xmlns="http://www.w3.org/1999/xhtml">
90 <title>Error loading ]]..url..[[</title>
93 <h1>Error loading ]]..url..[[</h1><pre>]]..(config.no_error_detail and "Remove configuration option `no_error_detail' on the server to get more information." or tostring(msg))..[[</pre>
95 <a href="http://www.videolan.org/">VideoLAN</a><br/>
96 <a href="http://www.lua.org/manual/5.1/">Lua 5.1 Reference Manual</a>
102 function dirlisting(url,listing)
104 for _,f in ipairs(listing) do
105 if not string.match(f,"^%.") then
106 table.insert(list,"<li><a href='"..f.."'>"..f.."</a></li>")
109 list = table.concat(list)
110 local function callback()
111 return [[<html xmlns="http://www.w3.org/1999/xhtml">
113 <title>Directory listing ]]..url..[[</title>
116 <h1>Directory listing ]]..url..[[</h1><ul>]]..list..[[</ul>
120 return h:file_new(url,"text/html",nil,nil,nil,callback,nil)
123 function file(h,path,url,acl_,mime)
124 local generate_page = process(path)
125 local callback = function(data,request)
126 -- FIXME: I'm sure that we could define a real sandbox
129 local function pageprint(...)
130 for i=1,select("#",...) do
132 table.insert(page,tostring(select(i,...)))
134 table.insert(page," "..tostring(select(i,...)))
138 _G._GET = parse_url_request(request)
139 local oldprint = print
141 local ok, msg = pcall(generate_page)
145 return callback_error(path,url,msg)
147 return table.concat(page)
149 return h:file_new(url or path,mime,nil,nil,acl_,callback,nil)
152 function rawfile(h,path,url,acl_)
153 local filename = path
154 vlc.msg.dbg("Loading `"..filename.."'")
155 local mtime = 0 -- vlc.fd.stat(filename).modification_time
156 local page = false -- io.open(filename):read("*a")
157 local callback = function(data,request)
158 local new_mtime = vlc.fd.stat(filename).modification_time
159 if mtime ~= new_mtime then
160 -- Re-read the file if it changed
161 vlc.msg.dbg("Reloading `"..filename.."'")
162 page = io.open(filename):read("*a")
167 return h:file_new(url or path,nil,nil,nil,acl_,callback,nil)
170 function parse_url_request(request)
171 if not request then return {} end
173 for k,v in string.gmatch(request,"([^=&]+)=?([^=&]*)") do
174 local k_ = vlc.decode_uri(k)
175 local v_ = vlc.decode_uri(v)
181 local function find_datadir(name)
182 local list = vlc.datadir_list(name)
183 for _, l in ipairs(list) do
184 local s = vlc.fd.stat(l)
189 error("Unable to find the `"..name.."' directory.")
191 http_dir = find_datadir("http-lua")
194 local oldpath = package.path
195 package.path = http_dir.."/?.lua"
196 local ok, err = pcall(require,"custom")
198 vlc.msg.warn("Couldn't load "..http_dir.."/custom.lua")
200 vlc.msg.dbg("Loaded "..http_dir.."/custom.lua")
202 package.path = oldpath
209 js = "text/javascript",
211 ico = "image/x-icon",
213 local function load_dir(dir,root,parent_acl)
214 local root = root or "/"
215 local has_index = false
216 if string.match(dir,"/old") then
219 local my_acl = parent_acl
221 local af = dir.."/.hosts"
222 local s = vlc.fd.stat(af)
223 if s and s.type == "file" then
225 my_acl = acl.new(false)
229 local d = vlc.fd.opendir(dir)
230 for _,f in ipairs(d) do
231 if not string.match(f,"^%.") then
232 local s = vlc.fd.stat(dir.."/"..f)
233 if s.type == "file" then
235 if f == "index.html" then
241 local ext = string.match(f,"%.([^%.]-)$")
242 local mime = mimes[ext]
244 if mime and string.match(mime,"^text/") then
245 table.insert(files,file(h,dir.."/"..f,url,my_acl and my_acl.private,mime))
247 table.insert(files,rawfile(h,dir.."/"..f,url,my_acl and my_acl.private))
249 elseif s.type == "dir" then
250 if f == "dialogs" then -- FIXME
252 load_dir(dir.."/"..f,root..f.."/",my_acl)
257 if not has_index and not config.no_index then
258 -- print("Adding index for", root)
259 table.insert(files,dirlisting(root,d,my_acl and my_acl.private))
263 h = httpd.new("localhost",8080)
266 while not die do die = vlc.lock_and_wait() end -- everything happens in callbacks
268 -- FIXME: We shouldn't need to do this ourselves.
270 getmetatable(files[i]).__gc(files[i])