luci-base: move some generic classes into a separate luci-base-libs package
authorSven Roederer <[email protected]>
Mon, 1 Jul 2019 19:52:07 +0000 (21:52 +0200)
committerJo-Philipp Wich <[email protected]>
Sun, 19 Jul 2020 18:14:22 +0000 (20:14 +0200)
The new package luci-base-libs provides the modules that not strictly relate
to the web-interface of luci. By separating these libs they can be used by
other packages without having to install the web-components.
This change was inspired by providing a shell-only interface for 4MB-flash
devices, by keeping as much code common with a full install.

Signed-off-by: Sven Roederer <[email protected]>
15 files changed:
libs/luci-lib-base/Makefile [new file with mode: 0644]
libs/luci-lib-base/luasrc/debug.lua [new file with mode: 0644]
libs/luci-lib-base/luasrc/http.lua [new file with mode: 0644]
libs/luci-lib-base/luasrc/http.luadoc [new file with mode: 0644]
libs/luci-lib-base/luasrc/ltn12.lua [new file with mode: 0644]
libs/luci-lib-base/luasrc/util.lua [new file with mode: 0644]
libs/luci-lib-base/luasrc/util.luadoc [new file with mode: 0644]
libs/luci-lib-httpclient/Makefile
libs/luci-lib-httpprotoutils/Makefile
modules/luci-base/luasrc/debug.lua [deleted file]
modules/luci-base/luasrc/http.lua [deleted file]
modules/luci-base/luasrc/http.luadoc [deleted file]
modules/luci-base/luasrc/ltn12.lua [deleted file]
modules/luci-base/luasrc/util.lua [deleted file]
modules/luci-base/luasrc/util.luadoc [deleted file]

diff --git a/libs/luci-lib-base/Makefile b/libs/luci-lib-base/Makefile
new file mode 100644 (file)
index 0000000..35b1836
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team <[email protected]>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=basic libraries for luci
+LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +luci-lib-jsonc +liblucihttp-lua
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/libs/luci-lib-base/luasrc/debug.lua b/libs/luci-lib-base/luasrc/debug.lua
new file mode 100644 (file)
index 0000000..8ff1bb6
--- /dev/null
@@ -0,0 +1,37 @@
+local debug = require "debug"
+local io = require "io"
+local collectgarbage, floor = collectgarbage, math.floor
+
+module "luci.debug"
+__file__ = debug.getinfo(1, 'S').source:sub(2)
+
+-- Enables the memory tracer with given flags and returns a function to disable the tracer again
+function trap_memtrace(flags, dest)
+       flags = flags or "clr"
+       local tracefile = io.open(dest or "/tmp/memtrace", "w")
+       local peak = 0
+
+       local function trap(what, line)
+               local info = debug.getinfo(2, "Sn")
+               local size = floor(collectgarbage("count"))
+               if size > peak then
+                       peak = size
+               end
+               if tracefile then
+                       tracefile:write(
+                               "[", what, "] ", info.source, ":", (line or "?"), "\t",
+                               (info.namewhat or ""), "\t",
+                               (info.name or ""), "\t",
+                               size, " (", peak, ")\n"
+                       )
+               end
+       end
+
+       debug.sethook(trap, flags)
+
+       return function()
+               debug.sethook()
+               tracefile:close()
+       end
+end
+
diff --git a/libs/luci-lib-base/luasrc/http.lua b/libs/luci-lib-base/luasrc/http.lua
new file mode 100644 (file)
index 0000000..20b55f2
--- /dev/null
@@ -0,0 +1,554 @@
+-- Copyright 2008 Steven Barth <[email protected]>
+-- Copyright 2010-2018 Jo-Philipp Wich <[email protected]>
+-- Licensed to the public under the Apache License 2.0.
+
+local util  = require "luci.util"
+local coroutine = require "coroutine"
+local table = require "table"
+local lhttp = require "lucihttp"
+local nixio = require "nixio"
+local ltn12 = require "luci.ltn12"
+
+local table, ipairs, pairs, type, tostring, tonumber, error =
+       table, ipairs, pairs, type, tostring, tonumber, error
+
+module "luci.http"
+
+HTTP_MAX_CONTENT      = 1024*100               -- 100 kB maximum content size
+
+context = util.threadlocal()
+
+Request = util.class()
+function Request.__init__(self, env, sourcein, sinkerr)
+       self.input = sourcein
+       self.error = sinkerr
+
+
+       -- File handler nil by default to let .content() work
+       self.filehandler = nil
+
+       -- HTTP-Message table
+       self.message = {
+               env = env,
+               headers = {},
+               params = urldecode_params(env.QUERY_STRING or ""),
+       }
+
+       self.parsed_input = false
+end
+
+function Request.formvalue(self, name, noparse)
+       if not noparse and not self.parsed_input then
+               self:_parse_input()
+       end
+
+       if name then
+               return self.message.params[name]
+       else
+               return self.message.params
+       end
+end
+
+function Request.formvaluetable(self, prefix)
+       local vals = {}
+       prefix = prefix and prefix .. "." or "."
+
+       if not self.parsed_input then
+               self:_parse_input()
+       end
+
+       local void = self.message.params[nil]
+       for k, v in pairs(self.message.params) do
+               if k:find(prefix, 1, true) == 1 then
+                       vals[k:sub(#prefix + 1)] = tostring(v)
+               end
+       end
+
+       return vals
+end
+
+function Request.content(self)
+       if not self.parsed_input then
+               self:_parse_input()
+       end
+
+       return self.message.content, self.message.content_length
+end
+
+function Request.getcookie(self, name)
+       return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
+end
+
+function Request.getenv(self, name)
+       if name then
+               return self.message.env[name]
+       else
+               return self.message.env
+       end
+end
+
+function Request.setfilehandler(self, callback)
+       self.filehandler = callback
+
+       if not self.parsed_input then
+               return
+       end
+
+       -- If input has already been parsed then uploads are stored as unlinked
+       -- temporary files pointed to by open file handles in the parameter
+       -- value table. Loop all params, and invoke the file callback for any
+       -- param with an open file handle.
+       local name, value
+       for name, value in pairs(self.message.params) do
+               if type(value) == "table" then
+                       while value.fd do
+                               local data = value.fd:read(1024)
+                               local eof = (not data or data == "")
+
+                               callback(value, data, eof)
+
+                               if eof then
+                                       value.fd:close()
+                                       value.fd = nil
+                               end
+                       end
+               end
+       end
+end
+
+function Request._parse_input(self)
+       parse_message_body(
+                self.input,
+                self.message,
+                self.filehandler
+       )
+       self.parsed_input = true
+end
+
+function close()
+       if not context.eoh then
+               context.eoh = true
+               coroutine.yield(3)
+       end
+
+       if not context.closed then
+               context.closed = true
+               coroutine.yield(5)
+       end
+end
+
+function content()
+       return context.request:content()
+end
+
+function formvalue(name, noparse)
+       return context.request:formvalue(name, noparse)
+end
+
+function formvaluetable(prefix)
+       return context.request:formvaluetable(prefix)
+end
+
+function getcookie(name)
+       return context.request:getcookie(name)
+end
+
+-- or the environment table itself.
+function getenv(name)
+       return context.request:getenv(name)
+end
+
+function setfilehandler(callback)
+       return context.request:setfilehandler(callback)
+end
+
+function header(key, value)
+       if not context.headers then
+               context.headers = {}
+       end
+       context.headers[key:lower()] = value
+       coroutine.yield(2, key, value)
+end
+
+function prepare_content(mime)
+       if not context.headers or not context.headers["content-type"] then
+               if mime == "application/xhtml+xml" then
+                       if not getenv("HTTP_ACCEPT") or
+                         not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
+                               mime = "text/html; charset=UTF-8"
+                       end
+                       header("Vary", "Accept")
+               end
+               header("Content-Type", mime)
+       end
+end
+
+function source()
+       return context.request.input
+end
+
+function status(code, message)
+       code = code or 200
+       message = message or "OK"
+       context.status = code
+       coroutine.yield(1, code, message)
+end
+
+-- This function is as a valid LTN12 sink.
+-- If the content chunk is nil this function will automatically invoke close.
+function write(content, src_err)
+       if not content then
+               if src_err then
+                       error(src_err)
+               else
+                       close()
+               end
+               return true
+       elseif #content == 0 then
+               return true
+       else
+               if not context.eoh then
+                       if not context.status then
+                               status()
+                       end
+                       if not context.headers or not context.headers["content-type"] then
+                               header("Content-Type", "text/html; charset=utf-8")
+                       end
+                       if not context.headers["cache-control"] then
+                               header("Cache-Control", "no-cache")
+                               header("Expires", "0")
+                       end
+                       if not context.headers["x-frame-options"] then
+                               header("X-Frame-Options", "SAMEORIGIN")
+                       end
+                       if not context.headers["x-xss-protection"] then
+                               header("X-XSS-Protection", "1; mode=block")
+                       end
+                       if not context.headers["x-content-type-options"] then
+                               header("X-Content-Type-Options", "nosniff")
+                       end
+
+                       context.eoh = true
+                       coroutine.yield(3)
+               end
+               coroutine.yield(4, content)
+               return true
+       end
+end
+
+function splice(fd, size)
+       coroutine.yield(6, fd, size)
+end
+
+function redirect(url)
+       if url == "" then url = "/" end
+       status(302, "Found")
+       header("Location", url)
+       close()
+end
+
+function build_querystring(q)
+       local s, n, k, v = {}, 1, nil, nil
+
+       for k, v in pairs(q) do
+               s[n+0] = (n == 1) and "?" or "&"
+               s[n+1] = util.urlencode(k)
+               s[n+2] = "="
+               s[n+3] = util.urlencode(v)
+               n = n + 4
+       end
+
+       return table.concat(s, "")
+end
+
+urldecode = util.urldecode
+
+urlencode = util.urlencode
+
+function write_json(x)
+       util.serialize_json(x, write)
+end
+
+-- 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 fed 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 whether 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")
+                               field[1] = field.file
+                       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
+                                               field.fd = nixio.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
+                               local val = msg.params[field.name]
+
+                               if type(val) == "table" then
+                                       val[#val+1] = field.value or ""
+                               elseif val ~= nil then
+                                       msg.params[field.name] = { val, field.value or "" }
+                               else
+                                       msg.params[field.name] = field.value or ""
+                               end
+                       end
+
+                       field = nil
+
+               elseif what == parser.ERROR then
+                       err = buffer
+               end
+
+               return true
+       end, HTTP_MAX_CONTENT)
+
+       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, lhttp.DECODE_PLUS)
+               elseif what == parser.VALUE and name then
+                       local val = msg.params[name]
+
+                       if type(val) == "table" then
+                               val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
+                       elseif val ~= nil then
+                               msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
+                       else
+                               msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
+                       end
+               elseif what == parser.ERROR then
+                       err = buffer
+               end
+
+               return true
+       end, HTTP_MAX_CONTENT)
+
+       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)
+       if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then
+               local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
+
+               -- Is it multipart/mime ?
+               if ctype == "multipart/form-data" then
+                       return mimedecode_message_body(src, msg, filecb)
+
+               -- Is it application/x-www-form-urlencoded ?
+               elseif ctype == "application/x-www-form-urlencoded" then
+                       return urldecode_message_body(src, msg)
+
+               end
+
+               -- Unhandled encoding
+               -- If a file callback is given then feed it chunk by chunk, else
+               -- store whole buffer in message.content
+               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
+
+       return false
+end
diff --git a/libs/luci-lib-base/luasrc/http.luadoc b/libs/luci-lib-base/luasrc/http.luadoc
new file mode 100644 (file)
index 0000000..8f6f380
--- /dev/null
@@ -0,0 +1,260 @@
+---[[
+LuCI Web Framework high-level HTTP functions.
+]]
+module "luci.http"
+
+---[[
+Close the HTTP-Connection.
+
+@class                 function
+@name                  close
+]]
+
+---[[
+Return the request content if the request was of unknown type.
+
+@class                 function
+@name                  content
+@return                        HTTP request body
+@return                        HTTP request body length
+]]
+
+---[[
+Get a certain HTTP input value or a table of all input values.
+
+@class                 function
+@name                  formvalue
+@param name            Name of the GET or POST variable to fetch
+@param noparse Don't parse POST data before getting the value
+@return                        HTTP input value or table of all input value
+]]
+
+---[[
+Get a table of all HTTP input values with a certain prefix.
+
+@class                 function
+@name                  formvaluetable
+@param prefix  Prefix
+@return                        Table of all HTTP input values with given prefix
+]]
+
+---[[
+Get the value of a certain HTTP-Cookie.
+
+@class                 function
+@name                  getcookie
+@param name            Cookie Name
+@return                        String containing cookie data
+]]
+
+---[[
+Get the value of a certain HTTP environment variable
+or the environment table itself.
+
+@class                 function
+@name                  getenv
+@param name            Environment variable
+@return                        HTTP environment value or environment table
+]]
+
+---[[
+Set a handler function for incoming user file uploads.
+
+@class                 function
+@name                  setfilehandler
+@param callback        Handler function
+]]
+
+---[[
+Send a HTTP-Header.
+
+@class                 function
+@name                  header
+@param key             Header key
+@param value   Header value
+]]
+
+---[[
+Set the mime type of following content data.
+
+@class                 function
+@name                  prepare_content
+@param mime            Mimetype of following content
+]]
+
+---[[
+Get the RAW HTTP input source
+
+@class                 function
+@name                  source
+@return                        HTTP LTN12 source
+]]
+
+---[[
+Set the HTTP status code and status message.
+
+@class                 function
+@name                  status
+@param code            Status code
+@param message Status message
+]]
+
+---[[
+Send a chunk of content data to the client.
+
+This function is as a valid LTN12 sink.
+If the content chunk is nil this function will automatically invoke close.
+
+@class                 function
+@name                  write
+@param content Content chunk
+@param src_err Error object from source (optional)
+@see close
+]]
+
+---[[
+Splice data from a filedescriptor to the client.
+
+@class                 function
+@name                  splice
+@param fp              File descriptor
+@param size            Bytes to splice (optional)
+]]
+
+---[[
+Redirects the client to a new URL and closes the connection.
+
+@class                 function
+@name                  redirect
+@param url             Target URL
+]]
+
+---[[
+Create a querystring out of a table of key - value pairs.
+
+@class                 function
+@name                  build_querystring
+@param table   Query string source table
+@return                        Encoded HTTP query string
+]]
+
+---[[
+Return the URL-decoded equivalent of a string.
+
+@class                 function
+@name                  urldecode
+@param str             URL-encoded string
+@param no_plus Don't decode + to " "
+@return                        URL-decoded string
+@see                   urlencode
+]]
+
+---[[
+Return the URL-encoded equivalent of a string.
+
+@class                 function
+@name                  urlencode
+@param str             Source string
+@return                        URL-encoded string
+@see                   urldecode
+]]
+
+---[[
+Send the given data as JSON encoded string.
+
+@class                 function
+@name                  write_json
+@param data            Data to send
+]]
+
+---[[
+Extract and split urlencoded data pairs, separated bei either "&" or ";"
+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.
+
+@class                 function
+@name                  urldecode_params
+@param url             The url or string which contains x-www-urlencoded form data
+@param tbl             Use the given table for storing values (optional)
+@return                        Table containing the urldecoded parameters
+@see                   urlencode_params
+]]
+
+---[[
+Encode each key-value-pair in given table to x-www-urlencoded format,
+separated by "&".
+
+Tables are encoded as parameters with multiple values by repeating the
+parameter name with each value.
+
+@class                 function
+@name                  urlencode_params
+@param tbl             Table with the values
+@return                        String containing encoded values
+@see                   urldecode_params
+]]
+
+---[[
+Decode a mime encoded http message body with multipart/form-data 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 fed 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 whether the current chunk is the last one (eof)
+
+@class                 function
+@name                  mimedecode_message_body
+@param src             Ltn12 source function
+@param msg             HTTP message object
+@param filecb  File callback function (optional)
+@return                        Value indicating successful operation (not nil means "ok")
+@return                        String containing the error if unsuccessful
+@see                   parse_message_header
+]]
+
+---[[
+Decode an urlencoded http message body with application/x-www-urlencoded
+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.
+
+@class                 function
+@name                  urldecode_message_body
+@param src             Ltn12 source function
+@param msg             HTTP message object
+@return                        Value indicating successful operation (not nil means "ok")
+@return                        String containing the error if unsuccessful
+@see                   parse_message_header
+]]
+
+---[[
+Try to extract and decode a http message body from the given ltn12 source.
+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.
+
+@class                 function
+@name                  parse_message_body
+@param src             Ltn12 source function
+@param msg             HTTP message object
+@param filecb  File data callback (optional, see mimedecode_message_body())
+@return                        Value indicating successful operation (not nil means "ok")
+@return                        String containing the error if unsuccessful
+@see                   parse_message_header
+]]
diff --git a/libs/luci-lib-base/luasrc/ltn12.lua b/libs/luci-lib-base/luasrc/ltn12.lua
new file mode 100644 (file)
index 0000000..3a7268c
--- /dev/null
@@ -0,0 +1,316 @@
+--[[
+LuaSocket 2.0.2 license
+Copyright ļæ½ 2004-2007 Diego Nehab
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+]]--
+--[[
+       Changes made by LuCI project:
+               * Renamed to luci.ltn12 to avoid collisions with luasocket
+               * Added inline documentation
+]]--
+-----------------------------------------------------------------------------
+-- LTN12 - Filters, sources, sinks and pumps.
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-- RCS ID: $Id$
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module
+-----------------------------------------------------------------------------
+local string = require("string")
+local table = require("table")
+local base = _G
+
+-- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
+module("luci.ltn12")
+
+filter = {}
+source = {}
+sink = {}
+pump = {}
+
+-- 2048 seems to be better in windows...
+BLOCKSIZE = 2048
+_VERSION = "LTN12 1.0.1"
+
+-----------------------------------------------------------------------------
+-- Filter stuff
+-----------------------------------------------------------------------------
+
+
+-- by passing it each chunk and updating a context between calls. 
+function filter.cycle(low, ctx, extra)
+    base.assert(low)
+    return function(chunk)
+        local ret
+        ret, ctx = low(ctx, chunk, extra)
+        return ret
+    end
+end
+
+-- (thanks to Wim Couwenberg)
+function filter.chain(...)
+    local n = table.getn(arg)
+    local top, index = 1, 1
+    local retry = ""
+    return function(chunk)
+        retry = chunk and retry
+        while true do
+            if index == top then
+                chunk = arg[index](chunk)
+                if chunk == "" or top == n then return chunk
+                elseif chunk then index = index + 1
+                else
+                    top = top+1
+                    index = top
+                end
+            else
+                chunk = arg[index](chunk or "")
+                if chunk == "" then
+                    index = index - 1
+                    chunk = retry
+                elseif chunk then
+                    if index == n then return chunk
+                    else index = index + 1 end
+                else base.error("filter returned inappropriate nil") end
+            end
+        end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Source stuff
+-----------------------------------------------------------------------------
+
+
+-- create an empty source
+local function empty()
+    return nil
+end
+
+function source.empty()
+    return empty
+end
+
+function source.error(err)
+    return function()
+        return nil, err
+    end
+end
+
+function source.file(handle, io_err)
+    if handle then
+        return function()
+            local chunk = handle:read(BLOCKSIZE)
+            if chunk and chunk:len() == 0 then chunk = nil end
+            if not chunk then handle:close() end
+            return chunk
+        end
+    else return source.error(io_err or "unable to open file") end
+end
+
+function source.simplify(src)
+    base.assert(src)
+    return function()
+        local chunk, err_or_new = src()
+        src = err_or_new or src
+        if not chunk then return nil, err_or_new
+        else return chunk end
+    end
+end
+
+function source.string(s)
+    if s then
+        local i = 1
+        return function()
+            local chunk = string.sub(s, i, i+BLOCKSIZE-1)
+            i = i + BLOCKSIZE
+            if chunk ~= "" then return chunk
+            else return nil end
+        end
+    else return source.empty() end
+end
+
+function source.rewind(src)
+    base.assert(src)
+    local t = {}
+    return function(chunk)
+        if not chunk then
+            chunk = table.remove(t)
+            if not chunk then return src()
+            else return chunk end
+        else
+            t[#t+1] = chunk
+        end
+    end
+end
+
+function source.chain(src, f)
+    base.assert(src and f)
+    local last_in, last_out = "", ""
+    local state = "feeding"
+    local err
+    return function()
+        if not last_out then
+            base.error('source is empty!', 2)
+        end
+        while true do
+            if state == "feeding" then
+                last_in, err = src()
+                if err then return nil, err end
+                last_out = f(last_in)
+                if not last_out then
+                    if last_in then
+                        base.error('filter returned inappropriate nil')
+                    else
+                        return nil
+                    end
+                elseif last_out ~= "" then
+                    state = "eating"
+                    if last_in then last_in = "" end
+                    return last_out
+                end
+            else
+                last_out = f(last_in)
+                if last_out == "" then
+                    if last_in == "" then
+                        state = "feeding"
+                    else
+                        base.error('filter returned ""')
+                    end
+                elseif not last_out then
+                    if last_in then
+                        base.error('filter returned inappropriate nil')
+                    else
+                        return nil
+                    end
+                else
+                    return last_out
+                end
+            end
+        end
+    end
+end
+
+-- Sources will be used one after the other, as if they were concatenated
+-- (thanks to Wim Couwenberg)
+function source.cat(...)
+    local src = table.remove(arg, 1)
+    return function()
+        while src do
+            local chunk, err = src()
+            if chunk then return chunk end
+            if err then return nil, err end
+            src = table.remove(arg, 1)
+        end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Sink stuff
+-----------------------------------------------------------------------------
+
+
+function sink.table(t)
+    t = t or {}
+    local f = function(chunk, err)
+        if chunk then t[#t+1] = chunk end
+        return 1
+    end
+    return f, t
+end
+
+function sink.simplify(snk)
+    base.assert(snk)
+    return function(chunk, err)
+        local ret, err_or_new = snk(chunk, err)
+        if not ret then return nil, err_or_new end
+        snk = err_or_new or snk
+        return 1
+    end
+end
+
+function sink.file(handle, io_err)
+    if handle then
+        return function(chunk, err)
+            if not chunk then
+                handle:close()
+                return 1
+            else return handle:write(chunk) end
+        end
+    else return sink.error(io_err or "unable to open file") end
+end
+
+-- creates a sink that discards data
+local function null()
+    return 1
+end
+
+function sink.null()
+    return null
+end
+
+function sink.error(err)
+    return function()
+        return nil, err
+    end
+end
+
+function sink.chain(f, snk)
+    base.assert(f and snk)
+    return function(chunk, err)
+        if chunk ~= "" then
+            local filtered = f(chunk)
+            local done = chunk and ""
+            while true do
+                local ret, snkerr = snk(filtered, err)
+                if not ret then return nil, snkerr end
+                if filtered == done then return 1 end
+                filtered = f(done)
+            end
+        else return 1 end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Pump stuff
+-----------------------------------------------------------------------------
+
+
+function pump.step(src, snk)
+    local chunk, src_err = src()
+    local ret, snk_err = snk(chunk, src_err)
+    if chunk and ret then return 1
+    else return nil, src_err or snk_err end
+end
+
+function pump.all(src, snk, step)
+    base.assert(src and snk)
+    step = step or pump.step
+    while true do
+        local ret, err = step(src, snk)
+        if not ret then
+            if err then return nil, err
+            else return 1 end
+        end
+    end
+end
+
diff --git a/libs/luci-lib-base/luasrc/util.lua b/libs/luci-lib-base/luasrc/util.lua
new file mode 100644 (file)
index 0000000..a30e8b7
--- /dev/null
@@ -0,0 +1,776 @@
+-- Copyright 2008 Steven Barth <[email protected]>
+-- Licensed to the public under the Apache License 2.0.
+
+local io = require "io"
+local math = require "math"
+local table = require "table"
+local debug = require "debug"
+local ldebug = require "luci.debug"
+local string = require "string"
+local coroutine = require "coroutine"
+local tparser = require "luci.template.parser"
+local json = require "luci.jsonc"
+local lhttp = require "lucihttp"
+
+local _ubus = require "ubus"
+local _ubus_connection = nil
+
+local getmetatable, setmetatable = getmetatable, setmetatable
+local rawget, rawset, unpack, select = rawget, rawset, unpack, select
+local tostring, type, assert, error = tostring, type, assert, error
+local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
+local require, pcall, xpcall = require, pcall, xpcall
+local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
+
+module "luci.util"
+
+--
+-- Pythonic string formatting extension
+--
+getmetatable("").__mod = function(a, b)
+       local ok, res
+
+       if not b then
+               return a
+       elseif type(b) == "table" then
+               local k, _
+               for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
+
+               ok, res = pcall(a.format, a, unpack(b))
+               if not ok then
+                       error(res, 2)
+               end
+               return res
+       else
+               if type(b) == "userdata" then b = tostring(b) end
+
+               ok, res = pcall(a.format, a, b)
+               if not ok then
+                       error(res, 2)
+               end
+               return res
+       end
+end
+
+
+--
+-- Class helper routines
+--
+
+-- Instantiates a class
+local function _instantiate(class, ...)
+       local inst = setmetatable({}, {__index = class})
+
+       if inst.__init__ then
+               inst:__init__(...)
+       end
+
+       return inst
+end
+
+-- The class object can be instantiated by calling itself.
+-- Any class functions or shared parameters can be attached to this object.
+-- Attaching a table to the class object makes this table shared between
+-- all instances of this class. For object parameters use the __init__ function.
+-- Classes can inherit member functions and values from a base class.
+-- Class can be instantiated by calling them. All parameters will be passed
+-- to the __init__ function of this class - if such a function exists.
+-- The __init__ function must be used to set any object parameters that are not shared
+-- with other objects of this class. Any return values will be ignored.
+function class(base)
+       return setmetatable({}, {
+               __call  = _instantiate,
+               __index = base
+       })
+end
+
+function instanceof(object, class)
+       local meta = getmetatable(object)
+       while meta and meta.__index do
+               if meta.__index == class then
+                       return true
+               end
+               meta = getmetatable(meta.__index)
+       end
+       return false
+end
+
+
+--
+-- Scope manipulation routines
+--
+
+coxpt = setmetatable({}, { __mode = "kv" })
+
+local tl_meta = {
+       __mode = "k",
+
+       __index = function(self, key)
+               local t = rawget(self, coxpt[coroutine.running()]
+                or coroutine.running() or 0)
+               return t and t[key]
+       end,
+
+       __newindex = function(self, key, value)
+               local c = coxpt[coroutine.running()] or coroutine.running() or 0
+               local r = rawget(self, c)
+               if not r then
+                       rawset(self, c, { [key] = value })
+               else
+                       r[key] = value
+               end
+       end
+}
+
+-- the current active coroutine. A thread local store is private a table object
+-- whose values can't be accessed from outside of the running coroutine.
+function threadlocal(tbl)
+       return setmetatable(tbl or {}, tl_meta)
+end
+
+
+--
+-- Debugging routines
+--
+
+function perror(obj)
+       return io.stderr:write(tostring(obj) .. "\n")
+end
+
+function dumptable(t, maxdepth, i, seen)
+       i = i or 0
+       seen = seen or setmetatable({}, {__mode="k"})
+
+       for k,v in pairs(t) do
+               perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
+               if type(v) == "table" and (not maxdepth or i < maxdepth) then
+                       if not seen[v] then
+                               seen[v] = true
+                               dumptable(v, maxdepth, i+1, seen)
+                       else
+                               perror(string.rep("\t", i) .. "*** RECURSION ***")
+                       end
+               end
+       end
+end
+
+
+--
+-- String and data manipulation routines
+--
+
+function pcdata(value)
+       return value and tparser.pcdata(tostring(value))
+end
+
+function urlencode(value)
+       if value ~= nil then
+               local str = tostring(value)
+               return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
+                       or str
+       end
+       return nil
+end
+
+function urldecode(value, decode_plus)
+       if value ~= nil then
+               local flag = decode_plus and lhttp.DECODE_PLUS or 0
+               local str = tostring(value)
+               return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
+                       or str
+       end
+       return nil
+end
+
+function striptags(value)
+       return value and tparser.striptags(tostring(value))
+end
+
+function shellquote(value)
+       return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
+end
+
+-- for bash, ash and similar shells single-quoted strings are taken
+-- literally except for single quotes (which terminate the string)
+-- (and the exception noted below for dash (-) at the start of a
+-- command line parameter).
+function shellsqescape(value)
+   local res
+   res, _ = string.gsub(value, "'", "'\\''")
+   return res
+end
+
+-- bash, ash and other similar shells interpret a dash (-) at the start
+-- of a command-line parameters as an option indicator regardless of
+-- whether it is inside a single-quoted string.  It must be backlash
+-- escaped to resolve this.  This requires in some funky special-case
+-- handling.  It may actually be a property of the getopt function
+-- rather than the shell proper.
+function shellstartsqescape(value)
+   res, _ = string.gsub(value, "^%-", "\\-")
+   return shellsqescape(res)
+end
+
+-- containing the resulting substrings. The optional max parameter specifies
+-- the number of bytes to process, regardless of the actual length of the given
+-- string. The optional last parameter, regex, specifies whether the separator
+-- sequence is interpreted as regular expression.
+--                                     pattern as regular expression (optional, default is false)
+function split(str, pat, max, regex)
+       pat = pat or "\n"
+       max = max or #str
+
+       local t = {}
+       local c = 1
+
+       if #str == 0 then
+               return {""}
+       end
+
+       if #pat == 0 then
+               return nil
+       end
+
+       if max == 0 then
+               return str
+       end
+
+       repeat
+               local s, e = str:find(pat, c, not regex)
+               max = max - 1
+               if s and max < 0 then
+                       t[#t+1] = str:sub(c)
+               else
+                       t[#t+1] = str:sub(c, s and s - 1)
+               end
+               c = e and e + 1 or #str + 1
+       until not s or max < 0
+
+       return t
+end
+
+function trim(str)
+       return (str:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function cmatch(str, pat)
+       local count = 0
+       for _ in str:gmatch(pat) do count = count + 1 end
+       return count
+end
+
+-- one token per invocation, the tokens are separated by whitespace. If the
+-- input value is a table, it is transformed into a string first. A nil value
+-- will result in a valid iterator which aborts with the first invocation.
+function imatch(v)
+       if type(v) == "table" then
+               local k = nil
+               return function()
+                       k = next(v, k)
+                       return v[k]
+               end
+
+       elseif type(v) == "number" or type(v) == "boolean" then
+               local x = true
+               return function()
+                       if x then
+                               x = false
+                               return tostring(v)
+                       end
+               end
+
+       elseif type(v) == "userdata" or type(v) == "string" then
+               return tostring(v):gmatch("%S+")
+       end
+
+       return function() end
+end
+
+-- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
+-- Recognized units are:
+--     o "y"   - one year   (60*60*24*366)
+--  o "m"      - one month  (60*60*24*31)
+--  o "w"      - one week   (60*60*24*7)
+--  o "d"      - one day    (60*60*24)
+--  o "h"      - one hour       (60*60)
+--  o "min"    - one minute (60)
+--  o "kb"  - one kilobyte (1024)
+--  o "mb"     - one megabyte (1024*1024)
+--  o "gb"     - one gigabyte (1024*1024*1024)
+--  o "kib" - one si kilobyte (1000)
+--  o "mib"    - one si megabyte (1000*1000)
+--  o "gib"    - one si gigabyte (1000*1000*1000)
+function parse_units(ustr)
+
+       local val = 0
+
+       -- unit map
+       local map = {
+               -- date stuff
+               y   = 60 * 60 * 24 * 366,
+               m   = 60 * 60 * 24 * 31,
+               w   = 60 * 60 * 24 * 7,
+               d   = 60 * 60 * 24,
+               h   = 60 * 60,
+               min = 60,
+
+               -- storage sizes
+               kb  = 1024,
+               mb  = 1024 * 1024,
+               gb  = 1024 * 1024 * 1024,
+
+               -- storage sizes (si)
+               kib = 1000,
+               mib = 1000 * 1000,
+               gib = 1000 * 1000 * 1000
+       }
+
+       -- parse input string
+       for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
+
+               local num = spec:gsub("[^0-9%.]+$","")
+               local spn = spec:gsub("^[0-9%.]+", "")
+
+               if map[spn] or map[spn:sub(1,1)] then
+                       val = val + num * ( map[spn] or map[spn:sub(1,1)] )
+               else
+                       val = val + num
+               end
+       end
+
+
+       return val
+end
+
+-- also register functions above in the central string class for convenience
+string.pcdata      = pcdata
+string.striptags   = striptags
+string.split       = split
+string.trim        = trim
+string.cmatch      = cmatch
+string.parse_units = parse_units
+
+
+function append(src, ...)
+       for i, a in ipairs({...}) do
+               if type(a) == "table" then
+                       for j, v in ipairs(a) do
+                               src[#src+1] = v
+                       end
+               else
+                       src[#src+1] = a
+               end
+       end
+       return src
+end
+
+function combine(...)
+       return append({}, ...)
+end
+
+function contains(table, value)
+       for k, v in pairs(table) do
+               if value == v then
+                       return k
+               end
+       end
+       return false
+end
+
+-- Both table are - in fact - merged together.
+function update(t, updates)
+       for k, v in pairs(updates) do
+               t[k] = v
+       end
+end
+
+function keys(t)
+       local keys = { }
+       if t then
+               for k, _ in kspairs(t) do
+                       keys[#keys+1] = k
+               end
+       end
+       return keys
+end
+
+function clone(object, deep)
+       local copy = {}
+
+       for k, v in pairs(object) do
+               if deep and type(v) == "table" then
+                       v = clone(v, deep)
+               end
+               copy[k] = v
+       end
+
+       return setmetatable(copy, getmetatable(object))
+end
+
+
+-- Serialize the contents of a table value.
+function _serialize_table(t, seen)
+       assert(not seen[t], "Recursion detected.")
+       seen[t] = true
+
+       local data  = ""
+       local idata = ""
+       local ilen  = 0
+
+       for k, v in pairs(t) do
+               if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
+                       k = serialize_data(k, seen)
+                       v = serialize_data(v, seen)
+                       data = data .. ( #data > 0 and ", " or "" ) ..
+                               '[' .. k .. '] = ' .. v
+               elseif k > ilen then
+                       ilen = k
+               end
+       end
+
+       for i = 1, ilen do
+               local v = serialize_data(t[i], seen)
+               idata = idata .. ( #idata > 0 and ", " or "" ) .. v
+       end
+
+       return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
+end
+
+-- with loadstring().
+function serialize_data(val, seen)
+       seen = seen or setmetatable({}, {__mode="k"})
+
+       if val == nil then
+               return "nil"
+       elseif type(val) == "number" then
+               return val
+       elseif type(val) == "string" then
+               return "%q" % val
+       elseif type(val) == "boolean" then
+               return val and "true" or "false"
+       elseif type(val) == "function" then
+               return "loadstring(%q)" % get_bytecode(val)
+       elseif type(val) == "table" then
+               return "{ " .. _serialize_table(val, seen) .. " }"
+       else
+               return '"[unhandled data type:' .. type(val) .. ']"'
+       end
+end
+
+function restore_data(str)
+       return loadstring("return " .. str)()
+end
+
+
+--
+-- Byte code manipulation routines
+--
+
+-- will be stripped before it is returned.
+function get_bytecode(val)
+       local code
+
+       if type(val) == "function" then
+               code = string.dump(val)
+       else
+               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
+       end
+
+       return code -- and strip_bytecode(code)
+end
+
+-- numbers and debugging numbers will be discarded. Original version by
+-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+function strip_bytecode(code)
+       local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
+       local subint
+       if endian == 1 then
+               subint = function(code, i, l)
+                       local val = 0
+                       for n = l, 1, -1 do
+                               val = val * 256 + code:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       else
+               subint = function(code, i, l)
+                       local val = 0
+                       for n = 1, l, 1 do
+                               val = val * 256 + code:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       end
+
+       local function strip_function(code)
+               local count, offset = subint(code, 1, size)
+               local stripped = { string.rep("\0", size) }
+               local dirty = offset + count
+               offset = offset + count + int * 2 + 4
+               offset = offset + int + subint(code, offset, int) * ins
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       local t
+                       t, offset = subint(code, offset, 1)
+                       if t == 1 then
+                               offset = offset + 1
+                       elseif t == 4 then
+                               offset = offset + size + subint(code, offset, size)
+                       elseif t == 3 then
+                               offset = offset + num
+                       elseif t == 254 or t == 9 then
+                               offset = offset + lnum
+                       end
+               end
+               count, offset = subint(code, offset, int)
+               stripped[#stripped+1] = code:sub(dirty, offset - 1)
+               for n = 1, count do
+                       local proto, off = strip_function(code:sub(offset, -1))
+                       stripped[#stripped+1] = proto
+                       offset = offset + off - 1
+               end
+               offset = offset + subint(code, offset, int) * int + int
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(code, offset, size) + size + int * 2
+               end
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(code, offset, size) + size
+               end
+               stripped[#stripped+1] = string.rep("\0", int * 3)
+               return table.concat(stripped), offset
+       end
+
+       return code:sub(1,12) .. strip_function(code:sub(13,-1))
+end
+
+
+--
+-- Sorting iterator functions
+--
+
+function _sortiter( t, f )
+       local keys = { }
+
+       local k, v
+       for k, v in pairs(t) do
+               keys[#keys+1] = k
+       end
+
+       local _pos = 0
+
+       table.sort( keys, f )
+
+       return function()
+               _pos = _pos + 1
+               if _pos <= #keys then
+                       return keys[_pos], t[keys[_pos]], _pos
+               end
+       end
+end
+
+-- the provided callback function.
+function spairs(t,f)
+       return _sortiter( t, f )
+end
+
+-- The table pairs are sorted by key.
+function kspairs(t)
+       return _sortiter( t )
+end
+
+-- The table pairs are sorted by value.
+function vspairs(t)
+       return _sortiter( t, function (a,b) return t[a] < t[b] end )
+end
+
+
+--
+-- System utility functions
+--
+
+function bigendian()
+       return string.byte(string.dump(function() end), 7) == 0
+end
+
+function exec(command)
+       local pp   = io.popen(command)
+       local data = pp:read("*a")
+       pp:close()
+
+       return data
+end
+
+function execi(command)
+       local pp = io.popen(command)
+
+       return pp and function()
+               local line = pp:read()
+
+               if not line then
+                       pp:close()
+               end
+
+               return line
+       end
+end
+
+-- Deprecated
+function execl(command)
+       local pp   = io.popen(command)
+       local line = ""
+       local data = {}
+
+       while true do
+               line = pp:read()
+               if (line == nil) then break end
+               data[#data+1] = line
+       end
+       pp:close()
+
+       return data
+end
+
+
+local ubus_codes = {
+       "INVALID_COMMAND",
+       "INVALID_ARGUMENT",
+       "METHOD_NOT_FOUND",
+       "NOT_FOUND",
+       "NO_DATA",
+       "PERMISSION_DENIED",
+       "TIMEOUT",
+       "NOT_SUPPORTED",
+       "UNKNOWN_ERROR",
+       "CONNECTION_FAILED"
+}
+
+local function ubus_return(...)
+       if select('#', ...) == 2 then
+               local rv, err = select(1, ...), select(2, ...)
+               if rv == nil and type(err) == "number" then
+                       return nil, err, ubus_codes[err]
+               end
+       end
+
+       return ...
+end
+
+function ubus(object, method, data)
+       if not _ubus_connection then
+               _ubus_connection = _ubus.connect()
+               assert(_ubus_connection, "Unable to establish ubus connection")
+       end
+
+       if object and method then
+               if type(data) ~= "table" then
+                       data = { }
+               end
+               return ubus_return(_ubus_connection:call(object, method, data))
+       elseif object then
+               return _ubus_connection:signatures(object)
+       else
+               return _ubus_connection:objects()
+       end
+end
+
+function serialize_json(x, cb)
+       local js = json.stringify(x)
+       if type(cb) == "function" then
+               cb(js)
+       else
+               return js
+       end
+end
+
+
+function libpath()
+       return require "nixio.fs".dirname(ldebug.__file__)
+end
+
+function checklib(fullpathexe, wantedlib)
+       local fs = require "nixio.fs"
+       local haveldd = fs.access('/usr/bin/ldd')
+       local haveexe = fs.access(fullpathexe)
+       if not haveldd or not haveexe then
+               return false
+       end
+       local libs = exec(string.format("/usr/bin/ldd %s", shellquote(fullpathexe)))
+       if not libs then
+               return false
+       end
+       for k, v in ipairs(split(libs)) do
+               if v:find(wantedlib) then
+                       return true
+               end
+       end
+       return false
+end
+
+-------------------------------------------------------------------------------
+-- Coroutine safe xpcall and pcall versions
+--
+-- Encapsulates the protected calls with a coroutine based loop, so errors can
+-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
+-- yielding inside the call to pcall or xpcall.
+--
+-- Authors: Roberto Ierusalimschy and Andre Carregal
+-- Contributors: Thomas Harning Jr., Ignacio BurgueƱo, Fabio Mascarenhas
+--
+-- Copyright 2005 - Kepler Project
+--
+-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- Implements xpcall with coroutines
+-------------------------------------------------------------------------------
+local coromap = setmetatable({}, { __mode = "k" })
+
+local function handleReturnValue(err, co, status, ...)
+       if not status then
+               return false, err(debug.traceback(co, (...)), ...)
+       end
+       if coroutine.status(co) == 'suspended' then
+               return performResume(err, co, coroutine.yield(...))
+       else
+               return true, ...
+       end
+end
+
+function performResume(err, co, ...)
+       return handleReturnValue(err, co, coroutine.resume(co, ...))
+end
+
+local function id(trace, ...)
+       return trace
+end
+
+function coxpcall(f, err, ...)
+       local current = coroutine.running()
+       if not current then
+               if err == id then
+                       return pcall(f, ...)
+               else
+                       if select("#", ...) > 0 then
+                               local oldf, params = f, { ... }
+                               f = function() return oldf(unpack(params)) end
+                       end
+                       return xpcall(f, err)
+               end
+       else
+               local res, co = pcall(coroutine.create, f)
+               if not res then
+                       local newf = function(...) return f(...) end
+                       co = coroutine.create(newf)
+               end
+               coromap[co] = current
+               coxpt[co] = coxpt[current] or current or 0
+               return performResume(err, co, ...)
+       end
+end
+
+function copcall(f, ...)
+       return coxpcall(f, id, ...)
+end
diff --git a/libs/luci-lib-base/luasrc/util.luadoc b/libs/luci-lib-base/luasrc/util.luadoc
new file mode 100644 (file)
index 0000000..4ec68dd
--- /dev/null
@@ -0,0 +1,413 @@
+---[[
+LuCI utility functions.
+]]
+module "luci.util"
+
+---[[
+Create a Class object (Python-style object model).
+
+The class object can be instantiated by calling itself.
+Any class functions or shared parameters can be attached to this object.
+Attaching a table to the class object makes this table shared between
+all instances of this class. For object parameters use the __init__ function.
+Classes can inherit member functions and values from a base class.
+Class can be instantiated by calling them. All parameters will be passed
+to the __init__ function of this class - if such a function exists.
+The __init__ function must be used to set any object parameters that are not shared
+with other objects of this class. Any return values will be ignored.
+
+@class                         function
+@name                          class
+@param base                    The base class to inherit from (optional)
+@return                                A class object
+@see                           instanceof
+@see                           clone
+]]
+
+---[[
+Test whether the given object is an instance of the given class.
+
+@class                         function
+@name                          instanceof
+@param object          Object instance
+@param class           Class object to test against
+@return                                Boolean indicating whether the object is an instance
+@see                           class
+@see                           clone
+]]
+
+---[[
+Create a new or get an already existing thread local store associated with
+the current active coroutine.
+
+A thread local store is private a table object
+whose values can't be accessed from outside of the running coroutine.
+
+@class                         function
+@name                          threadlocal
+@return                                Table value representing the corresponding thread local store
+]]
+
+---[[
+Write given object to stderr.
+
+@class                         function
+@name                          perror
+@param obj                     Value to write to stderr
+@return                                Boolean indicating whether the write operation was successful
+]]
+
+---[[
+Recursively dumps a table to stdout, useful for testing and debugging.
+
+@class                         function
+@name                          dumptable
+@param t                       Table value to dump
+@param maxdepth                Maximum depth
+@return                                Always nil
+]]
+
+---[[
+Create valid XML PCDATA from given string.
+
+@class                         function
+@name                          pcdata
+@param value           String value containing the data to escape
+@return                                String value containing the escaped data
+]]
+
+---[[
+Decode an URL-encoded string - optionally decoding the "+" sign to space.
+
+@class                         function
+@name                          urldecode
+@param str                     Input string in x-www-urlencoded format
+@param decode_plus     Decode "+" signs to spaces if true (optional)
+@return                                The decoded string
+@see                           urlencode
+]]
+
+---[[
+URL-encode given string.
+
+@class                         function
+@name                          urlencode
+@param str                     String to encode
+@return                                String containing the encoded data
+@see                           urldecode
+]]
+
+---[[
+Strip HTML tags from given string.
+
+@class                         function
+@name                          striptags
+@param value           String containing the HTML text
+@return                                String with HTML tags stripped of
+]]
+
+---[[
+Safely quote value for use in shell commands.
+
+@class                         function
+@name                          shellquote
+@param value           String containing the value to quote
+@return                        Single-quote enclosed string with embedded quotes escaped
+]]
+
+---[[
+Splits given string on a defined separator sequence and return a table
+containing the resulting substrings.
+
+The optional max parameter specifies the number of bytes to process,
+regardless of the actual length of the given string. The optional last
+parameter, regex, specifies whether the separator sequence is
+nterpreted as regular expression.
+
+@class                         function
+@name                          split
+@param str                     String value containing the data to split up
+@param pat                     String with separator pattern (optional, defaults to "\n")
+@param max                     Maximum times to split (optional)
+@param regex           Boolean indicating whether to interpret the separator
+--                                     pattern as regular expression (optional, default is false)
+@return                                Table containing the resulting substrings
+]]
+
+---[[
+Remove leading and trailing whitespace from given string value.
+
+@class                         function
+@name                          trim
+@param str                     String value containing whitespace padded data
+@return                                String value with leading and trailing space removed
+]]
+
+---[[
+Count the occurrences of given substring in given string.
+
+@class                         function
+@name                          cmatch
+@param str                     String to search in
+@param pattern         String containing pattern to find
+@return                                Number of found occurrences
+]]
+
+---[[
+Return a matching iterator for the given value.
+
+The iterator will return one token per invocation, the tokens are separated by
+whitespace. If the input value is a table, it is transformed into a string first.
+A nil value will result in a valid iterator which aborts with the first invocation.
+
+@class                         function
+@name                          imatch
+@param val                     The value to scan (table, string or nil)
+@return                                Iterator which returns one token per call
+]]
+
+---[[
+Parse certain units from the given string and return the canonical integer
+value or 0 if the unit is unknown.
+
+Upper- or lower case is irrelevant.
+Recognized units are:
+
+--     o "y"   - one year   (60*60*24*366)
+ o "m" - one month  (60*60*24*31)
+ o "w" - one week   (60*60*24*7)
+ o "d" - one day    (60*60*24)
+ o "h" - one hour       (60*60)
+ o "min"       - one minute (60)
+ o "kb"  - one kilobyte (1024)
+ o "mb"        - one megabyte (1024*1024)
+ o "gb"        - one gigabyte (1024*1024*1024)
+ o "kib" - one si kilobyte (1000)
+ o "mib"       - one si megabyte (1000*1000)
+ o "gib"       - one si gigabyte (1000*1000*1000)
+
+@class                         function
+@name                          parse_units
+@param ustr                    String containing a numerical value with trailing unit
+@return                                Number containing the canonical value
+]]
+
+---[[
+Appends numerically indexed tables or single objects to a given table.
+
+@class                         function
+@name                          append
+@param src                     Target table
+@param ...                     Objects to insert
+@return                                Target table
+]]
+
+---[[
+Combines two or more numerically indexed tables and single objects into one table.
+
+@class                         function
+@name                          combine
+@param tbl1                    Table value to combine
+@param tbl2                    Table value to combine
+@param ...                     More tables to combine
+@return                                Table value containing all values of given tables
+]]
+
+---[[
+Checks whether the given table contains the given value.
+
+@class                         function
+@name                          contains
+@param table           Table value
+@param value           Value to search within the given table
+@return                                Number indicating the first index at which the given value occurs
+--                                     within table or false.
+]]
+
+---[[
+Update values in given table with the values from the second given table.
+
+Both table are - in fact - merged together.
+
+@class                         function
+@name                          update
+@param t                       Table which should be updated
+@param updates         Table containing the values to update
+@return                                Always nil
+]]
+
+---[[
+Retrieve all keys of given associative table.
+
+@class                         function
+@name                          keys
+@param t                       Table to extract keys from
+@return                                Sorted table containing the keys
+]]
+
+---[[
+Clones the given object and return it's copy.
+
+@class                         function
+@name                          clone
+@param object          Table value to clone
+@param deep                    Boolean indicating whether to do recursive cloning
+@return                                Cloned table value
+]]
+
+---[[
+Recursively serialize given data to lua code, suitable for restoring
+with loadstring().
+
+@class                         function
+@name                          serialize_data
+@param val                     Value containing the data to serialize
+@return                                String value containing the serialized code
+@see                           restore_data
+@see                           get_bytecode
+]]
+
+---[[
+Restore data previously serialized with serialize_data().
+
+@class                         function
+@name                          restore_data
+@param str                     String containing the data to restore
+@return                                Value containing the restored data structure
+@see                           serialize_data
+@see                           get_bytecode
+]]
+
+---[[
+Return the current runtime bytecode of the given data. The byte code
+will be stripped before it is returned.
+
+@class                         function
+@name                          get_bytecode
+@param val                     Value to return as bytecode
+@return                                String value containing the bytecode of the given data
+]]
+
+---[[
+Strips unnecessary lua bytecode from given string.
+
+Information like line numbers and debugging numbers will be discarded.
+Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+
+@class                         function
+@name                          strip_bytecode
+@param code                    String value containing the original lua byte code
+@return                                String value containing the stripped lua byte code
+]]
+
+---[[
+Return a key, value iterator which returns the values sorted according to
+the provided callback function.
+
+@class                         function
+@name                          spairs
+@param t                       The table to iterate
+@param f                       A callback function to decide the order of elements
+@return                                Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by key.
+
+@class                         function
+@name                          kspairs
+@param t                       The table to iterate
+@return                                Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by value.
+
+@class                         function
+@name                          vspairs
+@param t                       The table to iterate
+@return                                Function value containing the corresponding iterator
+]]
+
+---[[
+Test whether the current system is operating in big endian mode.
+
+@class                         function
+@name                          bigendian
+@return                                Boolean value indicating whether system is big endian
+]]
+
+---[[
+Execute given commandline and gather stdout.
+
+@class                         function
+@name                          exec
+@param command         String containing command to execute
+@return                                String containing the command's stdout
+]]
+
+---[[
+Return a line-buffered iterator over the output of given command.
+
+@class                         function
+@name                          execi
+@param command         String containing the command to execute
+@return                                Iterator
+]]
+
+---[[
+Issue an ubus call.
+
+@class                         function
+@name                          ubus
+@param object          String containing the ubus object to call
+@param method          String containing the ubus method to call
+@param values          Table containing the values to pass
+@return                                Table containin the ubus result
+]]
+
+---[[
+Convert data structure to JSON
+
+@class                         function
+@name                          serialize_json
+@param data                    The data to serialize
+@param writer          A function to write a chunk of JSON data (optional)
+@return                                String containing the JSON if called without write callback
+]]
+
+---[[
+Returns the absolute path to LuCI base directory.
+
+@class                         function
+@name                          libpath
+@return                                String containing the directory path
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
+
+@class                         function
+@name                          coxpcall
+@param f                       Lua function to be called protected
+@param err                     Custom error handler
+@param ...                     Parameters passed to the function
+@return                                A boolean whether the function call succeeded and the return
+--                                     values of either the function or the error handler
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
+
+@class                         function
+@name                          copcall
+@param f                       Lua function to be called protected
+@param ...                     Parameters passed to the function
+@return                                A boolean whether the function call succeeded and the returns
+--                                     values of the function or the error object
+]]
+
index b8bd428b2838ef9a8cede83dc227ff0538a362b2..9c28c35d810593385ba10cb5208007d13f40e4ec 100644 (file)
@@ -7,7 +7,7 @@
 include $(TOPDIR)/rules.mk
 
 LUCI_TITLE:=HTTP(S) client library
-LUCI_DEPENDS:=+luci-base +luci-lib-nixio +luci-lib-httpprotoutils
+LUCI_DEPENDS:=+luci-lib-base +luci-lib-nixio +luci-lib-httpprotoutils
 
 include ../../luci.mk
 
index 851a362eb70465420a6a173fb9eea88ccfe07fe4..95f45d200275945ca08c1e5c5b7f37482c6a7f73 100644 (file)
@@ -7,7 +7,7 @@
 include $(TOPDIR)/rules.mk
 
 LUCI_TITLE:=HTTP protocol utility functions
-LUCI_DEPENDS:=+luci-base
+LUCI_DEPENDS:=+luci-lib-base
 
 include ../../luci.mk
 
diff --git a/modules/luci-base/luasrc/debug.lua b/modules/luci-base/luasrc/debug.lua
deleted file mode 100644 (file)
index 8ff1bb6..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-local debug = require "debug"
-local io = require "io"
-local collectgarbage, floor = collectgarbage, math.floor
-
-module "luci.debug"
-__file__ = debug.getinfo(1, 'S').source:sub(2)
-
--- Enables the memory tracer with given flags and returns a function to disable the tracer again
-function trap_memtrace(flags, dest)
-       flags = flags or "clr"
-       local tracefile = io.open(dest or "/tmp/memtrace", "w")
-       local peak = 0
-
-       local function trap(what, line)
-               local info = debug.getinfo(2, "Sn")
-               local size = floor(collectgarbage("count"))
-               if size > peak then
-                       peak = size
-               end
-               if tracefile then
-                       tracefile:write(
-                               "[", what, "] ", info.source, ":", (line or "?"), "\t",
-                               (info.namewhat or ""), "\t",
-                               (info.name or ""), "\t",
-                               size, " (", peak, ")\n"
-                       )
-               end
-       end
-
-       debug.sethook(trap, flags)
-
-       return function()
-               debug.sethook()
-               tracefile:close()
-       end
-end
-
diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua
deleted file mode 100644 (file)
index 20b55f2..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
--- Copyright 2008 Steven Barth <[email protected]>
--- Copyright 2010-2018 Jo-Philipp Wich <[email protected]>
--- Licensed to the public under the Apache License 2.0.
-
-local util  = require "luci.util"
-local coroutine = require "coroutine"
-local table = require "table"
-local lhttp = require "lucihttp"
-local nixio = require "nixio"
-local ltn12 = require "luci.ltn12"
-
-local table, ipairs, pairs, type, tostring, tonumber, error =
-       table, ipairs, pairs, type, tostring, tonumber, error
-
-module "luci.http"
-
-HTTP_MAX_CONTENT      = 1024*100               -- 100 kB maximum content size
-
-context = util.threadlocal()
-
-Request = util.class()
-function Request.__init__(self, env, sourcein, sinkerr)
-       self.input = sourcein
-       self.error = sinkerr
-
-
-       -- File handler nil by default to let .content() work
-       self.filehandler = nil
-
-       -- HTTP-Message table
-       self.message = {
-               env = env,
-               headers = {},
-               params = urldecode_params(env.QUERY_STRING or ""),
-       }
-
-       self.parsed_input = false
-end
-
-function Request.formvalue(self, name, noparse)
-       if not noparse and not self.parsed_input then
-               self:_parse_input()
-       end
-
-       if name then
-               return self.message.params[name]
-       else
-               return self.message.params
-       end
-end
-
-function Request.formvaluetable(self, prefix)
-       local vals = {}
-       prefix = prefix and prefix .. "." or "."
-
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       local void = self.message.params[nil]
-       for k, v in pairs(self.message.params) do
-               if k:find(prefix, 1, true) == 1 then
-                       vals[k:sub(#prefix + 1)] = tostring(v)
-               end
-       end
-
-       return vals
-end
-
-function Request.content(self)
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       return self.message.content, self.message.content_length
-end
-
-function Request.getcookie(self, name)
-       return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
-end
-
-function Request.getenv(self, name)
-       if name then
-               return self.message.env[name]
-       else
-               return self.message.env
-       end
-end
-
-function Request.setfilehandler(self, callback)
-       self.filehandler = callback
-
-       if not self.parsed_input then
-               return
-       end
-
-       -- If input has already been parsed then uploads are stored as unlinked
-       -- temporary files pointed to by open file handles in the parameter
-       -- value table. Loop all params, and invoke the file callback for any
-       -- param with an open file handle.
-       local name, value
-       for name, value in pairs(self.message.params) do
-               if type(value) == "table" then
-                       while value.fd do
-                               local data = value.fd:read(1024)
-                               local eof = (not data or data == "")
-
-                               callback(value, data, eof)
-
-                               if eof then
-                                       value.fd:close()
-                                       value.fd = nil
-                               end
-                       end
-               end
-       end
-end
-
-function Request._parse_input(self)
-       parse_message_body(
-                self.input,
-                self.message,
-                self.filehandler
-       )
-       self.parsed_input = true
-end
-
-function close()
-       if not context.eoh then
-               context.eoh = true
-               coroutine.yield(3)
-       end
-
-       if not context.closed then
-               context.closed = true
-               coroutine.yield(5)
-       end
-end
-
-function content()
-       return context.request:content()
-end
-
-function formvalue(name, noparse)
-       return context.request:formvalue(name, noparse)
-end
-
-function formvaluetable(prefix)
-       return context.request:formvaluetable(prefix)
-end
-
-function getcookie(name)
-       return context.request:getcookie(name)
-end
-
--- or the environment table itself.
-function getenv(name)
-       return context.request:getenv(name)
-end
-
-function setfilehandler(callback)
-       return context.request:setfilehandler(callback)
-end
-
-function header(key, value)
-       if not context.headers then
-               context.headers = {}
-       end
-       context.headers[key:lower()] = value
-       coroutine.yield(2, key, value)
-end
-
-function prepare_content(mime)
-       if not context.headers or not context.headers["content-type"] then
-               if mime == "application/xhtml+xml" then
-                       if not getenv("HTTP_ACCEPT") or
-                         not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
-                               mime = "text/html; charset=UTF-8"
-                       end
-                       header("Vary", "Accept")
-               end
-               header("Content-Type", mime)
-       end
-end
-
-function source()
-       return context.request.input
-end
-
-function status(code, message)
-       code = code or 200
-       message = message or "OK"
-       context.status = code
-       coroutine.yield(1, code, message)
-end
-
--- This function is as a valid LTN12 sink.
--- If the content chunk is nil this function will automatically invoke close.
-function write(content, src_err)
-       if not content then
-               if src_err then
-                       error(src_err)
-               else
-                       close()
-               end
-               return true
-       elseif #content == 0 then
-               return true
-       else
-               if not context.eoh then
-                       if not context.status then
-                               status()
-                       end
-                       if not context.headers or not context.headers["content-type"] then
-                               header("Content-Type", "text/html; charset=utf-8")
-                       end
-                       if not context.headers["cache-control"] then
-                               header("Cache-Control", "no-cache")
-                               header("Expires", "0")
-                       end
-                       if not context.headers["x-frame-options"] then
-                               header("X-Frame-Options", "SAMEORIGIN")
-                       end
-                       if not context.headers["x-xss-protection"] then
-                               header("X-XSS-Protection", "1; mode=block")
-                       end
-                       if not context.headers["x-content-type-options"] then
-                               header("X-Content-Type-Options", "nosniff")
-                       end
-
-                       context.eoh = true
-                       coroutine.yield(3)
-               end
-               coroutine.yield(4, content)
-               return true
-       end
-end
-
-function splice(fd, size)
-       coroutine.yield(6, fd, size)
-end
-
-function redirect(url)
-       if url == "" then url = "/" end
-       status(302, "Found")
-       header("Location", url)
-       close()
-end
-
-function build_querystring(q)
-       local s, n, k, v = {}, 1, nil, nil
-
-       for k, v in pairs(q) do
-               s[n+0] = (n == 1) and "?" or "&"
-               s[n+1] = util.urlencode(k)
-               s[n+2] = "="
-               s[n+3] = util.urlencode(v)
-               n = n + 4
-       end
-
-       return table.concat(s, "")
-end
-
-urldecode = util.urldecode
-
-urlencode = util.urlencode
-
-function write_json(x)
-       util.serialize_json(x, write)
-end
-
--- 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 fed 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 whether 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")
-                               field[1] = field.file
-                       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
-                                               field.fd = nixio.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
-                               local val = msg.params[field.name]
-
-                               if type(val) == "table" then
-                                       val[#val+1] = field.value or ""
-                               elseif val ~= nil then
-                                       msg.params[field.name] = { val, field.value or "" }
-                               else
-                                       msg.params[field.name] = field.value or ""
-                               end
-                       end
-
-                       field = nil
-
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end, HTTP_MAX_CONTENT)
-
-       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, lhttp.DECODE_PLUS)
-               elseif what == parser.VALUE and name then
-                       local val = msg.params[name]
-
-                       if type(val) == "table" then
-                               val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
-                       elseif val ~= nil then
-                               msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
-                       else
-                               msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
-                       end
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end, HTTP_MAX_CONTENT)
-
-       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)
-       if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then
-               local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
-
-               -- Is it multipart/mime ?
-               if ctype == "multipart/form-data" then
-                       return mimedecode_message_body(src, msg, filecb)
-
-               -- Is it application/x-www-form-urlencoded ?
-               elseif ctype == "application/x-www-form-urlencoded" then
-                       return urldecode_message_body(src, msg)
-
-               end
-
-               -- Unhandled encoding
-               -- If a file callback is given then feed it chunk by chunk, else
-               -- store whole buffer in message.content
-               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
-
-       return false
-end
diff --git a/modules/luci-base/luasrc/http.luadoc b/modules/luci-base/luasrc/http.luadoc
deleted file mode 100644 (file)
index 8f6f380..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
----[[
-LuCI Web Framework high-level HTTP functions.
-]]
-module "luci.http"
-
----[[
-Close the HTTP-Connection.
-
-@class                 function
-@name                  close
-]]
-
----[[
-Return the request content if the request was of unknown type.
-
-@class                 function
-@name                  content
-@return                        HTTP request body
-@return                        HTTP request body length
-]]
-
----[[
-Get a certain HTTP input value or a table of all input values.
-
-@class                 function
-@name                  formvalue
-@param name            Name of the GET or POST variable to fetch
-@param noparse Don't parse POST data before getting the value
-@return                        HTTP input value or table of all input value
-]]
-
----[[
-Get a table of all HTTP input values with a certain prefix.
-
-@class                 function
-@name                  formvaluetable
-@param prefix  Prefix
-@return                        Table of all HTTP input values with given prefix
-]]
-
----[[
-Get the value of a certain HTTP-Cookie.
-
-@class                 function
-@name                  getcookie
-@param name            Cookie Name
-@return                        String containing cookie data
-]]
-
----[[
-Get the value of a certain HTTP environment variable
-or the environment table itself.
-
-@class                 function
-@name                  getenv
-@param name            Environment variable
-@return                        HTTP environment value or environment table
-]]
-
----[[
-Set a handler function for incoming user file uploads.
-
-@class                 function
-@name                  setfilehandler
-@param callback        Handler function
-]]
-
----[[
-Send a HTTP-Header.
-
-@class                 function
-@name                  header
-@param key             Header key
-@param value   Header value
-]]
-
----[[
-Set the mime type of following content data.
-
-@class                 function
-@name                  prepare_content
-@param mime            Mimetype of following content
-]]
-
----[[
-Get the RAW HTTP input source
-
-@class                 function
-@name                  source
-@return                        HTTP LTN12 source
-]]
-
----[[
-Set the HTTP status code and status message.
-
-@class                 function
-@name                  status
-@param code            Status code
-@param message Status message
-]]
-
----[[
-Send a chunk of content data to the client.
-
-This function is as a valid LTN12 sink.
-If the content chunk is nil this function will automatically invoke close.
-
-@class                 function
-@name                  write
-@param content Content chunk
-@param src_err Error object from source (optional)
-@see close
-]]
-
----[[
-Splice data from a filedescriptor to the client.
-
-@class                 function
-@name                  splice
-@param fp              File descriptor
-@param size            Bytes to splice (optional)
-]]
-
----[[
-Redirects the client to a new URL and closes the connection.
-
-@class                 function
-@name                  redirect
-@param url             Target URL
-]]
-
----[[
-Create a querystring out of a table of key - value pairs.
-
-@class                 function
-@name                  build_querystring
-@param table   Query string source table
-@return                        Encoded HTTP query string
-]]
-
----[[
-Return the URL-decoded equivalent of a string.
-
-@class                 function
-@name                  urldecode
-@param str             URL-encoded string
-@param no_plus Don't decode + to " "
-@return                        URL-decoded string
-@see                   urlencode
-]]
-
----[[
-Return the URL-encoded equivalent of a string.
-
-@class                 function
-@name                  urlencode
-@param str             Source string
-@return                        URL-encoded string
-@see                   urldecode
-]]
-
----[[
-Send the given data as JSON encoded string.
-
-@class                 function
-@name                  write_json
-@param data            Data to send
-]]
-
----[[
-Extract and split urlencoded data pairs, separated bei either "&" or ";"
-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.
-
-@class                 function
-@name                  urldecode_params
-@param url             The url or string which contains x-www-urlencoded form data
-@param tbl             Use the given table for storing values (optional)
-@return                        Table containing the urldecoded parameters
-@see                   urlencode_params
-]]
-
----[[
-Encode each key-value-pair in given table to x-www-urlencoded format,
-separated by "&".
-
-Tables are encoded as parameters with multiple values by repeating the
-parameter name with each value.
-
-@class                 function
-@name                  urlencode_params
-@param tbl             Table with the values
-@return                        String containing encoded values
-@see                   urldecode_params
-]]
-
----[[
-Decode a mime encoded http message body with multipart/form-data 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 fed 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 whether the current chunk is the last one (eof)
-
-@class                 function
-@name                  mimedecode_message_body
-@param src             Ltn12 source function
-@param msg             HTTP message object
-@param filecb  File callback function (optional)
-@return                        Value indicating successful operation (not nil means "ok")
-@return                        String containing the error if unsuccessful
-@see                   parse_message_header
-]]
-
----[[
-Decode an urlencoded http message body with application/x-www-urlencoded
-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.
-
-@class                 function
-@name                  urldecode_message_body
-@param src             Ltn12 source function
-@param msg             HTTP message object
-@return                        Value indicating successful operation (not nil means "ok")
-@return                        String containing the error if unsuccessful
-@see                   parse_message_header
-]]
-
----[[
-Try to extract and decode a http message body from the given ltn12 source.
-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.
-
-@class                 function
-@name                  parse_message_body
-@param src             Ltn12 source function
-@param msg             HTTP message object
-@param filecb  File data callback (optional, see mimedecode_message_body())
-@return                        Value indicating successful operation (not nil means "ok")
-@return                        String containing the error if unsuccessful
-@see                   parse_message_header
-]]
diff --git a/modules/luci-base/luasrc/ltn12.lua b/modules/luci-base/luasrc/ltn12.lua
deleted file mode 100644 (file)
index 3a7268c..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
---[[
-LuaSocket 2.0.2 license
-Copyright ļæ½ 2004-2007 Diego Nehab
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-]]--
---[[
-       Changes made by LuCI project:
-               * Renamed to luci.ltn12 to avoid collisions with luasocket
-               * Added inline documentation
-]]--
------------------------------------------------------------------------------
--- LTN12 - Filters, sources, sinks and pumps.
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id$
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module
------------------------------------------------------------------------------
-local string = require("string")
-local table = require("table")
-local base = _G
-
--- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
-module("luci.ltn12")
-
-filter = {}
-source = {}
-sink = {}
-pump = {}
-
--- 2048 seems to be better in windows...
-BLOCKSIZE = 2048
-_VERSION = "LTN12 1.0.1"
-
------------------------------------------------------------------------------
--- Filter stuff
------------------------------------------------------------------------------
-
-
--- by passing it each chunk and updating a context between calls. 
-function filter.cycle(low, ctx, extra)
-    base.assert(low)
-    return function(chunk)
-        local ret
-        ret, ctx = low(ctx, chunk, extra)
-        return ret
-    end
-end
-
--- (thanks to Wim Couwenberg)
-function filter.chain(...)
-    local n = table.getn(arg)
-    local top, index = 1, 1
-    local retry = ""
-    return function(chunk)
-        retry = chunk and retry
-        while true do
-            if index == top then
-                chunk = arg[index](chunk)
-                if chunk == "" or top == n then return chunk
-                elseif chunk then index = index + 1
-                else
-                    top = top+1
-                    index = top
-                end
-            else
-                chunk = arg[index](chunk or "")
-                if chunk == "" then
-                    index = index - 1
-                    chunk = retry
-                elseif chunk then
-                    if index == n then return chunk
-                    else index = index + 1 end
-                else base.error("filter returned inappropriate nil") end
-            end
-        end
-    end
-end
-
------------------------------------------------------------------------------
--- Source stuff
------------------------------------------------------------------------------
-
-
--- create an empty source
-local function empty()
-    return nil
-end
-
-function source.empty()
-    return empty
-end
-
-function source.error(err)
-    return function()
-        return nil, err
-    end
-end
-
-function source.file(handle, io_err)
-    if handle then
-        return function()
-            local chunk = handle:read(BLOCKSIZE)
-            if chunk and chunk:len() == 0 then chunk = nil end
-            if not chunk then handle:close() end
-            return chunk
-        end
-    else return source.error(io_err or "unable to open file") end
-end
-
-function source.simplify(src)
-    base.assert(src)
-    return function()
-        local chunk, err_or_new = src()
-        src = err_or_new or src
-        if not chunk then return nil, err_or_new
-        else return chunk end
-    end
-end
-
-function source.string(s)
-    if s then
-        local i = 1
-        return function()
-            local chunk = string.sub(s, i, i+BLOCKSIZE-1)
-            i = i + BLOCKSIZE
-            if chunk ~= "" then return chunk
-            else return nil end
-        end
-    else return source.empty() end
-end
-
-function source.rewind(src)
-    base.assert(src)
-    local t = {}
-    return function(chunk)
-        if not chunk then
-            chunk = table.remove(t)
-            if not chunk then return src()
-            else return chunk end
-        else
-            t[#t+1] = chunk
-        end
-    end
-end
-
-function source.chain(src, f)
-    base.assert(src and f)
-    local last_in, last_out = "", ""
-    local state = "feeding"
-    local err
-    return function()
-        if not last_out then
-            base.error('source is empty!', 2)
-        end
-        while true do
-            if state == "feeding" then
-                last_in, err = src()
-                if err then return nil, err end
-                last_out = f(last_in)
-                if not last_out then
-                    if last_in then
-                        base.error('filter returned inappropriate nil')
-                    else
-                        return nil
-                    end
-                elseif last_out ~= "" then
-                    state = "eating"
-                    if last_in then last_in = "" end
-                    return last_out
-                end
-            else
-                last_out = f(last_in)
-                if last_out == "" then
-                    if last_in == "" then
-                        state = "feeding"
-                    else
-                        base.error('filter returned ""')
-                    end
-                elseif not last_out then
-                    if last_in then
-                        base.error('filter returned inappropriate nil')
-                    else
-                        return nil
-                    end
-                else
-                    return last_out
-                end
-            end
-        end
-    end
-end
-
--- Sources will be used one after the other, as if they were concatenated
--- (thanks to Wim Couwenberg)
-function source.cat(...)
-    local src = table.remove(arg, 1)
-    return function()
-        while src do
-            local chunk, err = src()
-            if chunk then return chunk end
-            if err then return nil, err end
-            src = table.remove(arg, 1)
-        end
-    end
-end
-
------------------------------------------------------------------------------
--- Sink stuff
------------------------------------------------------------------------------
-
-
-function sink.table(t)
-    t = t or {}
-    local f = function(chunk, err)
-        if chunk then t[#t+1] = chunk end
-        return 1
-    end
-    return f, t
-end
-
-function sink.simplify(snk)
-    base.assert(snk)
-    return function(chunk, err)
-        local ret, err_or_new = snk(chunk, err)
-        if not ret then return nil, err_or_new end
-        snk = err_or_new or snk
-        return 1
-    end
-end
-
-function sink.file(handle, io_err)
-    if handle then
-        return function(chunk, err)
-            if not chunk then
-                handle:close()
-                return 1
-            else return handle:write(chunk) end
-        end
-    else return sink.error(io_err or "unable to open file") end
-end
-
--- creates a sink that discards data
-local function null()
-    return 1
-end
-
-function sink.null()
-    return null
-end
-
-function sink.error(err)
-    return function()
-        return nil, err
-    end
-end
-
-function sink.chain(f, snk)
-    base.assert(f and snk)
-    return function(chunk, err)
-        if chunk ~= "" then
-            local filtered = f(chunk)
-            local done = chunk and ""
-            while true do
-                local ret, snkerr = snk(filtered, err)
-                if not ret then return nil, snkerr end
-                if filtered == done then return 1 end
-                filtered = f(done)
-            end
-        else return 1 end
-    end
-end
-
------------------------------------------------------------------------------
--- Pump stuff
------------------------------------------------------------------------------
-
-
-function pump.step(src, snk)
-    local chunk, src_err = src()
-    local ret, snk_err = snk(chunk, src_err)
-    if chunk and ret then return 1
-    else return nil, src_err or snk_err end
-end
-
-function pump.all(src, snk, step)
-    base.assert(src and snk)
-    step = step or pump.step
-    while true do
-        local ret, err = step(src, snk)
-        if not ret then
-            if err then return nil, err
-            else return 1 end
-        end
-    end
-end
-
diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua
deleted file mode 100644 (file)
index a30e8b7..0000000
+++ /dev/null
@@ -1,776 +0,0 @@
--- Copyright 2008 Steven Barth <[email protected]>
--- Licensed to the public under the Apache License 2.0.
-
-local io = require "io"
-local math = require "math"
-local table = require "table"
-local debug = require "debug"
-local ldebug = require "luci.debug"
-local string = require "string"
-local coroutine = require "coroutine"
-local tparser = require "luci.template.parser"
-local json = require "luci.jsonc"
-local lhttp = require "lucihttp"
-
-local _ubus = require "ubus"
-local _ubus_connection = nil
-
-local getmetatable, setmetatable = getmetatable, setmetatable
-local rawget, rawset, unpack, select = rawget, rawset, unpack, select
-local tostring, type, assert, error = tostring, type, assert, error
-local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
-local require, pcall, xpcall = require, pcall, xpcall
-local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
-
-module "luci.util"
-
---
--- Pythonic string formatting extension
---
-getmetatable("").__mod = function(a, b)
-       local ok, res
-
-       if not b then
-               return a
-       elseif type(b) == "table" then
-               local k, _
-               for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
-
-               ok, res = pcall(a.format, a, unpack(b))
-               if not ok then
-                       error(res, 2)
-               end
-               return res
-       else
-               if type(b) == "userdata" then b = tostring(b) end
-
-               ok, res = pcall(a.format, a, b)
-               if not ok then
-                       error(res, 2)
-               end
-               return res
-       end
-end
-
-
---
--- Class helper routines
---
-
--- Instantiates a class
-local function _instantiate(class, ...)
-       local inst = setmetatable({}, {__index = class})
-
-       if inst.__init__ then
-               inst:__init__(...)
-       end
-
-       return inst
-end
-
--- The class object can be instantiated by calling itself.
--- Any class functions or shared parameters can be attached to this object.
--- Attaching a table to the class object makes this table shared between
--- all instances of this class. For object parameters use the __init__ function.
--- Classes can inherit member functions and values from a base class.
--- Class can be instantiated by calling them. All parameters will be passed
--- to the __init__ function of this class - if such a function exists.
--- The __init__ function must be used to set any object parameters that are not shared
--- with other objects of this class. Any return values will be ignored.
-function class(base)
-       return setmetatable({}, {
-               __call  = _instantiate,
-               __index = base
-       })
-end
-
-function instanceof(object, class)
-       local meta = getmetatable(object)
-       while meta and meta.__index do
-               if meta.__index == class then
-                       return true
-               end
-               meta = getmetatable(meta.__index)
-       end
-       return false
-end
-
-
---
--- Scope manipulation routines
---
-
-coxpt = setmetatable({}, { __mode = "kv" })
-
-local tl_meta = {
-       __mode = "k",
-
-       __index = function(self, key)
-               local t = rawget(self, coxpt[coroutine.running()]
-                or coroutine.running() or 0)
-               return t and t[key]
-       end,
-
-       __newindex = function(self, key, value)
-               local c = coxpt[coroutine.running()] or coroutine.running() or 0
-               local r = rawget(self, c)
-               if not r then
-                       rawset(self, c, { [key] = value })
-               else
-                       r[key] = value
-               end
-       end
-}
-
--- the current active coroutine. A thread local store is private a table object
--- whose values can't be accessed from outside of the running coroutine.
-function threadlocal(tbl)
-       return setmetatable(tbl or {}, tl_meta)
-end
-
-
---
--- Debugging routines
---
-
-function perror(obj)
-       return io.stderr:write(tostring(obj) .. "\n")
-end
-
-function dumptable(t, maxdepth, i, seen)
-       i = i or 0
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       for k,v in pairs(t) do
-               perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
-               if type(v) == "table" and (not maxdepth or i < maxdepth) then
-                       if not seen[v] then
-                               seen[v] = true
-                               dumptable(v, maxdepth, i+1, seen)
-                       else
-                               perror(string.rep("\t", i) .. "*** RECURSION ***")
-                       end
-               end
-       end
-end
-
-
---
--- String and data manipulation routines
---
-
-function pcdata(value)
-       return value and tparser.pcdata(tostring(value))
-end
-
-function urlencode(value)
-       if value ~= nil then
-               local str = tostring(value)
-               return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
-                       or str
-       end
-       return nil
-end
-
-function urldecode(value, decode_plus)
-       if value ~= nil then
-               local flag = decode_plus and lhttp.DECODE_PLUS or 0
-               local str = tostring(value)
-               return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
-                       or str
-       end
-       return nil
-end
-
-function striptags(value)
-       return value and tparser.striptags(tostring(value))
-end
-
-function shellquote(value)
-       return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
-end
-
--- for bash, ash and similar shells single-quoted strings are taken
--- literally except for single quotes (which terminate the string)
--- (and the exception noted below for dash (-) at the start of a
--- command line parameter).
-function shellsqescape(value)
-   local res
-   res, _ = string.gsub(value, "'", "'\\''")
-   return res
-end
-
--- bash, ash and other similar shells interpret a dash (-) at the start
--- of a command-line parameters as an option indicator regardless of
--- whether it is inside a single-quoted string.  It must be backlash
--- escaped to resolve this.  This requires in some funky special-case
--- handling.  It may actually be a property of the getopt function
--- rather than the shell proper.
-function shellstartsqescape(value)
-   res, _ = string.gsub(value, "^%-", "\\-")
-   return shellsqescape(res)
-end
-
--- containing the resulting substrings. The optional max parameter specifies
--- the number of bytes to process, regardless of the actual length of the given
--- string. The optional last parameter, regex, specifies whether the separator
--- sequence is interpreted as regular expression.
---                                     pattern as regular expression (optional, default is false)
-function split(str, pat, max, regex)
-       pat = pat or "\n"
-       max = max or #str
-
-       local t = {}
-       local c = 1
-
-       if #str == 0 then
-               return {""}
-       end
-
-       if #pat == 0 then
-               return nil
-       end
-
-       if max == 0 then
-               return str
-       end
-
-       repeat
-               local s, e = str:find(pat, c, not regex)
-               max = max - 1
-               if s and max < 0 then
-                       t[#t+1] = str:sub(c)
-               else
-                       t[#t+1] = str:sub(c, s and s - 1)
-               end
-               c = e and e + 1 or #str + 1
-       until not s or max < 0
-
-       return t
-end
-
-function trim(str)
-       return (str:gsub("^%s*(.-)%s*$", "%1"))
-end
-
-function cmatch(str, pat)
-       local count = 0
-       for _ in str:gmatch(pat) do count = count + 1 end
-       return count
-end
-
--- one token per invocation, the tokens are separated by whitespace. If the
--- input value is a table, it is transformed into a string first. A nil value
--- will result in a valid iterator which aborts with the first invocation.
-function imatch(v)
-       if type(v) == "table" then
-               local k = nil
-               return function()
-                       k = next(v, k)
-                       return v[k]
-               end
-
-       elseif type(v) == "number" or type(v) == "boolean" then
-               local x = true
-               return function()
-                       if x then
-                               x = false
-                               return tostring(v)
-                       end
-               end
-
-       elseif type(v) == "userdata" or type(v) == "string" then
-               return tostring(v):gmatch("%S+")
-       end
-
-       return function() end
-end
-
--- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
--- Recognized units are:
---     o "y"   - one year   (60*60*24*366)
---  o "m"      - one month  (60*60*24*31)
---  o "w"      - one week   (60*60*24*7)
---  o "d"      - one day    (60*60*24)
---  o "h"      - one hour       (60*60)
---  o "min"    - one minute (60)
---  o "kb"  - one kilobyte (1024)
---  o "mb"     - one megabyte (1024*1024)
---  o "gb"     - one gigabyte (1024*1024*1024)
---  o "kib" - one si kilobyte (1000)
---  o "mib"    - one si megabyte (1000*1000)
---  o "gib"    - one si gigabyte (1000*1000*1000)
-function parse_units(ustr)
-
-       local val = 0
-
-       -- unit map
-       local map = {
-               -- date stuff
-               y   = 60 * 60 * 24 * 366,
-               m   = 60 * 60 * 24 * 31,
-               w   = 60 * 60 * 24 * 7,
-               d   = 60 * 60 * 24,
-               h   = 60 * 60,
-               min = 60,
-
-               -- storage sizes
-               kb  = 1024,
-               mb  = 1024 * 1024,
-               gb  = 1024 * 1024 * 1024,
-
-               -- storage sizes (si)
-               kib = 1000,
-               mib = 1000 * 1000,
-               gib = 1000 * 1000 * 1000
-       }
-
-       -- parse input string
-       for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
-
-               local num = spec:gsub("[^0-9%.]+$","")
-               local spn = spec:gsub("^[0-9%.]+", "")
-
-               if map[spn] or map[spn:sub(1,1)] then
-                       val = val + num * ( map[spn] or map[spn:sub(1,1)] )
-               else
-                       val = val + num
-               end
-       end
-
-
-       return val
-end
-
--- also register functions above in the central string class for convenience
-string.pcdata      = pcdata
-string.striptags   = striptags
-string.split       = split
-string.trim        = trim
-string.cmatch      = cmatch
-string.parse_units = parse_units
-
-
-function append(src, ...)
-       for i, a in ipairs({...}) do
-               if type(a) == "table" then
-                       for j, v in ipairs(a) do
-                               src[#src+1] = v
-                       end
-               else
-                       src[#src+1] = a
-               end
-       end
-       return src
-end
-
-function combine(...)
-       return append({}, ...)
-end
-
-function contains(table, value)
-       for k, v in pairs(table) do
-               if value == v then
-                       return k
-               end
-       end
-       return false
-end
-
--- Both table are - in fact - merged together.
-function update(t, updates)
-       for k, v in pairs(updates) do
-               t[k] = v
-       end
-end
-
-function keys(t)
-       local keys = { }
-       if t then
-               for k, _ in kspairs(t) do
-                       keys[#keys+1] = k
-               end
-       end
-       return keys
-end
-
-function clone(object, deep)
-       local copy = {}
-
-       for k, v in pairs(object) do
-               if deep and type(v) == "table" then
-                       v = clone(v, deep)
-               end
-               copy[k] = v
-       end
-
-       return setmetatable(copy, getmetatable(object))
-end
-
-
--- Serialize the contents of a table value.
-function _serialize_table(t, seen)
-       assert(not seen[t], "Recursion detected.")
-       seen[t] = true
-
-       local data  = ""
-       local idata = ""
-       local ilen  = 0
-
-       for k, v in pairs(t) do
-               if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
-                       k = serialize_data(k, seen)
-                       v = serialize_data(v, seen)
-                       data = data .. ( #data > 0 and ", " or "" ) ..
-                               '[' .. k .. '] = ' .. v
-               elseif k > ilen then
-                       ilen = k
-               end
-       end
-
-       for i = 1, ilen do
-               local v = serialize_data(t[i], seen)
-               idata = idata .. ( #idata > 0 and ", " or "" ) .. v
-       end
-
-       return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
-end
-
--- with loadstring().
-function serialize_data(val, seen)
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       if val == nil then
-               return "nil"
-       elseif type(val) == "number" then
-               return val
-       elseif type(val) == "string" then
-               return "%q" % val
-       elseif type(val) == "boolean" then
-               return val and "true" or "false"
-       elseif type(val) == "function" then
-               return "loadstring(%q)" % get_bytecode(val)
-       elseif type(val) == "table" then
-               return "{ " .. _serialize_table(val, seen) .. " }"
-       else
-               return '"[unhandled data type:' .. type(val) .. ']"'
-       end
-end
-
-function restore_data(str)
-       return loadstring("return " .. str)()
-end
-
-
---
--- Byte code manipulation routines
---
-
--- will be stripped before it is returned.
-function get_bytecode(val)
-       local code
-
-       if type(val) == "function" then
-               code = string.dump(val)
-       else
-               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
-       end
-
-       return code -- and strip_bytecode(code)
-end
-
--- numbers and debugging numbers will be discarded. Original version by
--- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
-function strip_bytecode(code)
-       local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
-       local subint
-       if endian == 1 then
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = l, 1, -1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       else
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = 1, l, 1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       end
-
-       local function strip_function(code)
-               local count, offset = subint(code, 1, size)
-               local stripped = { string.rep("\0", size) }
-               local dirty = offset + count
-               offset = offset + count + int * 2 + 4
-               offset = offset + int + subint(code, offset, int) * ins
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       local t
-                       t, offset = subint(code, offset, 1)
-                       if t == 1 then
-                               offset = offset + 1
-                       elseif t == 4 then
-                               offset = offset + size + subint(code, offset, size)
-                       elseif t == 3 then
-                               offset = offset + num
-                       elseif t == 254 or t == 9 then
-                               offset = offset + lnum
-                       end
-               end
-               count, offset = subint(code, offset, int)
-               stripped[#stripped+1] = code:sub(dirty, offset - 1)
-               for n = 1, count do
-                       local proto, off = strip_function(code:sub(offset, -1))
-                       stripped[#stripped+1] = proto
-                       offset = offset + off - 1
-               end
-               offset = offset + subint(code, offset, int) * int + int
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size + int * 2
-               end
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size
-               end
-               stripped[#stripped+1] = string.rep("\0", int * 3)
-               return table.concat(stripped), offset
-       end
-
-       return code:sub(1,12) .. strip_function(code:sub(13,-1))
-end
-
-
---
--- Sorting iterator functions
---
-
-function _sortiter( t, f )
-       local keys = { }
-
-       local k, v
-       for k, v in pairs(t) do
-               keys[#keys+1] = k
-       end
-
-       local _pos = 0
-
-       table.sort( keys, f )
-
-       return function()
-               _pos = _pos + 1
-               if _pos <= #keys then
-                       return keys[_pos], t[keys[_pos]], _pos
-               end
-       end
-end
-
--- the provided callback function.
-function spairs(t,f)
-       return _sortiter( t, f )
-end
-
--- The table pairs are sorted by key.
-function kspairs(t)
-       return _sortiter( t )
-end
-
--- The table pairs are sorted by value.
-function vspairs(t)
-       return _sortiter( t, function (a,b) return t[a] < t[b] end )
-end
-
-
---
--- System utility functions
---
-
-function bigendian()
-       return string.byte(string.dump(function() end), 7) == 0
-end
-
-function exec(command)
-       local pp   = io.popen(command)
-       local data = pp:read("*a")
-       pp:close()
-
-       return data
-end
-
-function execi(command)
-       local pp = io.popen(command)
-
-       return pp and function()
-               local line = pp:read()
-
-               if not line then
-                       pp:close()
-               end
-
-               return line
-       end
-end
-
--- Deprecated
-function execl(command)
-       local pp   = io.popen(command)
-       local line = ""
-       local data = {}
-
-       while true do
-               line = pp:read()
-               if (line == nil) then break end
-               data[#data+1] = line
-       end
-       pp:close()
-
-       return data
-end
-
-
-local ubus_codes = {
-       "INVALID_COMMAND",
-       "INVALID_ARGUMENT",
-       "METHOD_NOT_FOUND",
-       "NOT_FOUND",
-       "NO_DATA",
-       "PERMISSION_DENIED",
-       "TIMEOUT",
-       "NOT_SUPPORTED",
-       "UNKNOWN_ERROR",
-       "CONNECTION_FAILED"
-}
-
-local function ubus_return(...)
-       if select('#', ...) == 2 then
-               local rv, err = select(1, ...), select(2, ...)
-               if rv == nil and type(err) == "number" then
-                       return nil, err, ubus_codes[err]
-               end
-       end
-
-       return ...
-end
-
-function ubus(object, method, data)
-       if not _ubus_connection then
-               _ubus_connection = _ubus.connect()
-               assert(_ubus_connection, "Unable to establish ubus connection")
-       end
-
-       if object and method then
-               if type(data) ~= "table" then
-                       data = { }
-               end
-               return ubus_return(_ubus_connection:call(object, method, data))
-       elseif object then
-               return _ubus_connection:signatures(object)
-       else
-               return _ubus_connection:objects()
-       end
-end
-
-function serialize_json(x, cb)
-       local js = json.stringify(x)
-       if type(cb) == "function" then
-               cb(js)
-       else
-               return js
-       end
-end
-
-
-function libpath()
-       return require "nixio.fs".dirname(ldebug.__file__)
-end
-
-function checklib(fullpathexe, wantedlib)
-       local fs = require "nixio.fs"
-       local haveldd = fs.access('/usr/bin/ldd')
-       local haveexe = fs.access(fullpathexe)
-       if not haveldd or not haveexe then
-               return false
-       end
-       local libs = exec(string.format("/usr/bin/ldd %s", shellquote(fullpathexe)))
-       if not libs then
-               return false
-       end
-       for k, v in ipairs(split(libs)) do
-               if v:find(wantedlib) then
-                       return true
-               end
-       end
-       return false
-end
-
--------------------------------------------------------------------------------
--- Coroutine safe xpcall and pcall versions
---
--- Encapsulates the protected calls with a coroutine based loop, so errors can
--- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
--- yielding inside the call to pcall or xpcall.
---
--- Authors: Roberto Ierusalimschy and Andre Carregal
--- Contributors: Thomas Harning Jr., Ignacio BurgueƱo, Fabio Mascarenhas
---
--- Copyright 2005 - Kepler Project
---
--- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
--------------------------------------------------------------------------------
-
--------------------------------------------------------------------------------
--- Implements xpcall with coroutines
--------------------------------------------------------------------------------
-local coromap = setmetatable({}, { __mode = "k" })
-
-local function handleReturnValue(err, co, status, ...)
-       if not status then
-               return false, err(debug.traceback(co, (...)), ...)
-       end
-       if coroutine.status(co) == 'suspended' then
-               return performResume(err, co, coroutine.yield(...))
-       else
-               return true, ...
-       end
-end
-
-function performResume(err, co, ...)
-       return handleReturnValue(err, co, coroutine.resume(co, ...))
-end
-
-local function id(trace, ...)
-       return trace
-end
-
-function coxpcall(f, err, ...)
-       local current = coroutine.running()
-       if not current then
-               if err == id then
-                       return pcall(f, ...)
-               else
-                       if select("#", ...) > 0 then
-                               local oldf, params = f, { ... }
-                               f = function() return oldf(unpack(params)) end
-                       end
-                       return xpcall(f, err)
-               end
-       else
-               local res, co = pcall(coroutine.create, f)
-               if not res then
-                       local newf = function(...) return f(...) end
-                       co = coroutine.create(newf)
-               end
-               coromap[co] = current
-               coxpt[co] = coxpt[current] or current or 0
-               return performResume(err, co, ...)
-       end
-end
-
-function copcall(f, ...)
-       return coxpcall(f, id, ...)
-end
diff --git a/modules/luci-base/luasrc/util.luadoc b/modules/luci-base/luasrc/util.luadoc
deleted file mode 100644 (file)
index 4ec68dd..0000000
+++ /dev/null
@@ -1,413 +0,0 @@
----[[
-LuCI utility functions.
-]]
-module "luci.util"
-
----[[
-Create a Class object (Python-style object model).
-
-The class object can be instantiated by calling itself.
-Any class functions or shared parameters can be attached to this object.
-Attaching a table to the class object makes this table shared between
-all instances of this class. For object parameters use the __init__ function.
-Classes can inherit member functions and values from a base class.
-Class can be instantiated by calling them. All parameters will be passed
-to the __init__ function of this class - if such a function exists.
-The __init__ function must be used to set any object parameters that are not shared
-with other objects of this class. Any return values will be ignored.
-
-@class                         function
-@name                          class
-@param base                    The base class to inherit from (optional)
-@return                                A class object
-@see                           instanceof
-@see                           clone
-]]
-
----[[
-Test whether the given object is an instance of the given class.
-
-@class                         function
-@name                          instanceof
-@param object          Object instance
-@param class           Class object to test against
-@return                                Boolean indicating whether the object is an instance
-@see                           class
-@see                           clone
-]]
-
----[[
-Create a new or get an already existing thread local store associated with
-the current active coroutine.
-
-A thread local store is private a table object
-whose values can't be accessed from outside of the running coroutine.
-
-@class                         function
-@name                          threadlocal
-@return                                Table value representing the corresponding thread local store
-]]
-
----[[
-Write given object to stderr.
-
-@class                         function
-@name                          perror
-@param obj                     Value to write to stderr
-@return                                Boolean indicating whether the write operation was successful
-]]
-
----[[
-Recursively dumps a table to stdout, useful for testing and debugging.
-
-@class                         function
-@name                          dumptable
-@param t                       Table value to dump
-@param maxdepth                Maximum depth
-@return                                Always nil
-]]
-
----[[
-Create valid XML PCDATA from given string.
-
-@class                         function
-@name                          pcdata
-@param value           String value containing the data to escape
-@return                                String value containing the escaped data
-]]
-
----[[
-Decode an URL-encoded string - optionally decoding the "+" sign to space.
-
-@class                         function
-@name                          urldecode
-@param str                     Input string in x-www-urlencoded format
-@param decode_plus     Decode "+" signs to spaces if true (optional)
-@return                                The decoded string
-@see                           urlencode
-]]
-
----[[
-URL-encode given string.
-
-@class                         function
-@name                          urlencode
-@param str                     String to encode
-@return                                String containing the encoded data
-@see                           urldecode
-]]
-
----[[
-Strip HTML tags from given string.
-
-@class                         function
-@name                          striptags
-@param value           String containing the HTML text
-@return                                String with HTML tags stripped of
-]]
-
----[[
-Safely quote value for use in shell commands.
-
-@class                         function
-@name                          shellquote
-@param value           String containing the value to quote
-@return                        Single-quote enclosed string with embedded quotes escaped
-]]
-
----[[
-Splits given string on a defined separator sequence and return a table
-containing the resulting substrings.
-
-The optional max parameter specifies the number of bytes to process,
-regardless of the actual length of the given string. The optional last
-parameter, regex, specifies whether the separator sequence is
-nterpreted as regular expression.
-
-@class                         function
-@name                          split
-@param str                     String value containing the data to split up
-@param pat                     String with separator pattern (optional, defaults to "\n")
-@param max                     Maximum times to split (optional)
-@param regex           Boolean indicating whether to interpret the separator
---                                     pattern as regular expression (optional, default is false)
-@return                                Table containing the resulting substrings
-]]
-
----[[
-Remove leading and trailing whitespace from given string value.
-
-@class                         function
-@name                          trim
-@param str                     String value containing whitespace padded data
-@return                                String value with leading and trailing space removed
-]]
-
----[[
-Count the occurrences of given substring in given string.
-
-@class                         function
-@name                          cmatch
-@param str                     String to search in
-@param pattern         String containing pattern to find
-@return                                Number of found occurrences
-]]
-
----[[
-Return a matching iterator for the given value.
-
-The iterator will return one token per invocation, the tokens are separated by
-whitespace. If the input value is a table, it is transformed into a string first.
-A nil value will result in a valid iterator which aborts with the first invocation.
-
-@class                         function
-@name                          imatch
-@param val                     The value to scan (table, string or nil)
-@return                                Iterator which returns one token per call
-]]
-
----[[
-Parse certain units from the given string and return the canonical integer
-value or 0 if the unit is unknown.
-
-Upper- or lower case is irrelevant.
-Recognized units are:
-
---     o "y"   - one year   (60*60*24*366)
- o "m" - one month  (60*60*24*31)
- o "w" - one week   (60*60*24*7)
- o "d" - one day    (60*60*24)
- o "h" - one hour       (60*60)
- o "min"       - one minute (60)
- o "kb"  - one kilobyte (1024)
- o "mb"        - one megabyte (1024*1024)
- o "gb"        - one gigabyte (1024*1024*1024)
- o "kib" - one si kilobyte (1000)
- o "mib"       - one si megabyte (1000*1000)
- o "gib"       - one si gigabyte (1000*1000*1000)
-
-@class                         function
-@name                          parse_units
-@param ustr                    String containing a numerical value with trailing unit
-@return                                Number containing the canonical value
-]]
-
----[[
-Appends numerically indexed tables or single objects to a given table.
-
-@class                         function
-@name                          append
-@param src                     Target table
-@param ...                     Objects to insert
-@return                                Target table
-]]
-
----[[
-Combines two or more numerically indexed tables and single objects into one table.
-
-@class                         function
-@name                          combine
-@param tbl1                    Table value to combine
-@param tbl2                    Table value to combine
-@param ...                     More tables to combine
-@return                                Table value containing all values of given tables
-]]
-
----[[
-Checks whether the given table contains the given value.
-
-@class                         function
-@name                          contains
-@param table           Table value
-@param value           Value to search within the given table
-@return                                Number indicating the first index at which the given value occurs
---                                     within table or false.
-]]
-
----[[
-Update values in given table with the values from the second given table.
-
-Both table are - in fact - merged together.
-
-@class                         function
-@name                          update
-@param t                       Table which should be updated
-@param updates         Table containing the values to update
-@return                                Always nil
-]]
-
----[[
-Retrieve all keys of given associative table.
-
-@class                         function
-@name                          keys
-@param t                       Table to extract keys from
-@return                                Sorted table containing the keys
-]]
-
----[[
-Clones the given object and return it's copy.
-
-@class                         function
-@name                          clone
-@param object          Table value to clone
-@param deep                    Boolean indicating whether to do recursive cloning
-@return                                Cloned table value
-]]
-
----[[
-Recursively serialize given data to lua code, suitable for restoring
-with loadstring().
-
-@class                         function
-@name                          serialize_data
-@param val                     Value containing the data to serialize
-@return                                String value containing the serialized code
-@see                           restore_data
-@see                           get_bytecode
-]]
-
----[[
-Restore data previously serialized with serialize_data().
-
-@class                         function
-@name                          restore_data
-@param str                     String containing the data to restore
-@return                                Value containing the restored data structure
-@see                           serialize_data
-@see                           get_bytecode
-]]
-
----[[
-Return the current runtime bytecode of the given data. The byte code
-will be stripped before it is returned.
-
-@class                         function
-@name                          get_bytecode
-@param val                     Value to return as bytecode
-@return                                String value containing the bytecode of the given data
-]]
-
----[[
-Strips unnecessary lua bytecode from given string.
-
-Information like line numbers and debugging numbers will be discarded.
-Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
-
-@class                         function
-@name                          strip_bytecode
-@param code                    String value containing the original lua byte code
-@return                                String value containing the stripped lua byte code
-]]
-
----[[
-Return a key, value iterator which returns the values sorted according to
-the provided callback function.
-
-@class                         function
-@name                          spairs
-@param t                       The table to iterate
-@param f                       A callback function to decide the order of elements
-@return                                Function value containing the corresponding iterator
-]]
-
----[[
-Return a key, value iterator for the given table.
-
-The table pairs are sorted by key.
-
-@class                         function
-@name                          kspairs
-@param t                       The table to iterate
-@return                                Function value containing the corresponding iterator
-]]
-
----[[
-Return a key, value iterator for the given table.
-
-The table pairs are sorted by value.
-
-@class                         function
-@name                          vspairs
-@param t                       The table to iterate
-@return                                Function value containing the corresponding iterator
-]]
-
----[[
-Test whether the current system is operating in big endian mode.
-
-@class                         function
-@name                          bigendian
-@return                                Boolean value indicating whether system is big endian
-]]
-
----[[
-Execute given commandline and gather stdout.
-
-@class                         function
-@name                          exec
-@param command         String containing command to execute
-@return                                String containing the command's stdout
-]]
-
----[[
-Return a line-buffered iterator over the output of given command.
-
-@class                         function
-@name                          execi
-@param command         String containing the command to execute
-@return                                Iterator
-]]
-
----[[
-Issue an ubus call.
-
-@class                         function
-@name                          ubus
-@param object          String containing the ubus object to call
-@param method          String containing the ubus method to call
-@param values          Table containing the values to pass
-@return                                Table containin the ubus result
-]]
-
----[[
-Convert data structure to JSON
-
-@class                         function
-@name                          serialize_json
-@param data                    The data to serialize
-@param writer          A function to write a chunk of JSON data (optional)
-@return                                String containing the JSON if called without write callback
-]]
-
----[[
-Returns the absolute path to LuCI base directory.
-
-@class                         function
-@name                          libpath
-@return                                String containing the directory path
-]]
-
----[[
-This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
-
-@class                         function
-@name                          coxpcall
-@param f                       Lua function to be called protected
-@param err                     Custom error handler
-@param ...                     Parameters passed to the function
-@return                                A boolean whether the function call succeeded and the return
---                                     values of either the function or the error handler
-]]
-
----[[
-This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
-
-@class                         function
-@name                          copcall
-@param f                       Lua function to be called protected
-@param ...                     Parameters passed to the function
-@return                                A boolean whether the function call succeeded and the returns
---                                     values of the function or the error object
-]]
-