--- Decode MIME encoded data.
--- Returns a table with decoded values.
-function mimedecode( data, boundary, filecb )
-
- local params = { }
-
- -- create a line reader
- local reader = _linereader( data, HTTP_DEFAULT_LINEBUF )
-
- -- state variables
- local in_part = false
- local in_file = false
- local in_fbeg = false
- local in_size = true
-
- local filename
- local buffer
- local field
- local clen = 0
-
- -- try to read all mime parts
- for line, eol in reader do
-
- -- update content length
- clen = clen + line:len()
-
- if clen >= HTTP_MAX_CONTENT then
- in_size = false
- end
-
- -- when no boundary is given, try to find it
- if not boundary then
- boundary = line:match("^%-%-([^\r\n]+)\r?\n$")
- end
-
- -- Got a valid boundary line or reached max allowed size.
- if ( boundary and line:sub(1,2) == "--" and line:len() > #boundary + 2 and
- line:sub( 3, 2 + #boundary ) == boundary ) or not in_size
- then
- -- Flush the data of the previous mime part.
- -- When field and/or buffer are set to nil we should discard
- -- the previous section entirely due to format violations.
- if type(field) == "string" and field:len() > 0 and
- type(buffer) == "string"
- then
- -- According to the rfc the \r\n preceeding a boundary
- -- is assumed to be part of the boundary itself.
- -- Since we are reading line by line here, this crlf
- -- is part of the last line of our section content,
- -- so strip it before storing the buffer.
- buffer = buffer:gsub("\r?\n$","")
-
- -- If we're in a file part and a file callback has been provided
- -- then do a final call and send eof.
- if in_file and type(filecb) == "function" then
- filecb( field, filename, buffer, true )
- params[field] = filename
-
- -- Store buffer.
- else
- params[field] = buffer
- end
- end
-
- -- Reset vars
- buffer = ""
- filename = nil
- field = nil
- in_file = false
-
- -- Abort here if we reached maximum allowed size
- if not in_size then break end
-
- -- Do we got the last boundary?
- if line:len() > #boundary + 4 and
- line:sub( #boundary + 2, #boundary + 4 ) == "--"
- then
- -- No more processing
- in_part = false
-
- -- It's a middle boundary
- else
-
- -- Read headers
- local hlen, headers = extract_headers( reader )
-
- -- Check for valid headers
- if headers['Content-Disposition'] then
-
- -- Got no content type header, assume content-type "text/plain"
- if not headers['Content-Type'] then
- headers['Content-Type'] = 'text/plain'
- end
-
- -- Find field name
- local hdrvals = luci.util.split(
- headers['Content-Disposition'], '; '
- )
-
- -- Valid form data part?
- if hdrvals[1] == "form-data" and hdrvals[2]:match("^name=") then
-
- -- Store field identifier
- field = hdrvals[2]:match('^name="(.+)"$')
-
- -- Do we got a file upload field?
- if #hdrvals == 3 and hdrvals[3]:match("^filename=") then
- in_file = true
- if_fbeg = true
- filename = hdrvals[3]:match('^filename="(.+)"$')
- end
-
- -- Entering next part processing
- in_part = true
- end
- end
- end
-
- -- Processing content
- elseif in_part then
-
- -- XXX: Would be really good to switch from line based to
- -- buffered reading here.
-
-
- -- If we're in a file part and a file callback has been provided
- -- then call the callback and reset the buffer.
- if in_file and type(filecb) == "function" then
-
- -- If we're not processing the first chunk, then call
- if not in_fbeg then
- filecb( field, filename, buffer, false )
- buffer = ""
-
- -- Clear in_fbeg flag after first run
- else
- in_fbeg = false
- end
- end