luci-base: fold luci.http.protocol into luci.http
[project/luci.git] / modules / luci-base / luasrc / http / protocol.lua
diff --git a/modules/luci-base/luasrc/http/protocol.lua b/modules/luci-base/luasrc/http/protocol.lua
deleted file mode 100644 (file)
index 7bbc556..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
--- Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io>
--- Licensed to the public under the Apache License 2.0.
-
--- This class contains several functions useful for http message- and content
--- decoding and to retrive form data from raw http messages.
-
-local require, type, tonumber = require, type, tonumber
-local table, pairs, ipairs, pcall = table, pairs, ipairs, pcall
-
-module "luci.http.protocol"
-
-local ltn12 = require "luci.ltn12"
-local lhttp = require "lucihttp"
-
-HTTP_MAX_CONTENT      = 1024*8         -- 8 kB maximum content size
-
--- from given url or string. Returns a table with urldecoded values.
--- Simple parameters are stored as string values associated with the parameter
--- name within the table. Parameters with multiple values are stored as array
--- containing the corresponding values.
-function urldecode_params(url, tbl)
-       local parser, name
-       local params = tbl or { }
-
-       parser = lhttp.urlencoded_parser(function (what, buffer, length)
-               if what == parser.TUPLE then
-                       name, value = nil, nil
-               elseif what == parser.NAME then
-                       name = lhttp.urldecode(buffer)
-               elseif what == parser.VALUE and name then
-                       params[name] = lhttp.urldecode(buffer) or ""
-               end
-
-               return true
-       end)
-
-       if parser then
-               parser:parse((url or ""):match("[^?]*$"))
-               parser:parse(nil)
-       end
-
-       return params
-end
-
--- separated by "&". Tables are encoded as parameters with multiple values by
--- repeating the parameter name with each value.
-function urlencode_params(tbl)
-       local k, v
-       local n, enc = 1, {}
-       for k, v in pairs(tbl) do
-               if type(v) == "table" then
-                       local i, v2
-                       for i, v2 in ipairs(v) do
-                               if enc[1] then
-                                       enc[n] = "&"
-                                       n = n + 1
-                               end
-
-                               enc[n+0] = lhttp.urlencode(k)
-                               enc[n+1] = "="
-                               enc[n+2] = lhttp.urlencode(v2)
-                               n = n + 3
-                       end
-               else
-                       if enc[1] then
-                               enc[n] = "&"
-                               n = n + 1
-                       end
-
-                       enc[n+0] = lhttp.urlencode(k)
-                       enc[n+1] = "="
-                       enc[n+2] = lhttp.urlencode(v)
-                       n = n + 3
-               end
-       end
-
-       return table.concat(enc, "")
-end
-
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table within the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
--- If an optional file callback function is given then it is feeded with the
--- file contents chunk by chunk and only the extracted file name is stored
--- within the params table. The callback function will be called subsequently
--- with three arguments:
---  o Table containing decoded (name, file) and raw (headers) mime header data
---  o String value containing a chunk of the file data
---  o Boolean which indicates wheather the current chunk is the last one (eof)
-function mimedecode_message_body(src, msg, file_cb)
-       local parser, header, field
-       local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
-
-       parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length)
-               if what == parser.PART_INIT then
-                       field = { }
-
-               elseif what == parser.HEADER_NAME then
-                       header = buffer:lower()
-
-               elseif what == parser.HEADER_VALUE and header then
-                       if header:lower() == "content-disposition" and
-                          lhttp.header_attribute(buffer, nil) == "form-data"
-                       then
-                               field.name = lhttp.header_attribute(buffer, "name")
-                               field.file = lhttp.header_attribute(buffer, "filename")
-                       end
-
-                       if field.headers then
-                               field.headers[header] = buffer
-                       else
-                               field.headers = { [header] = buffer }
-                       end
-
-               elseif what == parser.PART_BEGIN then
-                       return not field.file
-
-               elseif what == parser.PART_DATA and field.name and length > 0 then
-                       if field.file then
-                               if file_cb then
-                                       file_cb(field, buffer, false)
-                                       msg.params[field.name] = msg.params[field.name] or field
-                               else
-                                       if not field.fd then
-                                               local ok, nx = pcall(require, "nixio")
-                                               field.fd = ok and nx.mkstemp(field.name)
-                                       end
-
-                                       if field.fd then
-                                               field.fd:write(buffer)
-                                               msg.params[field.name] = msg.params[field.name] or field
-                                       end
-                               end
-                       else
-                               field.value = buffer
-                       end
-
-               elseif what == parser.PART_END and field.name then
-                       if field.file and msg.params[field.name] then
-                               if file_cb then
-                                       file_cb(field, "", true)
-                               elseif field.fd then
-                                       field.fd:seek(0, "set")
-                               end
-                       else
-                               msg.params[field.name] = field.value or ""
-                       end
-
-                       field = nil
-
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end)
-
-       return ltn12.pump.all(src, function (chunk)
-               len = len + (chunk and #chunk or 0)
-
-               if maxlen and len > maxlen + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               end
-
-               if not parser or not parser:parse(chunk) then
-                       return nil, err
-               end
-
-               return true
-       end)
-end
-
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table within the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
-function urldecode_message_body(src, msg)
-       local err, name, value, parser
-       local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
-
-       parser = lhttp.urlencoded_parser(function (what, buffer, length)
-               if what == parser.TUPLE then
-                       name, value = nil, nil
-               elseif what == parser.NAME then
-                       name = lhttp.urldecode(buffer)
-               elseif what == parser.VALUE and name then
-                       msg.params[name] = lhttp.urldecode(buffer) or ""
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end)
-
-       return ltn12.pump.all(src, function (chunk)
-               len = len + (chunk and #chunk or 0)
-
-               if maxlen and len > maxlen + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               elseif len > HTTP_MAX_CONTENT then
-                       return nil, "Message body size exceeds maximum allowed length"
-               end
-
-               if not parser or not parser:parse(chunk) then
-                       return nil, err
-               end
-
-               return true
-       end)
-end
-
--- This function will examine the Content-Type within the given message object
--- to select the appropriate content decoder.
--- Currently the application/x-www-urlencoded and application/form-data
--- mime types are supported. If the encountered content encoding can't be
--- handled then the whole message body will be stored unaltered as "content"
--- property within the given message object.
-function parse_message_body(src, msg, filecb)
-       local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
-
-       -- Is it multipart/mime ?
-       if msg.env.REQUEST_METHOD == "POST" and
-          ctype == "multipart/form-data"
-       then
-               return mimedecode_message_body( src, msg, filecb )
-
-       -- Is it application/x-www-form-urlencoded ?
-       elseif msg.env.REQUEST_METHOD == "POST" and
-              ctype == "application/x-www-form-urlencoded"
-       then
-               return urldecode_message_body( src, msg, filecb )
-
-
-       -- Unhandled encoding
-       -- If a file callback is given then feed it chunk by chunk, else
-       -- store whole buffer in message.content
-       else
-
-               local sink
-
-               -- If we have a file callback then feed it
-               if type(filecb) == "function" then
-                       local meta = {
-                               name = "raw",
-                               encoding = msg.env.CONTENT_TYPE
-                       }
-                       sink = function( chunk )
-                               if chunk then
-                                       return filecb(meta, chunk, false)
-                               else
-                                       return filecb(meta, nil, true)
-                               end
-                       end
-               -- ... else append to .content
-               else
-                       msg.content = ""
-                       msg.content_length = 0
-
-                       sink = function( chunk )
-                               if chunk then
-                                       if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
-                                               msg.content        = msg.content        .. chunk
-                                               msg.content_length = msg.content_length + #chunk
-                                               return true
-                                       else
-                                               return nil, "POST data exceeds maximum allowed length"
-                                       end
-                               end
-                               return true
-                       end
-               end
-
-               -- Pump data...
-               while true do
-                       local ok, err = ltn12.pump.step( src, sink )
-
-                       if not ok and err then
-                               return nil, err
-                       elseif not ok then -- eof
-                               return true
-                       end
-               end
-
-               return true
-       end
-end