libs/core: Reworked some basic libraries to not use package.seeall
libs/json: Implemented own JSON-Decoder (Encoder will follow) modules/rpc: Preliminary implemented RPC-Exports for luci.fs, luci.sys and luci.model.uci
This commit is contained in:
parent
8e6d1e682a
commit
0c5dc7bc77
8 changed files with 476 additions and 521 deletions
|
@ -24,10 +24,12 @@ limitations under the License.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
--- LuCI filesystem library.
|
local posix = require "posix"
|
||||||
module("luci.fs", package.seeall)
|
local io = require "io"
|
||||||
|
local type = type
|
||||||
|
|
||||||
require("posix")
|
--- LuCI filesystem library.
|
||||||
|
module "luci.fs"
|
||||||
|
|
||||||
--- Test for file access permission on given path.
|
--- Test for file access permission on given path.
|
||||||
-- @class function
|
-- @class function
|
||||||
|
|
|
@ -1,519 +1,319 @@
|
||||||
--[[
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
JSON Encoder and Parser for Lua 5.1
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||||
Copyright <EFBFBD> 2007 Shaun Brown (http://www.chipmunkav.com).
|
|
||||||
All Rights Reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
|
||||||
obtaining a copy of this 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
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
included in all copies or substantial portions of the Software.
|
you may not use this file except in compliance with the License.
|
||||||
If you find this software useful please give www.chipmunkav.com a mention.
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
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.
|
|
||||||
|
|
||||||
Usage:
|
$Id$
|
||||||
|
]]--
|
||||||
|
|
||||||
-- Lua script:
|
local util = require "luci.util"
|
||||||
local t = {
|
local ltn12 = require "luci.ltn12"
|
||||||
["name1"] = "value1",
|
local table = require "table"
|
||||||
["name2"] = {1, false, true, 23.54, "a \021 string"},
|
local coroutine = require "coroutine"
|
||||||
name3 = Json.Null()
|
|
||||||
}
|
|
||||||
|
|
||||||
local json = Json.Encode (t)
|
local assert = assert
|
||||||
print (json)
|
local tonumber = tonumber
|
||||||
--> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]}
|
local error = error
|
||||||
|
|
||||||
local t = Json.Decode(json)
|
module "luci.json"
|
||||||
print(t.name2[4])
|
|
||||||
--> 23.54
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
1) Encodable Lua types: string, number, boolean, table, nil
|
|
||||||
2) Use Json.Null() to insert a null value into a Json object
|
|
||||||
3) All control chars are encoded to \uXXXX format eg "\021" encodes to "\u0015"
|
|
||||||
4) All Json \uXXXX chars are decoded to chars (0-255 byte range only)
|
|
||||||
5) Json single line // and /* */ block comments are discarded during decoding
|
|
||||||
6) Numerically indexed Lua arrays are encoded to Json Lists eg [1,2,3]
|
|
||||||
7) Lua dictionary tables are converted to Json objects eg {"one":1,"two":2}
|
|
||||||
8) Json nulls are decoded to Lua nil and treated by Lua in the normal way
|
|
||||||
|
|
||||||
--]]
|
--- Null replacement function
|
||||||
|
-- @return null
|
||||||
local string = string
|
function null()
|
||||||
local math = math
|
return null
|
||||||
local table = table
|
|
||||||
local error = error
|
|
||||||
local tonumber = tonumber
|
|
||||||
local tostring = tostring
|
|
||||||
local type = type
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
local pairs = pairs
|
|
||||||
local ipairs = ipairs
|
|
||||||
local assert = assert
|
|
||||||
local Chipmunk = Chipmunk
|
|
||||||
|
|
||||||
module("luci.json")
|
|
||||||
|
|
||||||
local StringBuilder = {
|
|
||||||
buffer = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function StringBuilder:New()
|
|
||||||
local o = {}
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
o.buffer = {}
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function StringBuilder:Append(s)
|
Decoder = util.class()
|
||||||
self.buffer[#self.buffer+1] = s
|
|
||||||
end
|
|
||||||
|
|
||||||
function StringBuilder:ToString()
|
--- Create an LTN12 sink from the decoder object
|
||||||
return table.concat(self.buffer)
|
-- @return LTN12 sink
|
||||||
end
|
function Decoder.sink(self)
|
||||||
|
local sink = coroutine.create(self.dispatch)
|
||||||
local JsonWriter = {
|
return function(...)
|
||||||
backslashes = {
|
return coroutine.resume(sink, self, ...)
|
||||||
['\b'] = "\\b",
|
|
||||||
['\t'] = "\\t",
|
|
||||||
['\n'] = "\\n",
|
|
||||||
['\f'] = "\\f",
|
|
||||||
['\r'] = "\\r",
|
|
||||||
['"'] = "\\\"",
|
|
||||||
['\\'] = "\\\\",
|
|
||||||
['/'] = "\\/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function JsonWriter:New()
|
|
||||||
local o = {}
|
|
||||||
o.writer = StringBuilder:New()
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:Append(s)
|
|
||||||
self.writer:Append(s)
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:ToString()
|
|
||||||
return self.writer:ToString()
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:Write(o)
|
|
||||||
local t = type(o)
|
|
||||||
if t == "nil" then
|
|
||||||
self:WriteNil()
|
|
||||||
elseif t == "boolean" then
|
|
||||||
self:WriteString(o)
|
|
||||||
elseif t == "number" then
|
|
||||||
self:WriteString(o)
|
|
||||||
elseif t == "string" then
|
|
||||||
self:ParseString(o)
|
|
||||||
elseif t == "table" then
|
|
||||||
self:WriteTable(o)
|
|
||||||
elseif t == "function" then
|
|
||||||
self:WriteFunction(o)
|
|
||||||
elseif t == "thread" then
|
|
||||||
self:WriteError(o)
|
|
||||||
elseif t == "userdata" then
|
|
||||||
self:WriteError(o)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonWriter:WriteNil()
|
|
||||||
self:Append("null")
|
--- Get the decoded data packets
|
||||||
|
-- @return Decoded data
|
||||||
|
function Decoder.get(self)
|
||||||
|
return self.data
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonWriter:WriteString(o)
|
|
||||||
self:Append(tostring(o))
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:ParseString(s)
|
function Decoder.dispatch(self, chunk, src_err, strict)
|
||||||
self:Append('"')
|
local robject, object
|
||||||
self:Append(string.gsub(s, "[%z%c\\\"/]", function(n)
|
|
||||||
local c = self.backslashes[n]
|
while chunk do
|
||||||
if c then return c end
|
if #chunk < 1 then
|
||||||
return string.format("\\u%.4X", string.byte(n))
|
chunk = self:fetch()
|
||||||
end))
|
|
||||||
self:Append('"')
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:IsArray(t)
|
|
||||||
local count = 0
|
|
||||||
local isindex = function(k)
|
|
||||||
if type(k) == "number" and k > 0 then
|
|
||||||
if math.floor(k) == k then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return false
|
|
||||||
end
|
|
||||||
for k,v in pairs(t) do
|
|
||||||
if not isindex(k) then
|
|
||||||
return false, '{', '}'
|
|
||||||
else
|
|
||||||
count = math.max(count, k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true, '[', ']', count
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:WriteTable(t)
|
|
||||||
local ba, st, et, n = self:IsArray(t)
|
|
||||||
self:Append(st)
|
|
||||||
if ba then
|
|
||||||
for i = 1, n do
|
|
||||||
self:Write(t[i])
|
|
||||||
if i < n then
|
|
||||||
self:Append(',')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local first = true;
|
|
||||||
for k, v in pairs(t) do
|
|
||||||
if not first then
|
|
||||||
self:Append(',')
|
|
||||||
end
|
|
||||||
first = false;
|
|
||||||
self:ParseString(k)
|
|
||||||
self:Append(':')
|
|
||||||
self:Write(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self:Append(et)
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:WriteError(o)
|
|
||||||
error(string.format(
|
|
||||||
"Encoding of %s unsupported",
|
|
||||||
tostring(o)))
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonWriter:WriteFunction(o)
|
|
||||||
if o == Null then
|
|
||||||
self:WriteNil()
|
|
||||||
else
|
|
||||||
self:WriteError(o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local StringReader = {
|
|
||||||
s = "",
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function StringReader:New(s)
|
|
||||||
local o = {}
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
o.s = s or o.s
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
function StringReader:Peek()
|
|
||||||
local i = self.i + 1
|
|
||||||
if i <= #self.s then
|
|
||||||
return string.sub(self.s, i, i)
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function StringReader:Next()
|
|
||||||
self.i = self.i+1
|
|
||||||
if self.i <= #self.s then
|
|
||||||
return string.sub(self.s, self.i, self.i)
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function StringReader:All()
|
|
||||||
return self.s
|
|
||||||
end
|
|
||||||
|
|
||||||
local JsonReader = {
|
|
||||||
escapes = {
|
|
||||||
['t'] = '\t',
|
|
||||||
['n'] = '\n',
|
|
||||||
['f'] = '\f',
|
|
||||||
['r'] = '\r',
|
|
||||||
['b'] = '\b',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function JsonReader:New(s)
|
|
||||||
local o = {}
|
|
||||||
o.reader = StringReader:New(s)
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
return o;
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:Read()
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
local peek = self:Peek()
|
|
||||||
if peek == nil then
|
|
||||||
error(string.format(
|
|
||||||
"Nil string: '%s'",
|
|
||||||
self:All()))
|
|
||||||
elseif peek == '{' then
|
|
||||||
return self:ReadObject()
|
|
||||||
elseif peek == '[' then
|
|
||||||
return self:ReadArray()
|
|
||||||
elseif peek == '"' then
|
|
||||||
return self:ReadString()
|
|
||||||
elseif string.find(peek, "[%+%-%d]") then
|
|
||||||
return self:ReadNumber()
|
|
||||||
elseif peek == 't' then
|
|
||||||
return self:ReadTrue()
|
|
||||||
elseif peek == 'f' then
|
|
||||||
return self:ReadFalse()
|
|
||||||
elseif peek == 'n' then
|
|
||||||
return self:ReadNull()
|
|
||||||
elseif peek == '/' then
|
|
||||||
self:ReadComment()
|
|
||||||
return self:Read()
|
|
||||||
else
|
|
||||||
error(string.format(
|
|
||||||
"Invalid input: '%s'",
|
|
||||||
self:All()))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadTrue()
|
assert(not strict or chunk, "Unexpected EOS")
|
||||||
self:TestReservedWord{'t','r','u','e'}
|
if not chunk then
|
||||||
return true
|
break
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadFalse()
|
|
||||||
self:TestReservedWord{'f','a','l','s','e'}
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadNull()
|
|
||||||
self:TestReservedWord{'n','u','l','l'}
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:TestReservedWord(t)
|
|
||||||
for i, v in ipairs(t) do
|
|
||||||
if self:Next() ~= v then
|
|
||||||
error(string.format(
|
|
||||||
"Error reading '%s': %s",
|
|
||||||
table.concat(t),
|
|
||||||
self:All()))
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
local parser = nil
|
||||||
|
local char = chunk:sub(1, 1)
|
||||||
function JsonReader:ReadNumber()
|
|
||||||
local result = self:Next()
|
if char == '"' then
|
||||||
local peek = self:Peek()
|
parser = self.parse_string
|
||||||
while peek ~= nil and string.find(
|
elseif char == 't' then
|
||||||
peek,
|
parser = self.parse_true
|
||||||
"[%+%-%d%.eE]") do
|
elseif char == 'f' then
|
||||||
result = result .. self:Next()
|
parser = self.parse_false
|
||||||
peek = self:Peek()
|
elseif char == 'n' then
|
||||||
end
|
parser = self.parse_null
|
||||||
result = tonumber(result)
|
elseif char == '[' then
|
||||||
if result == nil then
|
parser = self.parse_array
|
||||||
error(string.format(
|
elseif char == '{' then
|
||||||
"Invalid number: '%s'",
|
parser = self.parse_object
|
||||||
result))
|
elseif char:match("%s") then
|
||||||
else
|
parser = self.parse_space
|
||||||
return result
|
elseif char:match("[0-9-]") then
|
||||||
end
|
parser = self.parse_number
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonReader:ReadString()
|
if parser then
|
||||||
local result = ""
|
chunk, robject = parser(self, chunk)
|
||||||
assert(self:Next() == '"')
|
|
||||||
while self:Peek() ~= '"' do
|
if robject ~= nil then
|
||||||
local ch = self:Next()
|
assert(object == nil, "Scope violation: Too many objects")
|
||||||
if ch == '\\' then
|
object = robject
|
||||||
ch = self:Next()
|
|
||||||
if self.escapes[ch] then
|
|
||||||
ch = self.escapes[ch]
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
result = result .. ch
|
if strict and object ~= nil then
|
||||||
end
|
return chunk, object
|
||||||
assert(self:Next() == '"')
|
|
||||||
local fromunicode = function(m)
|
|
||||||
return string.char(tonumber(m, 16))
|
|
||||||
end
|
|
||||||
return string.gsub(
|
|
||||||
result,
|
|
||||||
"u%x%x(%x%x)",
|
|
||||||
fromunicode)
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadComment()
|
|
||||||
assert(self:Next() == '/')
|
|
||||||
local second = self:Next()
|
|
||||||
if second == '/' then
|
|
||||||
self:ReadSingleLineComment()
|
|
||||||
elseif second == '*' then
|
|
||||||
self:ReadBlockComment()
|
|
||||||
else
|
|
||||||
error(string.format(
|
|
||||||
"Invalid comment: %s",
|
|
||||||
self:All()))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadBlockComment()
|
|
||||||
local done = false
|
|
||||||
while not done do
|
|
||||||
local ch = self:Next()
|
|
||||||
if ch == '*' and self:Peek() == '/' then
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
if not done and
|
|
||||||
ch == '/' and
|
|
||||||
self:Peek() == "*" then
|
|
||||||
error(string.format(
|
|
||||||
"Invalid comment: %s, '/*' illegal.",
|
|
||||||
self:All()))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self:Next()
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadSingleLineComment()
|
|
||||||
local ch = self:Next()
|
|
||||||
while ch ~= '\r' and ch ~= '\n' do
|
|
||||||
ch = self:Next()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadArray()
|
|
||||||
local result = {}
|
|
||||||
assert(self:Next() == '[')
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
local done = false
|
|
||||||
if self:Peek() == ']' then
|
|
||||||
done = true;
|
|
||||||
end
|
|
||||||
while not done do
|
|
||||||
local item = self:Read()
|
|
||||||
result[#result+1] = item
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
if self:Peek() == ']' then
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
if not done then
|
|
||||||
local ch = self:Next()
|
|
||||||
if ch ~= ',' then
|
|
||||||
error(string.format(
|
|
||||||
"Invalid array: '%s' due to: '%s'",
|
|
||||||
self:All(), ch))
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
assert(']' == self:Next())
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:ReadObject()
|
|
||||||
local result = {}
|
|
||||||
assert(self:Next() == '{')
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
local done = false
|
|
||||||
if self:Peek() == '}' then
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
while not done do
|
|
||||||
local key = self:Read()
|
|
||||||
if type(key) ~= "string" then
|
|
||||||
error(string.format(
|
|
||||||
"Invalid non-string object key: %s",
|
|
||||||
key))
|
|
||||||
end
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
local ch = self:Next()
|
|
||||||
if ch ~= ':' then
|
|
||||||
error(string.format(
|
|
||||||
"Invalid object: '%s' due to: '%s'",
|
|
||||||
self:All(),
|
|
||||||
ch))
|
|
||||||
end
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
local val = self:Read()
|
|
||||||
result[key] = val
|
|
||||||
self:SkipWhiteSpace()
|
|
||||||
if self:Peek() == '}' then
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
if not done then
|
|
||||||
ch = self:Next()
|
|
||||||
if ch ~= ',' then
|
|
||||||
error(string.format(
|
|
||||||
"Invalid array: '%s' near: '%s'",
|
|
||||||
self:All(),
|
|
||||||
ch))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert(self:Next() == "}")
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function JsonReader:SkipWhiteSpace()
|
|
||||||
local p = self:Peek()
|
|
||||||
while p ~= nil and string.find(p, "[%s/]") do
|
|
||||||
if p == '/' then
|
|
||||||
self:ReadComment()
|
|
||||||
else
|
else
|
||||||
self:Next()
|
error("Unexpected char '%s'" % char)
|
||||||
end
|
end
|
||||||
p = self:Peek()
|
end
|
||||||
|
|
||||||
|
assert(not src_err, src_err)
|
||||||
|
assert(object ~= nil, "Unexpected EOS")
|
||||||
|
|
||||||
|
self.data = object
|
||||||
|
return chunk, object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.fetch(self)
|
||||||
|
local tself, chunk, src_err = coroutine.yield()
|
||||||
|
assert(chunk or not src_err, src_err)
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.fetch_atleast(self, chunk, bytes)
|
||||||
|
while #chunk < bytes do
|
||||||
|
local nchunk = self:fetch()
|
||||||
|
assert(nchunk, "Unexpected EOS")
|
||||||
|
chunk = chunk .. nchunk
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.fetch_until(self, chunk, pattern)
|
||||||
|
local start = chunk:find(pattern)
|
||||||
|
|
||||||
|
while not start do
|
||||||
|
local nchunk = self:fetch()
|
||||||
|
assert(nchunk, "Unexpected EOS")
|
||||||
|
chunk = chunk .. nchunk
|
||||||
|
start = chunk:find(pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk, start
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_space(self, chunk)
|
||||||
|
local start = chunk:find("[^%s]")
|
||||||
|
|
||||||
|
while not start do
|
||||||
|
chunk = self:fetch()
|
||||||
|
if not chunk then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
start = chunk:find("[^%s]")
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk:sub(start)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_literal(self, chunk, literal, value)
|
||||||
|
chunk = self:fetch_atleast(chunk, #literal)
|
||||||
|
assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
|
||||||
|
return chunk:sub(#literal + 1), value
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_null(self, chunk)
|
||||||
|
return self:parse_literal(chunk, "null", null)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_true(self, chunk)
|
||||||
|
return self:parse_literal(chunk, "true", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_false(self, chunk)
|
||||||
|
return self:parse_literal(chunk, "false", false)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_number(self, chunk)
|
||||||
|
local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
|
||||||
|
local number = tonumber(chunk:sub(1, start - 1))
|
||||||
|
assert(number, "Invalid number specification")
|
||||||
|
return chunk:sub(start), number
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_string(self, chunk)
|
||||||
|
local str = ""
|
||||||
|
local object = nil
|
||||||
|
assert(chunk:sub(1, 1) == '"', 'Expected "')
|
||||||
|
chunk = chunk:sub(2)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local spos = chunk:find('[\\"]')
|
||||||
|
if spos then
|
||||||
|
str = str .. chunk:sub(1, spos - 1)
|
||||||
|
|
||||||
|
local char = chunk:sub(spos, spos)
|
||||||
|
if char == '"' then -- String end
|
||||||
|
chunk = chunk:sub(spos + 1)
|
||||||
|
break
|
||||||
|
elseif char == "\\" then -- Escape sequence
|
||||||
|
chunk, object = self:parse_escape(chunk:sub(spos))
|
||||||
|
str = str .. object
|
||||||
|
end
|
||||||
|
else
|
||||||
|
str = str .. chunk
|
||||||
|
chunk = self:fetch()
|
||||||
|
assert(chunk, "Unexpected EOS while parsing a string")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk, str
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_escape(self, chunk)
|
||||||
|
local str = ""
|
||||||
|
chunk = self:fetch_atleast(chunk:sub(2), 1)
|
||||||
|
local char = chunk:sub(1, 1)
|
||||||
|
chunk = chunk:sub(2)
|
||||||
|
|
||||||
|
if char == '"' then
|
||||||
|
return chunk, '"'
|
||||||
|
elseif char == "\\" then
|
||||||
|
return chunk, "\\"
|
||||||
|
elseif char == "/" then
|
||||||
|
return chunk, "/"
|
||||||
|
elseif char == "b" then
|
||||||
|
return chunk, "\b"
|
||||||
|
elseif char == "f" then
|
||||||
|
return chunk, "\f"
|
||||||
|
elseif char == "n" then
|
||||||
|
return chunk, "\n"
|
||||||
|
elseif char == "r" then
|
||||||
|
return chunk, "\r"
|
||||||
|
elseif char == "t" then
|
||||||
|
return chunk, "\t"
|
||||||
|
elseif char == "u" then
|
||||||
|
chunk = self:fetch_atleast(chunk, 4)
|
||||||
|
local s1, s2 = chunk:sub(1, 4):match("^([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])$")
|
||||||
|
assert(s1 and s2, "Invalid Unicode character 'U+%s%s'" % {s1, s2})
|
||||||
|
s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
|
||||||
|
|
||||||
|
-- ToDo: Unicode support
|
||||||
|
return chunk:sub(5), s1 == 0 and s2 or ""
|
||||||
|
else
|
||||||
|
error("Unexpected escaping sequence '\\%s'" % char)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonReader:Peek()
|
|
||||||
return self.reader:Peek()
|
function Decoder.parse_array(self, chunk)
|
||||||
|
chunk = chunk:sub(2)
|
||||||
|
local array = {}
|
||||||
|
|
||||||
|
local chunk, object = self:parse_delimiter(chunk, "%]")
|
||||||
|
|
||||||
|
if object then
|
||||||
|
return chunk, array
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
chunk, object = self:dispatch(chunk, nil, true)
|
||||||
|
table.insert(array, object)
|
||||||
|
|
||||||
|
chunk, object = self:parse_delimiter(chunk, ",%]")
|
||||||
|
assert(object, "Delimiter expected")
|
||||||
|
until object == "]"
|
||||||
|
|
||||||
|
return chunk, array
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonReader:Next()
|
|
||||||
return self.reader:Next()
|
function Decoder.parse_object(self, chunk)
|
||||||
|
chunk = chunk:sub(2)
|
||||||
|
local array = {}
|
||||||
|
local name
|
||||||
|
|
||||||
|
local chunk, object = self:parse_delimiter(chunk, "}")
|
||||||
|
|
||||||
|
if object then
|
||||||
|
return chunk, array
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
chunk = self:parse_space(chunk)
|
||||||
|
assert(chunk, "Unexpected EOS")
|
||||||
|
|
||||||
|
chunk, name = self:parse_string(chunk)
|
||||||
|
|
||||||
|
chunk, object = self:parse_delimiter(chunk, ":")
|
||||||
|
assert(object, "Separator expected")
|
||||||
|
|
||||||
|
chunk, object = self:dispatch(chunk, nil, true)
|
||||||
|
array[name] = object
|
||||||
|
|
||||||
|
chunk, object = self:parse_delimiter(chunk, ",}")
|
||||||
|
assert(object, "Delimiter expected")
|
||||||
|
until object == "}"
|
||||||
|
|
||||||
|
return chunk, array
|
||||||
end
|
end
|
||||||
|
|
||||||
function JsonReader:All()
|
|
||||||
return self.reader:All()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Encode(o)
|
function Decoder.parse_delimiter(self, chunk, delimiter)
|
||||||
local writer = JsonWriter:New()
|
while true do
|
||||||
writer:Write(o)
|
chunk = self:fetch_atleast(chunk, 1)
|
||||||
return writer:ToString()
|
local char = chunk:sub(1, 1)
|
||||||
end
|
if char:match("%s") then
|
||||||
|
chunk = self:parse_space(chunk)
|
||||||
function Decode(s)
|
assert(chunk, "Unexpected EOS")
|
||||||
local reader = JsonReader:New(s)
|
elseif char:match("[%s]" % delimiter) then
|
||||||
local object = reader:Read()
|
return chunk:sub(2), char
|
||||||
reader:SkipWhiteSpace()
|
else
|
||||||
assert(reader:Peek() == nil, "Invalid characters after JSON body")
|
return chunk, nil
|
||||||
return object
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
function Null()
|
|
||||||
return Null
|
|
||||||
end
|
|
|
@ -24,14 +24,26 @@ limitations under the License.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
local io = require "io"
|
||||||
|
local os = require "os"
|
||||||
|
local posix = require "posix"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
|
local luci = {}
|
||||||
|
luci.util = require "luci.util"
|
||||||
|
luci.fs = require "luci.fs"
|
||||||
|
luci.ip = require "luci.ip"
|
||||||
|
|
||||||
|
local tonumber, ipairs, pairs = tonumber, ipairs, pairs
|
||||||
|
|
||||||
|
|
||||||
--- LuCI Linux and POSIX system utilities.
|
--- LuCI Linux and POSIX system utilities.
|
||||||
module("luci.sys", package.seeall)
|
module "luci.sys"
|
||||||
require("posix")
|
|
||||||
require("luci.util")
|
|
||||||
require("luci.fs")
|
|
||||||
require("luci.ip")
|
|
||||||
|
|
||||||
--- Invoke the luci-flash executable to write an image to the flash memory.
|
--- Invoke the luci-flash executable to write an image to the flash memory.
|
||||||
|
-- @param image Local path or URL to image file
|
||||||
-- @param kpattern Pattern of files to keep over flash process
|
-- @param kpattern Pattern of files to keep over flash process
|
||||||
-- @return Return value of os.execute()
|
-- @return Return value of os.execute()
|
||||||
function flash(image, kpattern)
|
function flash(image, kpattern)
|
||||||
|
|
|
@ -23,11 +23,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
local uci = require("uci")
|
local uci = require "uci"
|
||||||
local util = require("luci.util")
|
local util = require "luci.util"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||||||
local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
|
local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
|
||||||
local table = table
|
local require = require
|
||||||
|
|
||||||
--- LuCI UCI model library.
|
--- LuCI UCI model library.
|
||||||
module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end)
|
module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end)
|
||||||
|
@ -37,6 +39,14 @@ confdir_default = "/etc/config"
|
||||||
|
|
||||||
savedir_state = "/var/state"
|
savedir_state = "/var/state"
|
||||||
|
|
||||||
|
|
||||||
|
--- Applies the new config
|
||||||
|
-- @param config UCI config
|
||||||
|
function apply(config)
|
||||||
|
local conf = require "luci.config"
|
||||||
|
return conf.uci_oncommit[config] and os.execute(conf.uci_oncommit[config])
|
||||||
|
end
|
||||||
|
|
||||||
--- Delete all sections of a given type that match certain criteria.
|
--- Delete all sections of a given type that match certain criteria.
|
||||||
-- @param config UCI config
|
-- @param config UCI config
|
||||||
-- @param type UCI section type
|
-- @param type UCI section type
|
||||||
|
@ -149,7 +159,6 @@ function get_list(config, section, option)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set given values as list.
|
--- Set given values as list.
|
||||||
-- Warning: This function is unsave! You should use save_config or save_state if possible.
|
|
||||||
-- @param config UCI config
|
-- @param config UCI config
|
||||||
-- @param section UCI section name
|
-- @param section UCI section name
|
||||||
-- @param option UCI option
|
-- @param option UCI option
|
||||||
|
@ -244,7 +253,6 @@ end
|
||||||
-- @see unload
|
-- @see unload
|
||||||
|
|
||||||
--- Set a value or create a named section.
|
--- Set a value or create a named section.
|
||||||
-- Warning: This function is unsave! You should use save_config or save_state if possible.
|
|
||||||
-- @class function
|
-- @class function
|
||||||
-- @name set
|
-- @name set
|
||||||
-- @param config UCI config
|
-- @param config UCI config
|
||||||
|
|
|
@ -187,13 +187,15 @@ function dispatch(request)
|
||||||
|
|
||||||
if not luci.util.contains(accs, user) then
|
if not luci.util.contains(accs, user) then
|
||||||
if authen then
|
if authen then
|
||||||
local user = authen(luci.sys.user.checkpasswd, accs, def)
|
local user, sess = authen(luci.sys.user.checkpasswd, accs, def)
|
||||||
if not user or not luci.util.contains(accs, user) then
|
if not user or not luci.util.contains(accs, user) then
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
local sid = luci.sys.uniqueid(16)
|
local sid = sess or luci.sys.uniqueid(16)
|
||||||
luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
||||||
luci.sauth.write(sid, user)
|
if not sess then
|
||||||
|
luci.sauth.write(sid, user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
luci.http.status(403, "Forbidden")
|
luci.http.status(403, "Forbidden")
|
||||||
|
|
|
@ -12,15 +12,20 @@ You may obtain a copy of the License at
|
||||||
|
|
||||||
$Id$
|
$Id$
|
||||||
]]--
|
]]--
|
||||||
module("luci.controller.rpc", package.seeall)
|
|
||||||
|
local require = require
|
||||||
|
local pairs = pairs
|
||||||
|
local print = print
|
||||||
|
|
||||||
|
module "luci.controller.rpc"
|
||||||
|
|
||||||
function index()
|
function index()
|
||||||
local function authenticator(validator, accs)
|
local function authenticator(validator, accs)
|
||||||
local args = luci.dispatcher.context.args
|
local auth = luci.http.formvalue("auth", true)
|
||||||
if args and #args > 0 then
|
if auth then
|
||||||
local user = luci.sauth.read(args[1])
|
local user = luci.sauth.read(auth)
|
||||||
if user and luci.util.contains(accs, user) then
|
if user and luci.util.contains(accs, user) then
|
||||||
return user
|
return user, auth
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
luci.http.status(403, "Forbidden")
|
luci.http.status(403, "Forbidden")
|
||||||
|
@ -29,16 +34,25 @@ function index()
|
||||||
uci = entry({"rpc", "uci"}, call("rpc_uci"))
|
uci = entry({"rpc", "uci"}, call("rpc_uci"))
|
||||||
uci.sysauth = "root"
|
uci.sysauth = "root"
|
||||||
uci.sysauth_authenticator = authenticator
|
uci.sysauth_authenticator = authenticator
|
||||||
uci.leaf = true
|
|
||||||
|
fs = entry({"rpc", "fs"}, call("rpc_fs"))
|
||||||
|
fs.sysauth = "root"
|
||||||
|
fs.sysauth_authenticator = authenticator
|
||||||
|
|
||||||
|
fs = entry({"rpc", "sys"}, call("rpc_sys"))
|
||||||
|
fs.sysauth = "root"
|
||||||
|
fs.sysauth_authenticator = authenticator
|
||||||
|
|
||||||
uci = entry({"rpc", "auth"}, call("rpc_auth"))
|
uci = entry({"rpc", "auth"}, call("rpc_auth"))
|
||||||
end
|
end
|
||||||
|
|
||||||
function rpc_auth()
|
function rpc_auth()
|
||||||
require "luci.jsonrpc"
|
local jsonrpc = require "luci.jsonrpc"
|
||||||
require "luci.sauth"
|
local sauth = require "luci.sauth"
|
||||||
|
local http = require "luci.http"
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
|
||||||
luci.http.setfilehandler()
|
http.setfilehandler()
|
||||||
|
|
||||||
local loginstat
|
local loginstat
|
||||||
|
|
||||||
|
@ -46,21 +60,45 @@ function rpc_auth()
|
||||||
server.login = function(user, pass)
|
server.login = function(user, pass)
|
||||||
local sid
|
local sid
|
||||||
|
|
||||||
if luci.sys.user.checkpasswd(user, pass) then
|
if sys.user.checkpasswd(user, pass) then
|
||||||
sid = luci.sys.uniqueid(16)
|
sid = sys.uniqueid(16)
|
||||||
luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
||||||
luci.sauth.write(sid, user)
|
sauth.write(sid, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
return sid
|
return sid
|
||||||
end
|
end
|
||||||
|
|
||||||
luci.http.prepare_content("application/json")
|
http.prepare_content("application/json")
|
||||||
luci.http.write(luci.jsonrpc.handle(server, luci.http.content()))
|
http.write(jsonrpc.handle(server, http.content()))
|
||||||
|
|
||||||
return loginstat
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function rpc_uci()
|
function rpc_uci()
|
||||||
|
local uci = require "luci.controller.rpc.uci"
|
||||||
|
local jsonrpc = require "luci.jsonrpc"
|
||||||
|
local http = require "luci.http"
|
||||||
|
|
||||||
|
http.setfilehandler()
|
||||||
|
http.prepare_content("application/json")
|
||||||
|
http.write(jsonrpc.handle(uci, http.content()))
|
||||||
|
end
|
||||||
|
|
||||||
|
function rpc_fs()
|
||||||
|
local fs = require "luci.fs"
|
||||||
|
local jsonrpc = require "luci.jsonrpc"
|
||||||
|
local http = require "luci.http"
|
||||||
|
|
||||||
|
http.setfilehandler()
|
||||||
|
http.prepare_content("application/json")
|
||||||
|
http.write(jsonrpc.handle(fs, http.content()))
|
||||||
|
end
|
||||||
|
|
||||||
|
function rpc_sys()
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local jsonrpc = require "luci.jsonrpc"
|
||||||
|
local http = require "luci.http"
|
||||||
|
|
||||||
|
http.setfilehandler()
|
||||||
|
http.prepare_content("application/json")
|
||||||
|
http.write(jsonrpc.handle(sys, http.content()))
|
||||||
end
|
end
|
93
modules/rpc/luasrc/controller/rpc/uci.lua
Normal file
93
modules/rpc/luasrc/controller/rpc/uci.lua
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local uci = require "luci.model.uci"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
|
|
||||||
|
module "luci.controller.rpc.uci"
|
||||||
|
_M, _PACKAGE, _NAME = nil, nil, nil
|
||||||
|
|
||||||
|
function add(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
local stat = uci.add(config, ...)
|
||||||
|
return uci.save_config(config) and stat
|
||||||
|
end
|
||||||
|
|
||||||
|
function apply(config)
|
||||||
|
return uci.apply(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function changes(...)
|
||||||
|
return uci.changes(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function commit(config)
|
||||||
|
return uci.load(config) and uci.commit(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function delete(config, ...)
|
||||||
|
uci.load(config)
|
||||||
|
return uci.delete(config, ...) and uci.save(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function delete_all(config, ...)
|
||||||
|
uci.load(config)
|
||||||
|
return uci.delete_all(config, ...) and uci.save(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function foreach(config, stype)
|
||||||
|
uci.load_config(config)
|
||||||
|
local sections = {}
|
||||||
|
|
||||||
|
return uci.foreach(config, stype, function(section)
|
||||||
|
table.insert(sections, section)
|
||||||
|
end) and sections
|
||||||
|
end
|
||||||
|
|
||||||
|
function get(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
return uci.get(config, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_all(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
return uci.get_all(config, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_state(config, ...)
|
||||||
|
uci.load_state(config)
|
||||||
|
return uci.get(config, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function revert(config)
|
||||||
|
return uci.load(config) and uci.revert(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function section(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
return uci.section(config, ...) and uci.save_config(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function set(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
return uci.set(config, ...) and uci.save_config(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tset(config, ...)
|
||||||
|
uci.load_config(config)
|
||||||
|
return uci.tset(config, ...) and uci.save_config(config)
|
||||||
|
end
|
||||||
|
|
|
@ -23,12 +23,12 @@ function resolve(mod, method)
|
||||||
if not type(mod) == "table" then
|
if not type(mod) == "table" then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
mod = mod[path[j]]
|
mod = rawget(mod, path[j])
|
||||||
if not mod then
|
if not mod then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
mod = type(mod) == "table" and mod[path[#path]] or nil
|
mod = type(mod) == "table" and rawget(mod, path[#path]) or nil
|
||||||
if type(mod) == "function" then
|
if type(mod) == "function" then
|
||||||
return mod
|
return mod
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue