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