1-- Some basic checks
2if mg.request_info.request_method ~= "POST" or mg.request_info.content_type:lower():sub(1,19) ~= 'multipart/form-data' then
3  mg.write("HTTP/1.0 400 OK\r\n")
4  mg.write("Connection: close\r\n")
5  mg.write("Content-Type: text/plain; charset=utf-8\r\n")
6  mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
7  mg.write("\r\n")
8  mg.write("Bad request\r\n\r\n")
9  return
10end
11
12-- HTTP headers
13mg.write("HTTP/1.0 200 OK\r\n")
14mg.write("Connection: close\r\n")
15mg.write("Content-Type: text/plain; charset=utf-8\r\n")
16mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
17mg.write("\r\n")
18
19-- Which form sent the data?
20mg.write("Read POST data from " .. mg.request_info.http_headers.Referer .. ":\r\n\r\n")
21
22-- Count some data fields
23local fields = 0
24local datasize = 0
25
26-- Read the entire body data (POST content) into "bdata" variable.
27-- Use a string builder pattern for performance reasons
28stringtab = {}
29bdata = ""
30repeat
31  local add_data = mg.read()
32  if add_data then
33    stringtab[#stringtab+1] = add_data
34  end
35until (add_data == nil);
36bdata = table.concat(stringtab)
37stringtab = nil
38
39-- Get the boundary string.
40bs = "--" .. ((mg.request_info.content_type):upper():match("BOUNDARY=(.*)"));
41
42-- The POST data has to start with the boundary string.
43-- Check this and remove the starting boundary.
44if bdata:sub(1, #bs) ~= bs then
45  error "invalid format of POST data"
46end
47bdata = bdata:sub(#bs)
48
49-- The boundary now starts with CR LF.
50bs = "\r\n" .. bs
51
52-- Now loop through all the parts
53while #bdata>4 do
54   -- Find the header of new part.
55   part_header_end = bdata:find("\r\n\r\n", 1, true)
56
57   -- Parse the header.
58   local form_field_name, file_name
59   h = bdata:sub(1, part_header_end+2)
60   for key,val in h:gmatch("([^%:\r\n]*)%s*%:%s*([^\r\n]*)\r\n") do
61      if key:upper() == "CONTENT-DISPOSITION" then
62          form_field_name = val:match('name=%"([^%"]*)%"')
63          file_name = val:match('filename=%"([^%"]*)%"')
64      end
65   end
66
67   -- Remove the header from "bdata".
68   bdata = bdata:sub(part_header_end+4)
69
70   -- Find the end of the body by locating the boundary string.
71   local part_body_end = bdata:find(bs, 1, true)
72
73   -- Isolate the content, and drop it from "bdata".
74   local form_field_value = bdata:sub(1,part_body_end-1)
75   bdata = bdata:sub(part_body_end+#bs)
76
77   -- Now the data (file content or field value) is isolated: We know form_field_name and form_field_value.
78   -- Here the script should do something useful with the data. This example just sends it back to the client.
79   if form_field_name then
80     mg.write("Field name: " .. form_field_name .. "\r\n")
81   end
82
83   local len = #form_field_value
84   mg.write("Field data length: " .. len .. "\r\n")
85
86   if file_name then
87     mg.write("File name: " .. file_name .. "\r\n")
88     mg.write("File content:\r\n")
89     local maxlen
90     if len>320 then maxlen=320 else maxlen=len end
91
92     for l=0,maxlen,16 do
93       for m=1,16 do
94         local b = form_field_value:byte(l+m)
95         if (b) then
96           mg.write(string.format("%02x ", b))
97         else
98           mg.write("   ")
99         end
100       end
101       mg.write(" -  " .. form_field_value:sub(l+1,l+16):gsub("[%c%z%s]", " ") .. "\r\n")
102     end
103     if maxlen<len then
104       mg.write(string.format("... (+ %u bytes)\r\n", len-maxlen))
105     end
106
107   else
108     -- not a file
109     if len<50 then
110       mg.write("Field value: " .. form_field_value .. "\r\n")
111     else
112       mg.write("Field value: " .. form_field_value:sub(1, 40) .. " .. (" .. len .. " bytes)\r\n")
113     end
114   end
115
116
117   mg.write("\r\n")
118   fields = fields + 1
119   datasize = datasize + len
120
121end
122
123mg.write("Got " .. fields .. " input fields with " .. datasize .. " bytes total\r\n");
124