Приведение в более приличный вид с выносом используемых базовых функций в отдельную...
[lua-filemanager.git] / usr / lib / lua / cgi.lua
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