Skip to content

Commit b4b5d84

Browse files
committed
Lua backend for OpenWRT added.
1 parent 032b321 commit b4b5d84

File tree

6 files changed

+654
-0
lines changed

6 files changed

+654
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
config filemanager 'config'
3+
option basedir '/mnt'
4+
option tmpdir '/tmp'
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
-- cgi util module
2+
3+
local uci = require("uci")
4+
local u_c = uci.cursor()
5+
local tmppath = u_c.get("filemanager","config","tmpdir")
6+
7+
local prevbuf = ""
8+
local blocksize = 4096
9+
local _M = {}
10+
11+
_M.statusmsg = {
12+
[200] = "OK",
13+
[206] = "Partial Content",
14+
[301] = "Moved Permanently",
15+
[302] = "Found",
16+
[304] = "Not Modified",
17+
[400] = "Bad Request",
18+
[403] = "Forbidden",
19+
[404] = "Not Found",
20+
[405] = "Method Not Allowed",
21+
[408] = "Request Time-out",
22+
[411] = "Length Required",
23+
[412] = "Precondition Failed",
24+
[416] = "Requested range not satisfiable",
25+
[500] = "Internal Server Error",
26+
[503] = "Server Unavailable",
27+
}
28+
29+
-- call this function passing an empy table. use that table for successive calls.
30+
function _M.new(req)
31+
req.content_length = os.getenv("CONTENT_LENGTH")
32+
req.request_method = os.getenv("REQUEST_METHOD")
33+
if req.request_method == "POST" then
34+
req.content_type, req.boundary = string.match(os.getenv("CONTENT_TYPE"),"^(multipart/form%-data); boundary=\"?(.+)\"?$")
35+
req.boundary = "--" .. req.boundary
36+
end
37+
-- this is useful only if you have /tmp on tmpfs like in openwrt. otherwise can be set to ""
38+
req.tempdir = tmppath
39+
req.post = {}
40+
end
41+
42+
-- this function is needed to clean temp file since and hide implementation details
43+
function _M.cleanup(req)
44+
for k, v in pairs(req.post) do
45+
for j, v in pairs(req.post[k]) do
46+
if req.post[k][j].tempname then
47+
os.remove(req.post[k][j].tempname) -- if file unused
48+
os.remove("/tmp/" .. string.match(req.post[k][j].tempname,"^" .. req.tempdir .. "(.+)"))
49+
end
50+
end
51+
end
52+
end
53+
54+
-- del: delimiter
55+
-- return chunk (string), found (boolean)
56+
local function chunkread(del)
57+
local buf, found = 0
58+
local del = del or "\r\n"
59+
60+
buf = io.read(math.max(0,blocksize + #del - #prevbuf))
61+
if prevbuf ~= "" then buf = prevbuf .. ( buf or "" ); prevbuf = "" end
62+
if not buf then return end
63+
64+
s, e = string.find(buf,del,1,true)
65+
if s and e then
66+
found = 1
67+
prevbuf = string.sub(buf,e+1)
68+
buf = string.sub(buf,1,s-1)
69+
else
70+
prevbuf = string.sub(buf,math.min(blocksize,#buf)+1)
71+
buf = string.sub(buf,1,math.min(blocksize,#buf))
72+
end
73+
74+
return buf, found
75+
end
76+
77+
78+
function _M.parse_request_body (req)
79+
local chunk, found, type, tempname, tempfile
80+
local param = {}
81+
82+
-- read first boundary line
83+
chunk, found = chunkread(req.boundary)
84+
chunk, found = chunkread("\r\n")
85+
while chunk == "" do
86+
-- read part headers and get parameters value
87+
repeat
88+
chunk, found = chunkread("\r\n")
89+
if not found then return 400, "Malformed POST. Missing Part Header or Part Header too long." end
90+
string.gsub(chunk, ';%s*([^%s=]+)="(.-[^\\])"', function(k, v) param[k] = v end)
91+
param.type = param.type or string.match(chunk, "^Content%-Type: (.+)$")
92+
until chunk == ""
93+
94+
-- prepare file data read
95+
if not param.name then return 400, "Malformed POST. Check Header parameters." end
96+
param.size=0
97+
param.tempname = req.tempdir .. string.match(os.tmpname(), "^/tmp(.+)")
98+
tempfile = io.open(param.tempname, "w")
99+
100+
-- read part body content until boundary
101+
repeat
102+
chunk, found = chunkread("\r\n" .. req.boundary)
103+
if not chunk then return 400, "Malformed POST. Incomplete Part received." end
104+
tempfile:write(chunk)
105+
param.size = param.size + #chunk
106+
until found
107+
tempfile:close()
108+
req.post[param.name] = req.post[param.name] or {}
109+
table.insert(req.post[param.name], 1, param)
110+
param = {}
111+
112+
-- read after boundary. if CRLF ("") repeat. if "--" end POST processing
113+
chunk, found = chunkread("\r\n")
114+
end
115+
116+
if found and chunk == "--" then return 0, "OK" end
117+
return 400, "Malformed POST. Boundary not properly ended with CRLF or --."
118+
end
119+
120+
return _M
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/lua
2+
3+
local uci = require "uci"
4+
local fs = require "nixio.fs"
5+
local u_c = uci.cursor()
6+
local basepath = u_c.get("filemanager","config","basedir")
7+
8+
local _M = {}
9+
10+
local bp = basepath:match("(.*)/")
11+
if bp and bp ~= "" then
12+
basepath = bp
13+
end
14+
_M.basepath = basepath;
15+
16+
function _M.is_in_dir(file, dir)
17+
if file == dir then
18+
return true
19+
else
20+
return file:sub(1, #dir) == dir
21+
end
22+
end
23+
24+
function _M.path_valid(path)
25+
return _M.is_in_dir(path,_M.basepath) and fs.access(path,"r")
26+
end
27+
28+
function _M.dir_path_valid(path)
29+
return _M.is_in_dir(path,_M.basepath) and fs.stat(path,"type")=="dir" and fs.access(path,"w")
30+
end
31+
32+
function _M.new_path_valid(path)
33+
local dirpath = fs.dirname(path)
34+
return _M.is_in_dir(dirpath,_M.basepath) and fs.access(dirpath,"w")
35+
end
36+
37+
function _M.make_path(path)
38+
local realpath = fs.realpath(_M.basepath..'/'..path)
39+
if _M.path_valid(realpath) then
40+
return realpath
41+
else
42+
return nil
43+
end
44+
end
45+
46+
function _M.make_new_path(path)
47+
local realpath = fs.realpath(fs.dirname(_M.basepath..'/'..path))..'/'..fs.basename(path)
48+
if _M.new_path_valid(realpath) then
49+
return realpath
50+
else
51+
return nil
52+
end
53+
end
54+
55+
function _M.make_dir_path(path)
56+
local realpath = fs.realpath(_M.basepath..'/'..path)
57+
if _M.dir_path_valid(realpath) then
58+
return realpath
59+
else
60+
return nil
61+
end
62+
end
63+
64+
function _M.rm(item)
65+
local ftype = fs.stat(item,"type")
66+
if ftype == "reg" then
67+
return fs.remove(item)
68+
elseif ftype == "dir" then
69+
local dir = fs.dir(item)
70+
for file in dir do
71+
if not _M.rm(item..'/'..file) then
72+
return false
73+
end
74+
end
75+
return fs.rmdir(item)
76+
else
77+
return false
78+
end
79+
end
80+
81+
function _M.chmod(item,mode,recursive)
82+
local result = fs.chmod(item,mode)
83+
if result and recursive then
84+
local dir = fs.dir(item)
85+
if dir then
86+
for file in dir do
87+
local ftype = fs.stat(item..'/'..file,"type")
88+
if ftype == "dir" then
89+
result = _M.chmod(item..'/'..file,mode,recursive)
90+
elseif ftype == "reg" then
91+
result = _M.chmod(item..'/'..file,string.gsub(mode,"x","-"),false)
92+
end
93+
if not result then
94+
break
95+
end
96+
end
97+
end
98+
end
99+
return result
100+
end
101+
102+
return _M

0 commit comments

Comments
 (0)