mirror of
https://github.com/JamesonHuang/OpenWrt_Luci_Lua.git
synced 2024-11-23 13:50:11 +00:00
adjust directory 2
This commit is contained in:
parent
b09950bf62
commit
4039994946
113
1_1.mi_Lua/Posix.lua
Normal file
113
1_1.mi_Lua/Posix.lua
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
local base = _G
|
||||||
|
local string = require("string")
|
||||||
|
local M = require "posix"
|
||||||
|
|
||||||
|
function M.timeradd (x,y)
|
||||||
|
local sec, usec = 0, 0
|
||||||
|
if x.sec then sec = sec + x.sec end
|
||||||
|
if y.sec then sec = sec + y.sec end
|
||||||
|
if x.usec then usec = usec + x.usec end
|
||||||
|
if y.usec then usec = usec + y.usec end
|
||||||
|
if usec > 1000000 then
|
||||||
|
sec = sec + 1
|
||||||
|
usec = usec - 1000000
|
||||||
|
end
|
||||||
|
|
||||||
|
return { sec = sec, usec = usec }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.timercmp (x, y)
|
||||||
|
local x = { sec = x.sec or 0, usec = x.usec or 0 }
|
||||||
|
local y = { sec = y.sec or 0, usec = y.usec or 0 }
|
||||||
|
if x.sec ~= y.sec then
|
||||||
|
return x.sec - y.sec
|
||||||
|
else
|
||||||
|
return x.usec - y.usec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.timersub (x,y)
|
||||||
|
local sec, usec = 0, 0
|
||||||
|
if x.sec then sec = x.sec end
|
||||||
|
if y.sec then sec = sec - y.sec end
|
||||||
|
if x.usec then usec = x.usec end
|
||||||
|
if y.usec then usec = usec - y.usec end
|
||||||
|
if usec < 0 then
|
||||||
|
sec = sec - 1
|
||||||
|
usec = usec + 1000000
|
||||||
|
end
|
||||||
|
|
||||||
|
return { sec = sec, usec = usec }
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.timesleep (x)
|
||||||
|
local sec, nsec = 0, 0
|
||||||
|
y = M.gettimeofday();
|
||||||
|
if( M.timercmp(x, y) > 0 ) then
|
||||||
|
sec = x.sec - y.sec
|
||||||
|
nsec = (x.usec - y.usec) * 1000
|
||||||
|
if nsec < 0 then
|
||||||
|
sec = sec - 1
|
||||||
|
nsec = nsec + 1000000000
|
||||||
|
end
|
||||||
|
M.nanosleep(sec, nsec)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.strsplit(str, delim, maxNb)
|
||||||
|
-- Eliminate bad cases...
|
||||||
|
if string.find(str, delim) == nil then
|
||||||
|
return { str }
|
||||||
|
end
|
||||||
|
if maxNb == nil or maxNb < 1 then
|
||||||
|
maxNb = 0 -- No limit
|
||||||
|
end
|
||||||
|
local result = {}
|
||||||
|
local pat = "(.-)" .. delim .. "()"
|
||||||
|
local nb = 0
|
||||||
|
local lastPos
|
||||||
|
for part, pos in string.gfind(str, pat) do
|
||||||
|
nb = nb + 1
|
||||||
|
result[nb] = part
|
||||||
|
lastPos = pos
|
||||||
|
if nb == maxNb then break end
|
||||||
|
end
|
||||||
|
-- Handle the last field
|
||||||
|
if nb ~= maxNb then
|
||||||
|
result[nb + 1] = string.sub(str, lastPos)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.var_dump(data, max_level, prefix)
|
||||||
|
if type(prefix) ~= "string" then
|
||||||
|
prefix = ""
|
||||||
|
end
|
||||||
|
if type(data) ~= "table" then
|
||||||
|
print(prefix .. tostring(data))
|
||||||
|
else
|
||||||
|
print(data)
|
||||||
|
if max_level ~= 0 then
|
||||||
|
local prefix_next = prefix .. " "
|
||||||
|
print(prefix .. "{")
|
||||||
|
for k,v in pairs(data) do
|
||||||
|
io.stdout:write(prefix_next .. k .. " = ")
|
||||||
|
if type(v) ~= "table" or (type(max_level) == "number" and max_level <= 1) then
|
||||||
|
print(v)
|
||||||
|
else
|
||||||
|
if max_level == nil then
|
||||||
|
M.var_dump(v, nil, prefix_next)
|
||||||
|
else
|
||||||
|
M.var_dump(v, max_level - 1, prefix_next)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(prefix .. "}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
15
1_1.mi_Lua/bit.lua
Normal file
15
1_1.mi_Lua/bit.lua
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
--[[
|
||||||
|
nixio - Linux I/O library for lua
|
||||||
|
|
||||||
|
Copyright 2009 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
return require "nixio".bit
|
BIN
1_1.mi_Lua/cjson.so
Normal file
BIN
1_1.mi_Lua/cjson.so
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/iproute.so
Normal file
BIN
1_1.mi_Lua/iproute.so
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/iwinfo.so
Normal file
BIN
1_1.mi_Lua/iwinfo.so
Normal file
Binary file not shown.
376
1_1.mi_Lua/json.lua
Normal file
376
1_1.mi_Lua/json.lua
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- JSON4Lua: JSON encoding / decoding support for the Lua language.
|
||||||
|
-- json Module.
|
||||||
|
-- Author: Craig Mason-Jones
|
||||||
|
-- Homepage: http://json.luaforge.net/
|
||||||
|
-- Version: 0.9.40
|
||||||
|
-- This module is released under the MIT License (MIT).
|
||||||
|
-- Please see LICENCE.txt for details.
|
||||||
|
--
|
||||||
|
-- USAGE:
|
||||||
|
-- This module exposes two functions:
|
||||||
|
-- encode(o)
|
||||||
|
-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string.
|
||||||
|
-- decode(json_string)
|
||||||
|
-- Returns a Lua object populated with the data encoded in the JSON string json_string.
|
||||||
|
--
|
||||||
|
-- REQUIREMENTS:
|
||||||
|
-- compat-5.1 if using Lua 5.0
|
||||||
|
--
|
||||||
|
-- CHANGELOG
|
||||||
|
-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix).
|
||||||
|
-- Fixed Lua 5.1 compatibility issues.
|
||||||
|
-- Introduced json.null to have null values in associative arrays.
|
||||||
|
-- encode() performance improvement (more than 50%) through table.concat rather than ..
|
||||||
|
-- Introduced decode ability to ignore /**/ comments in the JSON string.
|
||||||
|
-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Imports and dependencies
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
local math = require('math')
|
||||||
|
local string = require("string")
|
||||||
|
local table = require("table")
|
||||||
|
|
||||||
|
local base = _G
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Module declaration
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
module("json")
|
||||||
|
|
||||||
|
-- Public functions
|
||||||
|
|
||||||
|
-- Private functions
|
||||||
|
local decode_scanArray
|
||||||
|
local decode_scanComment
|
||||||
|
local decode_scanConstant
|
||||||
|
local decode_scanNumber
|
||||||
|
local decode_scanObject
|
||||||
|
local decode_scanString
|
||||||
|
local decode_scanWhitespace
|
||||||
|
local encodeString
|
||||||
|
local isArray
|
||||||
|
local isEncodable
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- PUBLIC FUNCTIONS
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
--- Encodes an arbitrary Lua object / variable.
|
||||||
|
-- @param v The Lua object / variable to be JSON encoded.
|
||||||
|
-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)
|
||||||
|
function encode (v)
|
||||||
|
-- Handle nil values
|
||||||
|
if v==nil then
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
local vtype = base.type(v)
|
||||||
|
|
||||||
|
-- Handle strings
|
||||||
|
if vtype=='string' then
|
||||||
|
return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle booleans
|
||||||
|
if vtype=='number' or vtype=='boolean' then
|
||||||
|
return base.tostring(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle tables
|
||||||
|
if vtype=='table' then
|
||||||
|
local rval = {}
|
||||||
|
-- Consider arrays separately
|
||||||
|
local bArray, maxCount = isArray(v)
|
||||||
|
if bArray then
|
||||||
|
for i = 1,maxCount do
|
||||||
|
table.insert(rval, encode(v[i]))
|
||||||
|
end
|
||||||
|
else -- An object, not an array
|
||||||
|
for i,j in base.pairs(v) do
|
||||||
|
if isEncodable(i) and isEncodable(j) then
|
||||||
|
table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if bArray then
|
||||||
|
return '[' .. table.concat(rval,',') ..']'
|
||||||
|
else
|
||||||
|
return '{' .. table.concat(rval,',') .. '}'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle null values
|
||||||
|
if vtype=='function' and v==null then
|
||||||
|
return 'null'
|
||||||
|
end
|
||||||
|
|
||||||
|
base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Decodes a JSON string and returns the decoded value as a Lua data structure / value.
|
||||||
|
-- @param s The string to scan.
|
||||||
|
-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.
|
||||||
|
-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,
|
||||||
|
-- and the position of the first character after
|
||||||
|
-- the scanned JSON object.
|
||||||
|
function decode(s, startPos)
|
||||||
|
startPos = startPos and startPos or 1
|
||||||
|
startPos = decode_scanWhitespace(s,startPos)
|
||||||
|
base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
|
||||||
|
local curChar = string.sub(s,startPos,startPos)
|
||||||
|
-- Object
|
||||||
|
if curChar=='{' then
|
||||||
|
return decode_scanObject(s,startPos)
|
||||||
|
end
|
||||||
|
-- Array
|
||||||
|
if curChar=='[' then
|
||||||
|
return decode_scanArray(s,startPos)
|
||||||
|
end
|
||||||
|
-- Number
|
||||||
|
if string.find("+-0123456789.e", curChar, 1, true) then
|
||||||
|
return decode_scanNumber(s,startPos)
|
||||||
|
end
|
||||||
|
-- String
|
||||||
|
if curChar==[["]] or curChar==[[']] then
|
||||||
|
return decode_scanString(s,startPos)
|
||||||
|
end
|
||||||
|
if string.sub(s,startPos,startPos+1)=='/*' then
|
||||||
|
return decode(s, decode_scanComment(s,startPos))
|
||||||
|
end
|
||||||
|
-- Otherwise, it must be a constant
|
||||||
|
return decode_scanConstant(s,startPos)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- The null function allows one to specify a null value in an associative array (which is otherwise
|
||||||
|
-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
|
||||||
|
function null()
|
||||||
|
return null -- so json.null() will also return null ;-)
|
||||||
|
end
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Internal, PRIVATE functions.
|
||||||
|
-- Following a Python-like convention, I have prefixed all these 'PRIVATE'
|
||||||
|
-- functions with an underscore.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Scans an array from JSON into a Lua object
|
||||||
|
-- startPos begins at the start of the array.
|
||||||
|
-- Returns the array and the next starting position
|
||||||
|
-- @param s The string being scanned.
|
||||||
|
-- @param startPos The starting position for the scan.
|
||||||
|
-- @return table, int The scanned array as a table, and the position of the next character to scan.
|
||||||
|
function decode_scanArray(s,startPos)
|
||||||
|
local array = {} -- The return value
|
||||||
|
local stringLen = string.len(s)
|
||||||
|
base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
|
||||||
|
startPos = startPos + 1
|
||||||
|
-- Infinite loop for array elements
|
||||||
|
repeat
|
||||||
|
startPos = decode_scanWhitespace(s,startPos)
|
||||||
|
base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
|
||||||
|
local curChar = string.sub(s,startPos,startPos)
|
||||||
|
if (curChar==']') then
|
||||||
|
return array, startPos+1
|
||||||
|
end
|
||||||
|
if (curChar==',') then
|
||||||
|
startPos = decode_scanWhitespace(s,startPos+1)
|
||||||
|
end
|
||||||
|
base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
|
||||||
|
object, startPos = decode(s,startPos)
|
||||||
|
table.insert(array,object)
|
||||||
|
until false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans a comment and discards the comment.
|
||||||
|
-- Returns the position of the next character following the comment.
|
||||||
|
-- @param string s The JSON string to scan.
|
||||||
|
-- @param int startPos The starting position of the comment
|
||||||
|
function decode_scanComment(s, startPos)
|
||||||
|
base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
|
||||||
|
local endPos = string.find(s,'*/',startPos+2)
|
||||||
|
base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
|
||||||
|
return endPos+2
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans for given constants: true, false or null
|
||||||
|
-- Returns the appropriate Lua type, and the position of the next character to read.
|
||||||
|
-- @param s The string being scanned.
|
||||||
|
-- @param startPos The position in the string at which to start scanning.
|
||||||
|
-- @return object, int The object (true, false or nil) and the position at which the next character should be
|
||||||
|
-- scanned.
|
||||||
|
function decode_scanConstant(s, startPos)
|
||||||
|
local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
|
||||||
|
local constNames = {"true","false","null"}
|
||||||
|
|
||||||
|
for i,k in base.pairs(constNames) do
|
||||||
|
--print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
|
||||||
|
if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
|
||||||
|
return consts[k], startPos + string.len(k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans a number from the JSON encoded string.
|
||||||
|
-- (in fact, also is able to scan numeric +- eqns, which is not
|
||||||
|
-- in the JSON spec.)
|
||||||
|
-- Returns the number, and the position of the next character
|
||||||
|
-- after the number.
|
||||||
|
-- @param s The string being scanned.
|
||||||
|
-- @param startPos The position at which to start scanning.
|
||||||
|
-- @return number, int The extracted number and the position of the next character to scan.
|
||||||
|
function decode_scanNumber(s,startPos)
|
||||||
|
local endPos = startPos+1
|
||||||
|
local stringLen = string.len(s)
|
||||||
|
local acceptableChars = "+-0123456789.e"
|
||||||
|
while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
|
||||||
|
and endPos<=stringLen
|
||||||
|
) do
|
||||||
|
endPos = endPos + 1
|
||||||
|
end
|
||||||
|
local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
|
||||||
|
local stringEval = base.loadstring(stringValue)
|
||||||
|
base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
|
||||||
|
return stringEval(), endPos
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans a JSON object into a Lua object.
|
||||||
|
-- startPos begins at the start of the object.
|
||||||
|
-- Returns the object and the next starting position.
|
||||||
|
-- @param s The string being scanned.
|
||||||
|
-- @param startPos The starting position of the scan.
|
||||||
|
-- @return table, int The scanned object as a table and the position of the next character to scan.
|
||||||
|
function decode_scanObject(s,startPos)
|
||||||
|
local object = {}
|
||||||
|
local stringLen = string.len(s)
|
||||||
|
local key, value
|
||||||
|
base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
|
||||||
|
startPos = startPos + 1
|
||||||
|
repeat
|
||||||
|
startPos = decode_scanWhitespace(s,startPos)
|
||||||
|
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
|
||||||
|
local curChar = string.sub(s,startPos,startPos)
|
||||||
|
if (curChar=='}') then
|
||||||
|
return object,startPos+1
|
||||||
|
end
|
||||||
|
if (curChar==',') then
|
||||||
|
startPos = decode_scanWhitespace(s,startPos+1)
|
||||||
|
end
|
||||||
|
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
|
||||||
|
-- Scan the key
|
||||||
|
key, startPos = decode(s,startPos)
|
||||||
|
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||||
|
startPos = decode_scanWhitespace(s,startPos)
|
||||||
|
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||||
|
base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
|
||||||
|
startPos = decode_scanWhitespace(s,startPos+1)
|
||||||
|
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||||
|
value, startPos = decode(s,startPos)
|
||||||
|
object[key]=value
|
||||||
|
until false -- infinite loop while key-value pairs are found
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans a JSON string from the opening inverted comma or single quote to the
|
||||||
|
-- end of the string.
|
||||||
|
-- Returns the string extracted as a Lua string,
|
||||||
|
-- and the position of the next non-string character
|
||||||
|
-- (after the closing inverted comma or single quote).
|
||||||
|
-- @param s The string being scanned.
|
||||||
|
-- @param startPos The starting position of the scan.
|
||||||
|
-- @return string, int The extracted string as a Lua string, and the next character to parse.
|
||||||
|
function decode_scanString(s,startPos)
|
||||||
|
base.assert(startPos, 'decode_scanString(..) called without start position')
|
||||||
|
local startChar = string.sub(s,startPos,startPos)
|
||||||
|
base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
|
||||||
|
local escaped = false
|
||||||
|
local endPos = startPos + 1
|
||||||
|
local bEnded = false
|
||||||
|
local stringLen = string.len(s)
|
||||||
|
repeat
|
||||||
|
local curChar = string.sub(s,endPos,endPos)
|
||||||
|
-- Character escaping is only used to escape the string delimiters
|
||||||
|
if not escaped then
|
||||||
|
if curChar==[[\]] then
|
||||||
|
escaped = true
|
||||||
|
else
|
||||||
|
bEnded = curChar==startChar
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- If we're escaped, we accept the current character come what may
|
||||||
|
escaped = false
|
||||||
|
end
|
||||||
|
endPos = endPos + 1
|
||||||
|
base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
|
||||||
|
until bEnded
|
||||||
|
local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
|
||||||
|
local stringEval = base.loadstring(stringValue)
|
||||||
|
base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
|
||||||
|
return stringEval(), endPos
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scans a JSON string skipping all whitespace from the current start position.
|
||||||
|
-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached.
|
||||||
|
-- @param s The string being scanned
|
||||||
|
-- @param startPos The starting position where we should begin removing whitespace.
|
||||||
|
-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string
|
||||||
|
-- was reached.
|
||||||
|
function decode_scanWhitespace(s,startPos)
|
||||||
|
local whitespace=" \n\r\t"
|
||||||
|
local stringLen = string.len(s)
|
||||||
|
while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do
|
||||||
|
startPos = startPos + 1
|
||||||
|
end
|
||||||
|
return startPos
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Encodes a string to be JSON-compatible.
|
||||||
|
-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-)
|
||||||
|
-- @param s The string to return as a JSON encoded (i.e. backquoted string)
|
||||||
|
-- @return The string appropriately escaped.
|
||||||
|
function encodeString(s)
|
||||||
|
s = string.gsub(s,'\\','\\\\')
|
||||||
|
s = string.gsub(s,'"','\\"')
|
||||||
|
s = string.gsub(s,"'","\\'")
|
||||||
|
s = string.gsub(s,'\n','\\n')
|
||||||
|
s = string.gsub(s,'\t','\\t')
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Determines whether the given Lua type is an array or a table / dictionary.
|
||||||
|
-- We consider any table an array if it has indexes 1..n for its n items, and no
|
||||||
|
-- other data in the table.
|
||||||
|
-- I think this method is currently a little 'flaky', but can't think of a good way around it yet...
|
||||||
|
-- @param t The table to evaluate as an array
|
||||||
|
-- @return boolean, number True if the table can be represented as an array, false otherwise. If true,
|
||||||
|
-- the second returned value is the maximum
|
||||||
|
-- number of indexed elements in the array.
|
||||||
|
function isArray(t)
|
||||||
|
-- Next we count all the elements, ensuring that any non-indexed elements are not-encodable
|
||||||
|
-- (with the possible exception of 'n')
|
||||||
|
local maxIndex = 0
|
||||||
|
for k,v in base.pairs(t) do
|
||||||
|
if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair
|
||||||
|
if (not isEncodable(v)) then return false end -- All array elements must be encodable
|
||||||
|
maxIndex = math.max(maxIndex,k)
|
||||||
|
else
|
||||||
|
if (k=='n') then
|
||||||
|
if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements
|
||||||
|
else -- Else of (k=='n')
|
||||||
|
if isEncodable(v) then return false end
|
||||||
|
end -- End of (k~='n')
|
||||||
|
end -- End of k,v not an indexed pair
|
||||||
|
end -- End of loop across all pairs
|
||||||
|
return true, maxIndex
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determines whether the given Lua object / table / variable can be JSON encoded. The only
|
||||||
|
-- types that are JSON encodable are: string, boolean, number, nil, table and json.null.
|
||||||
|
-- In this implementation, all other types are ignored.
|
||||||
|
-- @param o The object to examine.
|
||||||
|
-- @return boolean True if the object should be JSON encoded, false if it should be ignored.
|
||||||
|
function isEncodable(o)
|
||||||
|
local t = base.type(o)
|
||||||
|
return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)
|
||||||
|
end
|
201
1_1.mi_Lua/logging.lua
Normal file
201
1_1.mi_Lua/logging.lua
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- includes a new tostring function that handles tables recursively
|
||||||
|
--
|
||||||
|
-- @author Danilo Tuler (tuler@ideais.com.br)
|
||||||
|
-- @author Andre Carregal (info@keplerproject.org)
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local type, table, string, _tostring, tonumber = type, table, string, tostring, tonumber
|
||||||
|
local select = select
|
||||||
|
local error = error
|
||||||
|
local format = string.format
|
||||||
|
local pairs = pairs
|
||||||
|
local ipairs = ipairs
|
||||||
|
|
||||||
|
local logging = {
|
||||||
|
|
||||||
|
-- Meta information
|
||||||
|
_COPYRIGHT = "Copyright (C) 2004-2013 Kepler Project",
|
||||||
|
_DESCRIPTION = "A simple API to use logging features in Lua",
|
||||||
|
_VERSION = "LuaLogging 1.3.0",
|
||||||
|
|
||||||
|
-- The DEBUG Level designates fine-grained instring.formational events that are most
|
||||||
|
-- useful to debug an application
|
||||||
|
DEBUG = "DEBUG",
|
||||||
|
|
||||||
|
-- The INFO level designates instring.formational messages that highlight the
|
||||||
|
-- progress of the application at coarse-grained level
|
||||||
|
INFO = "INFO",
|
||||||
|
|
||||||
|
-- The WARN level designates potentially harmful situations
|
||||||
|
WARN = "WARN",
|
||||||
|
|
||||||
|
-- The ERROR level designates error events that might still allow the
|
||||||
|
-- application to continue running
|
||||||
|
ERROR = "ERROR",
|
||||||
|
|
||||||
|
-- The FATAL level designates very severe error events that will presumably
|
||||||
|
-- lead the application to abort
|
||||||
|
FATAL = "FATAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
local LEVEL = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
|
||||||
|
local MAX_LEVELS = #LEVEL
|
||||||
|
-- make level names to order
|
||||||
|
for i=1,MAX_LEVELS do
|
||||||
|
LEVEL[LEVEL[i]] = i
|
||||||
|
end
|
||||||
|
|
||||||
|
-- private log function, with support for formating a complex log message.
|
||||||
|
local function LOG_MSG(self, level, fmt, ...)
|
||||||
|
local f_type = type(fmt)
|
||||||
|
if f_type == 'string' then
|
||||||
|
if select('#', ...) > 0 then
|
||||||
|
return self:append(level, format(fmt, ...))
|
||||||
|
else
|
||||||
|
-- only a single string, no formating needed.
|
||||||
|
return self:append(level, fmt)
|
||||||
|
end
|
||||||
|
elseif f_type == 'function' then
|
||||||
|
-- fmt should be a callable function which returns the message to log
|
||||||
|
return self:append(level, fmt(...))
|
||||||
|
end
|
||||||
|
-- fmt is not a string and not a function, just call tostring() on it.
|
||||||
|
return self:append(level, logging.tostring(fmt))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create the proxy functions for each log level.
|
||||||
|
local LEVEL_FUNCS = {}
|
||||||
|
for i=1,MAX_LEVELS do
|
||||||
|
local level = LEVEL[i]
|
||||||
|
LEVEL_FUNCS[i] = function(self, ...)
|
||||||
|
-- no level checking needed here, this function will only be called if it's level is active.
|
||||||
|
return LOG_MSG(self, level, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- do nothing function for disabled levels.
|
||||||
|
local function disable_level() end
|
||||||
|
|
||||||
|
-- improved assertion function.
|
||||||
|
local function assert(exp, ...)
|
||||||
|
-- if exp is true, we are finished so don't do any processing of the parameters
|
||||||
|
if exp then return exp, ... end
|
||||||
|
-- assertion failed, raise error
|
||||||
|
error(format(...), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Creates a new logger object
|
||||||
|
-- @param append Function used by the logger to append a message with a
|
||||||
|
-- log-level to the log stream.
|
||||||
|
-- @return Table representing the new logger object.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
function logging.new(append)
|
||||||
|
if type(append) ~= "function" then
|
||||||
|
return nil, "Appender must be a function."
|
||||||
|
end
|
||||||
|
|
||||||
|
local logger = {}
|
||||||
|
logger.append = append
|
||||||
|
|
||||||
|
logger.setLevel = function (self, level)
|
||||||
|
local order = LEVEL[level]
|
||||||
|
assert(order, "undefined level `%s'", _tostring(level))
|
||||||
|
if self.level then
|
||||||
|
self:log(logging.WARN, "Logger: changing loglevel from %s to %s", self.level, level)
|
||||||
|
end
|
||||||
|
self.level = level
|
||||||
|
self.level_order = order
|
||||||
|
-- enable/disable levels
|
||||||
|
for i=1,MAX_LEVELS do
|
||||||
|
local name = LEVEL[i]:lower()
|
||||||
|
if i >= order then
|
||||||
|
self[name] = LEVEL_FUNCS[i]
|
||||||
|
else
|
||||||
|
self[name] = disable_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generic log function.
|
||||||
|
logger.log = function (self, level, ...)
|
||||||
|
local order = LEVEL[level]
|
||||||
|
assert(order, "undefined level `%s'", _tostring(level))
|
||||||
|
if order < self.level_order then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return LOG_MSG(self, level, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initialize log level.
|
||||||
|
logger:setLevel(logging.DEBUG)
|
||||||
|
return logger
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Prepares the log message
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
function logging.prepareLogMsg(pattern, dt, level, message)
|
||||||
|
local logMsg = pattern or "%date %level %message\n"
|
||||||
|
message = string.gsub(message, "%%", "%%%%")
|
||||||
|
logMsg = string.gsub(logMsg, "%%date", dt)
|
||||||
|
logMsg = string.gsub(logMsg, "%%level", level)
|
||||||
|
logMsg = string.gsub(logMsg, "%%message", message)
|
||||||
|
return logMsg
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Converts a Lua value to a string
|
||||||
|
--
|
||||||
|
-- Converts Table fields in alphabetical order
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
local function tostring(value)
|
||||||
|
local str = ''
|
||||||
|
|
||||||
|
if (type(value) ~= 'table') then
|
||||||
|
if (type(value) == 'string') then
|
||||||
|
str = string.format("%q", value)
|
||||||
|
else
|
||||||
|
str = _tostring(value)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local auxTable = {}
|
||||||
|
for key in pairs(value) do
|
||||||
|
if (tonumber(key) ~= key) then
|
||||||
|
table.insert(auxTable, key)
|
||||||
|
else
|
||||||
|
table.insert(auxTable, tostring(key))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(auxTable)
|
||||||
|
|
||||||
|
str = str..'{'
|
||||||
|
local separator = ""
|
||||||
|
local entry = ""
|
||||||
|
for _, fieldName in ipairs(auxTable) do
|
||||||
|
if ((tonumber(fieldName)) and (tonumber(fieldName) > 0)) then
|
||||||
|
entry = tostring(value[tonumber(fieldName)])
|
||||||
|
else
|
||||||
|
entry = fieldName.." = "..tostring(value[fieldName])
|
||||||
|
end
|
||||||
|
str = str..separator..entry
|
||||||
|
separator = ", "
|
||||||
|
end
|
||||||
|
str = str..'}'
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
logging.tostring = tostring
|
||||||
|
|
||||||
|
if _VERSION ~= 'Lua 5.2' then
|
||||||
|
-- still create 'logging' global for Lua versions < 5.2
|
||||||
|
_G.logging = logging
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging
|
20
1_1.mi_Lua/logging/console.lua
Normal file
20
1_1.mi_Lua/logging/console.lua
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Prints logging information to console
|
||||||
|
--
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
--
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
|
||||||
|
function logging.console(logPattern)
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
io.stdout:write(logging.prepareLogMsg(logPattern, os.date(), level, message))
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.console
|
||||||
|
|
43
1_1.mi_Lua/logging/email.lua
Normal file
43
1_1.mi_Lua/logging/email.lua
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Emails logging information to the given recipient
|
||||||
|
--
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
--
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
local smtp = require"socket.smtp"
|
||||||
|
|
||||||
|
function logging.email(params)
|
||||||
|
params = params or {}
|
||||||
|
params.headers = params.headers or {}
|
||||||
|
|
||||||
|
if params.from == nil then
|
||||||
|
return nil, "'from' parameter is required"
|
||||||
|
end
|
||||||
|
if params.rcpt == nil then
|
||||||
|
return nil, "'rcpt' parameter is required"
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
local s = logging.prepareLogMsg(params.logPattern, os.date(), level, message)
|
||||||
|
if params.headers.subject then
|
||||||
|
params.headers.subject =
|
||||||
|
logging.prepareLogMsg(params.headers.subject, os.date(), level, message)
|
||||||
|
end
|
||||||
|
local msg = { headers = params.headers, body = s }
|
||||||
|
params.source = smtp.message(msg)
|
||||||
|
|
||||||
|
local r, e = smtp.send(params)
|
||||||
|
if not r then
|
||||||
|
return nil, e
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.email
|
||||||
|
|
49
1_1.mi_Lua/logging/file.lua
Normal file
49
1_1.mi_Lua/logging/file.lua
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Saves logging information in a file
|
||||||
|
--
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
--
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
|
||||||
|
local lastFileNameDatePattern
|
||||||
|
local lastFileHandler
|
||||||
|
|
||||||
|
local openFileLogger = function (filename, datePattern)
|
||||||
|
local filename = string.format(filename, os.date(datePattern))
|
||||||
|
if (lastFileNameDatePattern ~= filename) then
|
||||||
|
local f = io.open(filename, "a")
|
||||||
|
if (f) then
|
||||||
|
f:setvbuf ("line")
|
||||||
|
lastFileNameDatePattern = filename
|
||||||
|
lastFileHandler = f
|
||||||
|
return f
|
||||||
|
else
|
||||||
|
return nil, string.format("file `%s' could not be opened for writing", filename)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return lastFileHandler
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function logging.file(filename, datePattern, logPattern)
|
||||||
|
if type(filename) ~= "string" then
|
||||||
|
filename = "lualogging.log"
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
local f, msg = openFileLogger(filename, datePattern)
|
||||||
|
if not f then
|
||||||
|
return nil, msg
|
||||||
|
end
|
||||||
|
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
|
||||||
|
f:write(s)
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.file
|
||||||
|
|
79
1_1.mi_Lua/logging/rolling_file.lua
Normal file
79
1_1.mi_Lua/logging/rolling_file.lua
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- RollingFileAppender is a FileAppender that rolls over the logfile
|
||||||
|
-- once it has reached a certain size limit. It also mantains a
|
||||||
|
-- maximum number of log files.
|
||||||
|
--
|
||||||
|
-- @author Tiago Cesar Katcipis (tiagokatcipis@gmail.com)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
|
||||||
|
local function openFile(self)
|
||||||
|
self.file = io.open(self.filename, "a")
|
||||||
|
if not self.file then
|
||||||
|
return nil, string.format("file `%s' could not be opened for writing", self.filename)
|
||||||
|
end
|
||||||
|
self.file:setvbuf ("line")
|
||||||
|
return self.file
|
||||||
|
end
|
||||||
|
|
||||||
|
local rollOver = function (self)
|
||||||
|
for i = self.maxIndex - 1, 1, -1 do
|
||||||
|
-- files may not exist yet, lets ignore the possible errors.
|
||||||
|
os.rename(self.filename.."."..i, self.filename.."."..i+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.file:close()
|
||||||
|
self.file = nil
|
||||||
|
|
||||||
|
local _, msg = os.rename(self.filename, self.filename..".".."1")
|
||||||
|
|
||||||
|
if msg then
|
||||||
|
return nil, string.format("error %s on log rollover", msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
return openFile(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local openRollingFileLogger = function (self)
|
||||||
|
if not self.file then
|
||||||
|
return openFile(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
local filesize = self.file:seek("end", 0)
|
||||||
|
|
||||||
|
if (filesize < self.maxSize) then
|
||||||
|
return self.file
|
||||||
|
end
|
||||||
|
|
||||||
|
return rollOver(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function logging.rolling_file(filename, maxFileSize, maxBackupIndex, logPattern)
|
||||||
|
if type(filename) ~= "string" then
|
||||||
|
filename = "lualogging.log"
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj = {
|
||||||
|
filename = filename,
|
||||||
|
maxSize = maxFileSize,
|
||||||
|
maxIndex = maxBackupIndex or 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
local f, msg = openRollingFileLogger(obj)
|
||||||
|
if not f then
|
||||||
|
return nil, msg
|
||||||
|
end
|
||||||
|
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
|
||||||
|
f:write(s)
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.rolling_file
|
||||||
|
|
33
1_1.mi_Lua/logging/socket.lua
Normal file
33
1_1.mi_Lua/logging/socket.lua
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Sends the logging information through a socket using luasocket
|
||||||
|
--
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
--
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
local socket = require"socket"
|
||||||
|
|
||||||
|
function logging.socket(address, port, logPattern)
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
local s = logging.prepareLogMsg(logPattern, os.date(), level, message)
|
||||||
|
|
||||||
|
local socket, err = socket.connect(address, port)
|
||||||
|
if not socket then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
local cond, err = socket:send(s)
|
||||||
|
if not cond then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
socket:close()
|
||||||
|
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.socket
|
||||||
|
|
62
1_1.mi_Lua/logging/sql.lua
Normal file
62
1_1.mi_Lua/logging/sql.lua
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Saves the logging information in a table using luasql
|
||||||
|
--
|
||||||
|
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
|
||||||
|
--
|
||||||
|
-- @copyright 2004-2013 Kepler Project
|
||||||
|
--
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local logging = require"logging"
|
||||||
|
|
||||||
|
function logging.sql(params)
|
||||||
|
params = params or {}
|
||||||
|
params.tablename = params.tablename or "LogTable"
|
||||||
|
params.logdatefield = params.logdatefield or "LogDate"
|
||||||
|
params.loglevelfield = params.loglevelfield or "LogLevel"
|
||||||
|
params.logmessagefield = params.logmessagefield or "LogMessage"
|
||||||
|
|
||||||
|
if params.connectionfactory == nil or type(params.connectionfactory) ~= "function" then
|
||||||
|
return nil, "No specified connection factory function"
|
||||||
|
end
|
||||||
|
|
||||||
|
local con, err
|
||||||
|
if params.keepalive then
|
||||||
|
con, err = params.connectionfactory()
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.new( function(self, level, message)
|
||||||
|
if (not params.keepalive) or (con == nil) then
|
||||||
|
con, err = params.connectionfactory()
|
||||||
|
if not con then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local logDate = os.date("%Y-%m-%d %H:%M:%S")
|
||||||
|
local insert = string.format("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
|
||||||
|
params.tablename, params.logdatefield, params.loglevelfield,
|
||||||
|
params.logmessagefield, logDate, level, string.gsub(message, "'", "''"))
|
||||||
|
|
||||||
|
local ret, err = pcall(con.execute, con, insert)
|
||||||
|
if not ret then
|
||||||
|
con, err = params.connectionfactory()
|
||||||
|
if not con then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
ret, err = con:execute(insert)
|
||||||
|
if not ret then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not params.keepalive then
|
||||||
|
con:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logging.sql
|
||||||
|
|
BIN
1_1.mi_Lua/lsqlite3.so
Normal file
BIN
1_1.mi_Lua/lsqlite3.so
Normal file
Binary file not shown.
292
1_1.mi_Lua/ltn12.lua
Normal file
292
1_1.mi_Lua/ltn12.lua
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- LTN12 - Filters, sources, sinks and pumps.
|
||||||
|
-- LuaSocket toolkit.
|
||||||
|
-- Author: Diego Nehab
|
||||||
|
-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Declare module
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
local string = require("string")
|
||||||
|
local table = require("table")
|
||||||
|
local base = _G
|
||||||
|
module("ltn12")
|
||||||
|
|
||||||
|
filter = {}
|
||||||
|
source = {}
|
||||||
|
sink = {}
|
||||||
|
pump = {}
|
||||||
|
|
||||||
|
-- 2048 seems to be better in windows...
|
||||||
|
BLOCKSIZE = 2048
|
||||||
|
_VERSION = "LTN12 1.0.1"
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Filter stuff
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- returns a high level filter that cycles a low-level filter
|
||||||
|
function filter.cycle(low, ctx, extra)
|
||||||
|
base.assert(low)
|
||||||
|
return function(chunk)
|
||||||
|
local ret
|
||||||
|
ret, ctx = low(ctx, chunk, extra)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- chains a bunch of filters together
|
||||||
|
-- (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
|
||||||
|
|
||||||
|
-- returns a source that just outputs an error
|
||||||
|
function source.error(err)
|
||||||
|
return function()
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- creates a file source
|
||||||
|
function source.file(handle, io_err)
|
||||||
|
if handle then
|
||||||
|
return function()
|
||||||
|
local chunk = handle:read(BLOCKSIZE)
|
||||||
|
if not chunk then handle:close() end
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
else return source.error(io_err or "unable to open file") end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- turns a fancy source into a simple source
|
||||||
|
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
|
||||||
|
|
||||||
|
-- creates string source
|
||||||
|
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
|
||||||
|
|
||||||
|
-- creates rewindable source
|
||||||
|
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
|
||||||
|
table.insert(t, 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
|
||||||
|
|
||||||
|
-- creates a source that produces contents of several sources, 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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- creates a sink that stores into a table
|
||||||
|
function sink.table(t)
|
||||||
|
t = t or {}
|
||||||
|
local f = function(chunk, err)
|
||||||
|
if chunk then table.insert(t, chunk) end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
return f, t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- turns a fancy sink into a simple sink
|
||||||
|
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
|
||||||
|
|
||||||
|
-- creates a file sink
|
||||||
|
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
|
||||||
|
|
||||||
|
-- creates a sink that just returns an error
|
||||||
|
function sink.error(err)
|
||||||
|
return function()
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- chains a sink with a filter
|
||||||
|
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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- pumps one chunk from the source to the sink
|
||||||
|
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
|
||||||
|
|
||||||
|
-- pumps all data from a source to a sink, using a step function
|
||||||
|
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
|
||||||
|
|
23
1_1.mi_Lua/luci/cacheloader.lua
Normal file
23
1_1.mi_Lua/luci/cacheloader.lua
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
--[[
|
||||||
|
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 config = require "luci.config"
|
||||||
|
local ccache = require "luci.ccache"
|
||||||
|
|
||||||
|
module "luci.cacheloader"
|
||||||
|
|
||||||
|
if config.ccache and config.ccache.enable == "1" then
|
||||||
|
ccache.cache_ondemand()
|
||||||
|
end
|
1850
1_1.mi_Lua/luci/cbi.lua
Normal file
1850
1_1.mi_Lua/luci/cbi.lua
Normal file
File diff suppressed because it is too large
Load Diff
345
1_1.mi_Lua/luci/cbi/datatypes.lua
Normal file
345
1_1.mi_Lua/luci/cbi/datatypes.lua
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
LuCI - Configuration Bind Interface - Datatype Tests
|
||||||
|
(c) 2010 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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: datatypes.lua 9352 2012-10-06 23:50:52Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local ip = require "luci.ip"
|
||||||
|
local math = require "math"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
|
||||||
|
|
||||||
|
|
||||||
|
module "luci.cbi.datatypes"
|
||||||
|
|
||||||
|
|
||||||
|
_M['or'] = function(v, ...)
|
||||||
|
local i
|
||||||
|
for i = 1, select('#', ...), 2 do
|
||||||
|
local f = select(i, ...)
|
||||||
|
local a = select(i+1, ...)
|
||||||
|
if type(f) ~= "function" then
|
||||||
|
if f == v then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
i = i - 1
|
||||||
|
elseif f(v, unpack(a)) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
_M['and'] = function(v, ...)
|
||||||
|
local i
|
||||||
|
for i = 1, select('#', ...), 2 do
|
||||||
|
local f = select(i, ...)
|
||||||
|
local a = select(i+1, ...)
|
||||||
|
if type(f) ~= "function" then
|
||||||
|
if f ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
i = i - 1
|
||||||
|
elseif not f(v, unpack(a)) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function neg(v, ...)
|
||||||
|
return _M['or'](v:gsub("^%s*!%s*", ""), ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function list(v, subvalidator, subargs)
|
||||||
|
if type(subvalidator) ~= "function" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local token
|
||||||
|
for token in v:gmatch("%S+") do
|
||||||
|
if not subvalidator(token, unpack(subargs)) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function bool(val)
|
||||||
|
if val == "1" or val == "yes" or val == "on" or val == "true" then
|
||||||
|
return true
|
||||||
|
elseif val == "0" or val == "no" or val == "off" or val == "false" then
|
||||||
|
return true
|
||||||
|
elseif val == "" or val == nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function uinteger(val)
|
||||||
|
local n = tonumber(val)
|
||||||
|
if n ~= nil and math.floor(n) == n and n >= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function integer(val)
|
||||||
|
local n = tonumber(val)
|
||||||
|
if n ~= nil and math.floor(n) == n then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function ufloat(val)
|
||||||
|
local n = tonumber(val)
|
||||||
|
return ( n ~= nil and n >= 0 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function float(val)
|
||||||
|
return ( tonumber(val) ~= nil )
|
||||||
|
end
|
||||||
|
|
||||||
|
function ipaddr(val)
|
||||||
|
return ip4addr(val) or ip6addr(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ip4addr(val)
|
||||||
|
if val then
|
||||||
|
return ip.IPv4(val) and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function ip4prefix(val)
|
||||||
|
val = tonumber(val)
|
||||||
|
return ( val and val >= 0 and val <= 32 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function ip6addr(val)
|
||||||
|
if val then
|
||||||
|
return ip.IPv6(val) and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function ip6prefix(val)
|
||||||
|
val = tonumber(val)
|
||||||
|
return ( val and val >= 0 and val <= 128 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function port(val)
|
||||||
|
val = tonumber(val)
|
||||||
|
return ( val and val >= 0 and val <= 65535 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function portrange(val)
|
||||||
|
local p1, p2 = val:match("^(%d+)%-(%d+)$")
|
||||||
|
if p1 and p2 and port(p1) and port(p2) then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return port(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function macaddr(val)
|
||||||
|
if val and val:match(
|
||||||
|
"^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
|
||||||
|
"[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
|
||||||
|
) then
|
||||||
|
local parts = util.split( val, ":" )
|
||||||
|
|
||||||
|
for i = 1,6 do
|
||||||
|
parts[i] = tonumber( parts[i], 16 )
|
||||||
|
if parts[i] < 0 or parts[i] > 255 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function hostname(val)
|
||||||
|
if val and (#val < 254) and (
|
||||||
|
val:match("^[a-zA-Z_]+$") or
|
||||||
|
(val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
|
||||||
|
val:match("[^0-9%.]"))
|
||||||
|
) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function host(val)
|
||||||
|
return hostname(val) or ipaddr(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function network(val)
|
||||||
|
return uciname(val) or host(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function wpakey(val)
|
||||||
|
if #val == 64 then
|
||||||
|
return (val:match("^[a-fA-F0-9]+$") ~= nil)
|
||||||
|
else
|
||||||
|
return (#val >= 8) and (#val <= 63)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function wepkey(val)
|
||||||
|
if val:sub(1, 2) == "s:" then
|
||||||
|
val = val:sub(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (#val == 10) or (#val == 26) then
|
||||||
|
return (val:match("^[a-fA-F0-9]+$") ~= nil)
|
||||||
|
else
|
||||||
|
return (#val == 5) or (#val == 13)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function string(val)
|
||||||
|
return true -- Everything qualifies as valid string
|
||||||
|
end
|
||||||
|
|
||||||
|
function directory( val, seen )
|
||||||
|
local s = fs.stat(val)
|
||||||
|
seen = seen or { }
|
||||||
|
|
||||||
|
if s and not seen[s.ino] then
|
||||||
|
seen[s.ino] = true
|
||||||
|
if s.type == "dir" then
|
||||||
|
return true
|
||||||
|
elseif s.type == "lnk" then
|
||||||
|
return directory( fs.readlink(val), seen )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function file( val, seen )
|
||||||
|
local s = fs.stat(val)
|
||||||
|
seen = seen or { }
|
||||||
|
|
||||||
|
if s and not seen[s.ino] then
|
||||||
|
seen[s.ino] = true
|
||||||
|
if s.type == "reg" then
|
||||||
|
return true
|
||||||
|
elseif s.type == "lnk" then
|
||||||
|
return file( fs.readlink(val), seen )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function device( val, seen )
|
||||||
|
local s = fs.stat(val)
|
||||||
|
seen = seen or { }
|
||||||
|
|
||||||
|
if s and not seen[s.ino] then
|
||||||
|
seen[s.ino] = true
|
||||||
|
if s.type == "chr" or s.type == "blk" then
|
||||||
|
return true
|
||||||
|
elseif s.type == "lnk" then
|
||||||
|
return device( fs.readlink(val), seen )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function uciname(val)
|
||||||
|
return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
function range(val, min, max)
|
||||||
|
val = tonumber(val)
|
||||||
|
min = tonumber(min)
|
||||||
|
max = tonumber(max)
|
||||||
|
|
||||||
|
if val ~= nil and min ~= nil and max ~= nil then
|
||||||
|
return ((val >= min) and (val <= max))
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function min(val, min)
|
||||||
|
val = tonumber(val)
|
||||||
|
min = tonumber(min)
|
||||||
|
|
||||||
|
if val ~= nil and min ~= nil then
|
||||||
|
return (val >= min)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function max(val, max)
|
||||||
|
val = tonumber(val)
|
||||||
|
max = tonumber(max)
|
||||||
|
|
||||||
|
if val ~= nil and max ~= nil then
|
||||||
|
return (val <= max)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function rangelength(val, min, max)
|
||||||
|
val = tostring(val)
|
||||||
|
min = tonumber(min)
|
||||||
|
max = tonumber(max)
|
||||||
|
|
||||||
|
if val ~= nil and min ~= nil and max ~= nil then
|
||||||
|
return ((#val >= min) and (#val <= max))
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function minlength(val, min)
|
||||||
|
val = tostring(val)
|
||||||
|
min = tonumber(min)
|
||||||
|
|
||||||
|
if val ~= nil and min ~= nil then
|
||||||
|
return (#val >= min)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function maxlength(val, max)
|
||||||
|
val = tostring(val)
|
||||||
|
max = tonumber(max)
|
||||||
|
|
||||||
|
if val ~= nil and max ~= nil then
|
||||||
|
return (#val <= max)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function phonedigit(val)
|
||||||
|
return (val:match("^[0-9\*#]+$") ~= nil)
|
||||||
|
end
|
87
1_1.mi_Lua/luci/ccache.lua
Normal file
87
1_1.mi_Lua/luci/ccache.lua
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
--[[
|
||||||
|
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 io = require "io"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local debug = require "debug"
|
||||||
|
local string = require "string"
|
||||||
|
local package = require "package"
|
||||||
|
|
||||||
|
local type, loadfile = type, loadfile
|
||||||
|
|
||||||
|
|
||||||
|
module "luci.ccache"
|
||||||
|
|
||||||
|
function cache_ondemand(...)
|
||||||
|
if debug.getinfo(1, 'S').source ~= "=?" then
|
||||||
|
cache_enable(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cache_enable(cachepath, mode)
|
||||||
|
cachepath = cachepath or "/tmp/luci-modulecache"
|
||||||
|
mode = mode or "r--r--r--"
|
||||||
|
|
||||||
|
local loader = package.loaders[2]
|
||||||
|
local uid = nixio.getuid()
|
||||||
|
|
||||||
|
if not fs.stat(cachepath) then
|
||||||
|
fs.mkdir(cachepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _encode_filename(name)
|
||||||
|
local encoded = ""
|
||||||
|
for i=1, #name do
|
||||||
|
encoded = encoded .. ("%2X" % string.byte(name, i))
|
||||||
|
end
|
||||||
|
return encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _load_sane(file)
|
||||||
|
local stat = fs.stat(file)
|
||||||
|
if stat and stat.uid == uid and stat.modestr == mode then
|
||||||
|
return loadfile(file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _write_sane(file, func)
|
||||||
|
if nixio.getuid() == uid then
|
||||||
|
local fp = io.open(file, "w")
|
||||||
|
if fp then
|
||||||
|
fp:write(util.get_bytecode(func))
|
||||||
|
fp:close()
|
||||||
|
fs.chmod(file, mode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
package.loaders[2] = function(mod)
|
||||||
|
local encoded = cachepath .. "/" .. _encode_filename(mod)
|
||||||
|
local modcons = _load_sane(encoded)
|
||||||
|
|
||||||
|
if modcons then
|
||||||
|
return modcons
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No cachefile
|
||||||
|
modcons = loader(mod)
|
||||||
|
if type(modcons) == "function" then
|
||||||
|
_write_sane(encoded, modcons)
|
||||||
|
end
|
||||||
|
return modcons
|
||||||
|
end
|
||||||
|
end
|
42
1_1.mi_Lua/luci/config.lua
Normal file
42
1_1.mi_Lua/luci/config.lua
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Configuration
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Some LuCI configuration values read from uci file "luci"
|
||||||
|
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: config.lua 3856 2008-12-05 15:36:44Z Cyrus $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
module("luci.config",
|
||||||
|
function(m)
|
||||||
|
if pcall(require, "luci.model.uci") then
|
||||||
|
local config = util.threadlocal()
|
||||||
|
setmetatable(m, {
|
||||||
|
__index = function(tbl, key)
|
||||||
|
if not config[key] then
|
||||||
|
config[key] = luci.model.uci.cursor():get_all("luci", key)
|
||||||
|
end
|
||||||
|
return config[key]
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
10
1_1.mi_Lua/luci/controller/api/index.lua
Normal file
10
1_1.mi_Lua/luci/controller/api/index.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module("luci.controller.api.index", package.seeall)
|
||||||
|
function index()
|
||||||
|
local page = node("api")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = _("")
|
||||||
|
page.order = 10
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
end
|
336
1_1.mi_Lua/luci/controller/api/xqdatacenter.lua
Normal file
336
1_1.mi_Lua/luci/controller/api/xqdatacenter.lua
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
module("luci.controller.api.xqdatacenter", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("api","xqdatacenter")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = 300
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"api", "xqdatacenter"}, firstchild(), _(""), 300)
|
||||||
|
entry({"api", "xqdatacenter", "request"}, call("tunnelRequest"), _(""), 301)
|
||||||
|
entry({"api", "xqdatacenter", "identify_device"}, call("identifyDevice"), _(""), 302, 0x08)
|
||||||
|
entry({"api", "xqdatacenter", "download"}, call("download"), _(""), 303)
|
||||||
|
entry({"api", "xqdatacenter", "upload"}, call("upload"), _(""), 304)
|
||||||
|
entry({"api", "xqdatacenter", "thumb"}, call("getThumb"), _(""), 305)
|
||||||
|
entry({"api", "xqdatacenter", "device_id"}, call("getDeviceId"), _(""), 306)
|
||||||
|
entry({"api", "xqdatacenter", "check_file_exist"}, call("checkFileExist"), _(""), 307)
|
||||||
|
entry({"api", "xqdatacenter", "plugin_ssh"}, call("pluginSSH"), _(""), 308)
|
||||||
|
entry({"api", "xqdatacenter", "plugin_ssh_status"}, call("pluginSSHStatus"), _(""), 309)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local LuciJson = require("json")
|
||||||
|
local XQConfigs = require("xiaoqiang.common.XQConfigs")
|
||||||
|
local XQFunction = require("xiaoqiang.common.XQFunction")
|
||||||
|
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
|
||||||
|
|
||||||
|
function tunnelRequest()
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function identifyDevice()
|
||||||
|
local cmd = XQConfigs.THRIFT_TO_MQTT_IDENTIFY_DEVICE
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local result = {}
|
||||||
|
result["code"] = 0
|
||||||
|
result["info"] = LuciUtil.exec(cmd)
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getDeviceId()
|
||||||
|
local cmd = XQConfigs.THRIFT_TO_MQTT_GET_DEVICEID
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local result = {}
|
||||||
|
result["code"] = 0
|
||||||
|
result["deviceId"] = LuciUtil.exec(cmd)
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function download()
|
||||||
|
local fs = require("nixio.fs")
|
||||||
|
local mime = require("luci.http.protocol.mime")
|
||||||
|
local ltn12 = require("luci.ltn12")
|
||||||
|
local log = require("xiaoqiang.XQLog")
|
||||||
|
|
||||||
|
local path = LuciHttp.formvalue("path")
|
||||||
|
if XQFunction.isStrNil(path) then
|
||||||
|
LuciHttp.status(404, _("no Such file"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local constPrefix1 = "/userdisk/data/"
|
||||||
|
local constPrefix2 = "/extdisks/"
|
||||||
|
local constPrefix3 = "/userdisk/privacyData/"
|
||||||
|
if (string.sub(path, 1, string.len(constPrefix1)) ~= constPrefix1) and (string.sub(path, 1, string.len(constPrefix2)) ~= constPrefix2) and (string.sub(path, 1, string.len(constPrefix3)) ~= constPrefix3) then
|
||||||
|
LuciHttp.status(403, _("no permission"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check privacy disk permission by md5 device mac address
|
||||||
|
--[[if string.sub(path, 1, string.len(constPrefix3)) == constPrefix3 then
|
||||||
|
local secret = LuciHttp.formvalue("secret")
|
||||||
|
if XQFunction.isStrNil(secret) then
|
||||||
|
LuciHttp.status(403, _("no permission"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
log.log(3, "=============secret = " .. secret)
|
||||||
|
|
||||||
|
local access = false
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local XQDeviceUtil = require("xiaoqiang.util.XQDeviceUtil")
|
||||||
|
local macfilterInfoList = XQDeviceUtil.getMacfilterInfoList()
|
||||||
|
for _,value in ipairs(macfilterInfoList) do
|
||||||
|
if XQFunction.isStrNil(value.mac) == false then
|
||||||
|
log.log(3, "=============mac = " .. value.mac)
|
||||||
|
if string.lower(XQCryptoUtil.md5Str(string.lower(value.mac))) == string.lower(secret) then
|
||||||
|
log.log(3, "=============device found")
|
||||||
|
if value.pridisk then
|
||||||
|
access = true
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if access == false then
|
||||||
|
LuciHttp.status(403, _("no permission"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end]]
|
||||||
|
|
||||||
|
log.log(3, "=============path = " .. path)
|
||||||
|
local stat = fs.stat(path)
|
||||||
|
if not stat then
|
||||||
|
LuciHttp.status(404, _("no Such file"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
LuciHttp.header("Accept-Ranges", "bytes")
|
||||||
|
LuciHttp.header("Content-Type", mime.to_mime(path))
|
||||||
|
local range = LuciHttp.getenv("HTTP_RANGE")
|
||||||
|
-- format: bytes=123-
|
||||||
|
if range then
|
||||||
|
LuciHttp.status(206)
|
||||||
|
range = string.gsub(range, "bytes=", "")
|
||||||
|
range = string.gsub(range, "-", "")
|
||||||
|
else
|
||||||
|
range = 0
|
||||||
|
end
|
||||||
|
log.log(3, "=============range = " .. range)
|
||||||
|
-- format: bytes 123-456/457
|
||||||
|
local contentRange = "bytes " .. range .. "-" .. (stat.size - 1) .. "/" .. stat.size
|
||||||
|
log.log(3, "=============contentRange = " .. contentRange)
|
||||||
|
LuciHttp.header("Content-Length", stat.size - range)
|
||||||
|
LuciHttp.header("Content-Range", contentRange)
|
||||||
|
LuciHttp.header("Content-Disposition", "attachment; filename=" .. fs.basename(path))
|
||||||
|
|
||||||
|
if string.sub(path, 1, string.len(constPrefix1)) == constPrefix1 then
|
||||||
|
LuciHttp.header("X-Accel-Redirect", "/download-userdisk/" .. string.sub(path, string.len(constPrefix1) + 1, string.len(path)))
|
||||||
|
elseif string.sub(path, 1, string.len(constPrefix2)) == constPrefix2 then
|
||||||
|
LuciHttp.header("X-Accel-Redirect", "/download-extdisks/" .. string.sub(path, string.len(constPrefix2) + 1, string.len(path)))
|
||||||
|
elseif string.sub(path, 1, string.len(constPrefix3)) == constPrefix3 then
|
||||||
|
LuciHttp.header("X-Accel-Redirect", "/download-pridisk/" .. string.sub(path, string.len(constPrefix3) + 1, string.len(path)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--local file = io.open(path, "r")
|
||||||
|
--local position = file:seek("set", range)
|
||||||
|
--log.log(3, "=============position = " .. position)
|
||||||
|
--ltn12.pump.all(ltn12.source.file(file), LuciHttp.write)
|
||||||
|
end
|
||||||
|
|
||||||
|
function upload()
|
||||||
|
local fp
|
||||||
|
local log = require("xiaoqiang.XQLog")
|
||||||
|
local fs = require("luci.fs")
|
||||||
|
local tmpfile = "/userdisk/upload.tmp"
|
||||||
|
if fs.isfile(tmpfile) then
|
||||||
|
fs.unlink(tmpfile)
|
||||||
|
end
|
||||||
|
local filename
|
||||||
|
LuciHttp.setfilehandler(
|
||||||
|
function(meta, chunk, eof)
|
||||||
|
if not fp then
|
||||||
|
if meta and meta.name == "file" then
|
||||||
|
fp = io.open(tmpfile, "w")
|
||||||
|
filename = meta.file
|
||||||
|
filename = string.gsub(filename, "+", " ")
|
||||||
|
filename = string.gsub(filename, "%%(%x%x)",
|
||||||
|
function(h)
|
||||||
|
return string.char(tonumber(h, 16))
|
||||||
|
end)
|
||||||
|
filename = filename.gsub(filename, "\r\n", "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if chunk then
|
||||||
|
fp:write(chunk)
|
||||||
|
end
|
||||||
|
if eof then
|
||||||
|
fp:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local path = LuciHttp.formvalue("target")
|
||||||
|
if string.match(path, "\/$") == nil then
|
||||||
|
path = path .. "/"
|
||||||
|
end
|
||||||
|
fs.mkdir(path, true)
|
||||||
|
|
||||||
|
local savename = filename
|
||||||
|
if fs.isfile(path .. savename) then
|
||||||
|
local basename = savename
|
||||||
|
local index = basename:match(".+()%.%w+$")
|
||||||
|
if index then
|
||||||
|
basename = basename:sub(1, index - 1)
|
||||||
|
end
|
||||||
|
local extension = savename:match(".+%.(%w+)$")
|
||||||
|
for i = 1, 100, 1 do
|
||||||
|
local tmpname = basename .. "(" .. i .. ")"
|
||||||
|
if extension then
|
||||||
|
tmpname = tmpname .. "." .. extension
|
||||||
|
end
|
||||||
|
if not fs.isfile(path .. tmpname) then
|
||||||
|
savename = tmpname
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local dest = path .. savename
|
||||||
|
log.log(3, "dest=" .. dest)
|
||||||
|
fs.rename(tmpfile, dest)
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
result["code"] = 0
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getThumb()
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local fs = require("nixio.fs")
|
||||||
|
local mime = require("luci.http.protocol.mime")
|
||||||
|
local ltn12 = require("luci.ltn12")
|
||||||
|
local log = require("xiaoqiang.XQLog")
|
||||||
|
|
||||||
|
local realPath = LuciHttp.formvalue("filePath")
|
||||||
|
log.log(3, "realPath = ", realPath)
|
||||||
|
if (realPath == nil) then
|
||||||
|
LuciHttp.status(404, _("no Such file"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = "{\"api\":10, \"files\":[\"" ..realPath.. "\"]}"
|
||||||
|
local thumbResponse = XQFunction.thrift_tunnel_to_datacenter(payload)
|
||||||
|
if thumbResponse and thumbResponse.code == 0 then
|
||||||
|
local thumbPath = thumbResponse.thumbnails[1]
|
||||||
|
local stat = fs.stat(thumbPath)
|
||||||
|
LuciHttp.header("Content-Type", mime.to_mime(thumbPath))
|
||||||
|
LuciHttp.header("Content-Length", stat.size)
|
||||||
|
ltn12.pump.all(ltn12.source.file(io.open(thumbPath, "r")), LuciHttp.write)
|
||||||
|
else
|
||||||
|
LuciHttp.status(404, _("no Such thumb file"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function checkFileExist()
|
||||||
|
local fs = require("nixio.fs")
|
||||||
|
|
||||||
|
local exist = true
|
||||||
|
local path = LuciHttp.formvalue("filePath")
|
||||||
|
if XQFunction.isStrNil(path) then
|
||||||
|
exist = false
|
||||||
|
else
|
||||||
|
local stat = fs.stat(path)
|
||||||
|
if not stat then
|
||||||
|
exist = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
result["code"] = 0
|
||||||
|
result['exist'] = exist
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginSSH()
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local XQLog = require("xiaoqiang.XQLog")
|
||||||
|
local code = 0
|
||||||
|
local result = {}
|
||||||
|
local pluginID = LuciHttp.formvalue("pluginID")
|
||||||
|
local capabilitystr = LuciHttp.formvalue("capability")
|
||||||
|
local open = tonumber(LuciHttp.formvalue("open") or 0)
|
||||||
|
XQLog.check(0, XQLog.KEY_FUNC_PLUGIN, 1)
|
||||||
|
if open and open == 1 then
|
||||||
|
if pluginID and capabilitystr then
|
||||||
|
local payload = {
|
||||||
|
["api"] = 611,
|
||||||
|
["pluginID"] = pluginID,
|
||||||
|
["capability"] = LuciUtil.split(capabilitystr, ",")
|
||||||
|
}
|
||||||
|
local datacenter = XQFunction.thrift_tunnel_to_datacenter(LuciJson.encode(payload))
|
||||||
|
if datacenter and datacenter.code ~= 0 then
|
||||||
|
code = 1595
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1537
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local payload = {
|
||||||
|
["api"] = 613
|
||||||
|
}
|
||||||
|
local datacenter = XQFunction.thrift_tunnel_to_datacenter(LuciJson.encode(payload))
|
||||||
|
if datacenter and datacenter.code == 0 then
|
||||||
|
code = 0
|
||||||
|
else
|
||||||
|
code = 1601
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginSSHStatus()
|
||||||
|
local code = 0
|
||||||
|
local result = {}
|
||||||
|
local datacenter = XQFunction.thrift_tunnel_to_datacenter([[{"api":612}]])
|
||||||
|
local capability = XQFunction.thrift_tunnel_to_datacenter([[{"api":621}]])
|
||||||
|
if datacenter and datacenter.code == 0 and capability and datacenter.code == 0 then
|
||||||
|
local capabilitylist = {}
|
||||||
|
result["enable"] = datacenter.status == 1 and 1 or 0
|
||||||
|
local encapability = {}
|
||||||
|
if result.enable == 1 then
|
||||||
|
local pluginSSH = datacenter.plugin_ssh_status
|
||||||
|
result["pluginID"] = pluginSSH.pluginID
|
||||||
|
encapability = pluginSSH.capability
|
||||||
|
end
|
||||||
|
for _, item in ipairs(capability.list) do
|
||||||
|
item.enable = 0
|
||||||
|
for _, capa in ipairs(encapability) do
|
||||||
|
if item.key == capa then
|
||||||
|
item.enable = 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(capabilitylist, item)
|
||||||
|
end
|
||||||
|
result["capability"] = capabilitylist
|
||||||
|
else
|
||||||
|
code = 1600
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
293
1_1.mi_Lua/luci/controller/api/xqnetdetect.lua
Normal file
293
1_1.mi_Lua/luci/controller/api/xqnetdetect.lua
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
module("luci.controller.api.xqnetdetect", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("api","xqnetdetect")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = 350
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"api", "xqnetdetect"}, firstchild(), _(""), 350)
|
||||||
|
entry({"api", "xqnetdetect", "wan_status"}, call("getWanStatus"), _(""), 351, 0x01)
|
||||||
|
entry({"api", "xqnetdetect", "sys_info"}, call("getSysInfo"), (""), 352, 0x01)
|
||||||
|
entry({"api", "xqnetdetect", "ping_test"}, call("pingTest"), (""), 353, 0x01)
|
||||||
|
entry({"api", "xqnetdetect", "detect"}, call("systemDiagnostics"), (""), 354, 0x01)
|
||||||
|
entry({"api", "xqnetdetect", "sys_status"}, call("systemStatus"), (""), 355, 0x01)
|
||||||
|
entry({"api", "xqnetdetect", "netspeed"}, call("netspeed"), (""), 356)
|
||||||
|
entry({"api", "xqnetdetect", "uploadspeed"}, call("uploadSpeed"), (""), 357)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local XQFunction = require("xiaoqiang.common.XQFunction")
|
||||||
|
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
|
||||||
|
|
||||||
|
function getWanStatus()
|
||||||
|
local XQLanWanUtil = require("xiaoqiang.util.XQLanWanUtil")
|
||||||
|
local result = {}
|
||||||
|
local wanType = XQLanWanUtil.getAutoWanType()
|
||||||
|
local wanInfo = XQLanWanUtil.getLanWanInfo("wan")
|
||||||
|
local wanMonitor = XQLanWanUtil.getWanMonitorStat()
|
||||||
|
result["code"] = 0
|
||||||
|
result["wanLink"] = wanType == 99 and 0 or 1
|
||||||
|
result["wanType"] = wanType
|
||||||
|
result["wanInfo"] = wanInfo
|
||||||
|
result["wanMonitor"] = wanMonitor
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getSysInfo()
|
||||||
|
local LuciSys = require("luci.sys")
|
||||||
|
local result = {}
|
||||||
|
local cpu = {}
|
||||||
|
local mem = {}
|
||||||
|
local system, model, memtotal, memcached, membuffers, memfree, bogomips = LuciSys.sysinfo()
|
||||||
|
cpu["info"] = system
|
||||||
|
mem["total"] = memtotal
|
||||||
|
mem["free"] = memfree
|
||||||
|
result["code"] = 0
|
||||||
|
result["cpuInfo"] = cpu
|
||||||
|
result["memInfo"] = mem
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pingTest()
|
||||||
|
local LuciSys = require("luci.sys")
|
||||||
|
local pingUrl = LuciHttp.formvalue("url")
|
||||||
|
local ping = LuciSys.net.pingtest(pingUrl)
|
||||||
|
local result = {}
|
||||||
|
result["code"] = 0
|
||||||
|
result["result"] = ping == 0 and 1 or 0
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
simple : 0/1/2 (正常模式,时间长上传log/简单模式,时间短,不上传log/简单模式,时间短,上传log)
|
||||||
|
]]--
|
||||||
|
function systemDiagnostics()
|
||||||
|
local XQLog = require("xiaoqiang.XQLog")
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local XQSecureUtil = require("xiaoqiang.util.XQSecureUtil")
|
||||||
|
local XQWifiUtil = require("xiaoqiang.util.XQWifiUtil")
|
||||||
|
local XQDeviceUtil = require("xiaoqiang.util.XQDeviceUtil")
|
||||||
|
|
||||||
|
local lan = XQDeviceUtil.getWanLanNetworkStatistics("lan")
|
||||||
|
local wan = XQDeviceUtil.getWanLanNetworkStatistics("wan")
|
||||||
|
local speed = {}
|
||||||
|
speed["lan"] = tonumber(lan.downspeed)
|
||||||
|
speed["wan"] = tonumber(wan.downspeed)
|
||||||
|
|
||||||
|
local simple = tonumber(LuciHttp.formvalue("simple") or 0)
|
||||||
|
local target = LuciHttp.formvalue("target")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local status = 0
|
||||||
|
local count = 0
|
||||||
|
local cpuTemperature = XQSysUtil.getCpuTemperature()
|
||||||
|
local network = XQSysUtil.getNetworkDetectInfo(simple,target)
|
||||||
|
XQSysUtil.setDetectionTimestamp()
|
||||||
|
|
||||||
|
local wifiInfo = XQWifiUtil.getAllWifiInfo()
|
||||||
|
local same = false
|
||||||
|
local strong = true
|
||||||
|
local wifi = {}
|
||||||
|
for i=1, #wifiInfo do
|
||||||
|
if XQSecureUtil.checkPlaintextPwd("admin", wifiInfo[i].password) then
|
||||||
|
same = true
|
||||||
|
end
|
||||||
|
if XQSecureUtil.checkStrong(wifiInfo[i].password) < 2 then
|
||||||
|
strong = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
wifi["same"] = same and 1 or 0
|
||||||
|
wifi["strong"] = strong and 1 or 0
|
||||||
|
|
||||||
|
local disk = {}
|
||||||
|
local diskinfo = XQFunction.thrift_tunnel_to_datacenter([[{"api":26}]])
|
||||||
|
if diskinfo and diskinfo.code == 0 then
|
||||||
|
local capacity = tonumber(diskinfo.capacity)
|
||||||
|
local free = tonumber(diskinfo.free)
|
||||||
|
disk["Used"] = string.format("%.3fG", (capacity - free)/1073741824)
|
||||||
|
disk["Available"] = string.format("%.3fG", free/1073741824)
|
||||||
|
end
|
||||||
|
|
||||||
|
if network then
|
||||||
|
local cputemp = {}
|
||||||
|
cputemp["temperature"] = cpuTemperature
|
||||||
|
if cpuTemperature > 70 then
|
||||||
|
count = count + 1
|
||||||
|
status = 1
|
||||||
|
cputemp["status"] = 0
|
||||||
|
else
|
||||||
|
cputemp["status"] = 1
|
||||||
|
end
|
||||||
|
local cpuavg = {}
|
||||||
|
cpuavg["loadavg"] = network.cpu
|
||||||
|
if tonumber(network.cpu) > 90 then
|
||||||
|
count = count + 1
|
||||||
|
status = 1
|
||||||
|
cpuavg["status"] = 0
|
||||||
|
else
|
||||||
|
cpuavg["status"] = 1
|
||||||
|
end
|
||||||
|
local memoryuse = {}
|
||||||
|
memoryuse["use"] = network.memory
|
||||||
|
if tonumber(network.memory) > 90 then
|
||||||
|
count = count + 1
|
||||||
|
status = 1
|
||||||
|
memoryuse["status"] = 0
|
||||||
|
else
|
||||||
|
memoryuse["status"] = 1
|
||||||
|
end
|
||||||
|
local link = {}
|
||||||
|
if network.wanLink ~= 1 then
|
||||||
|
count = count + 1
|
||||||
|
status = 2
|
||||||
|
link["status"] = 0
|
||||||
|
else
|
||||||
|
link["status"] = 1
|
||||||
|
end
|
||||||
|
local wan = {}
|
||||||
|
wan["type"] = network.wanType
|
||||||
|
if tonumber(network.wanLink) ~= 1 then
|
||||||
|
count = count + 1
|
||||||
|
status = 2
|
||||||
|
wan["status"] = 0
|
||||||
|
else
|
||||||
|
wan["status"] = 1
|
||||||
|
end
|
||||||
|
local gateway = {}
|
||||||
|
gateway["lost"] = network.gw
|
||||||
|
if tonumber(network.gw) > 80 then
|
||||||
|
count = count + 1
|
||||||
|
status = 1
|
||||||
|
gateway["status"] = 0
|
||||||
|
else
|
||||||
|
gateway["status"] = 1
|
||||||
|
end
|
||||||
|
local dnsstatus = {}
|
||||||
|
if tonumber(network.dns) ~= 1 then
|
||||||
|
count = count + 1
|
||||||
|
status = 2
|
||||||
|
dnsstatus["status"] = 0
|
||||||
|
else
|
||||||
|
dnsstatus["status"] = 1
|
||||||
|
end
|
||||||
|
local ping = {}
|
||||||
|
ping["lost"] = network.pingLost
|
||||||
|
if tonumber(network.pingLost) > 80 then
|
||||||
|
count = count + 1
|
||||||
|
status = 2
|
||||||
|
ping["status"] = 0
|
||||||
|
else
|
||||||
|
ping["status"] = 1
|
||||||
|
end
|
||||||
|
result = network
|
||||||
|
result["count"] = count
|
||||||
|
result["status"] = status
|
||||||
|
result["cpuavg"] = cpuavg
|
||||||
|
result["memoryuse"] = memoryuse
|
||||||
|
result["cputemp"] = cputemp
|
||||||
|
result["link"] = link
|
||||||
|
result["wan"] = wan
|
||||||
|
result["gateway"] = gateway
|
||||||
|
result["dnsstatus"] = dnsstatus
|
||||||
|
result["ping"] = ping
|
||||||
|
result["cpuTemperature"] = cpuTemperature
|
||||||
|
result["wifi"] = wifi
|
||||||
|
result["speed"] = speed
|
||||||
|
result["disk"] = disk
|
||||||
|
if count > 0 then
|
||||||
|
XQLog.check(0, XQLog.KEY_DETECT_ERROR, 1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1567
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
else
|
||||||
|
local XQPushHelper = require("xiaoqiang.XQPushHelper")
|
||||||
|
local LuciJson = require("json")
|
||||||
|
local payload = {
|
||||||
|
["type"] = 6,
|
||||||
|
["data"] = {
|
||||||
|
["lan"] = speed.lan,
|
||||||
|
["wan"] = speed.wan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XQPushHelper.push_request(LuciJson.encode(payload))
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function systemStatus()
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local count = 0
|
||||||
|
local result = {}
|
||||||
|
local status = XQSysUtil.checkSystemStatus()
|
||||||
|
result["code"] = 0
|
||||||
|
result["status"] = 0
|
||||||
|
if (status.cpu and status.cpu > 90) then
|
||||||
|
count = count + 1
|
||||||
|
result["status"] = 1
|
||||||
|
end
|
||||||
|
if (status.mem and status.mem > 90) then
|
||||||
|
count = count + 1
|
||||||
|
result["status"] = 1
|
||||||
|
end
|
||||||
|
if (status.tmp and status.tmp > 70) then
|
||||||
|
count = count + 1
|
||||||
|
result["status"] = 1
|
||||||
|
end
|
||||||
|
if not status.wan or not status.link then
|
||||||
|
count = count + 1
|
||||||
|
result["status"] = 2
|
||||||
|
end
|
||||||
|
result["count"] = count
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function netspeed()
|
||||||
|
local XQPreference = require("xiaoqiang.XQPreference")
|
||||||
|
local XQNSTUtil = require("xiaoqiang.module.XQNetworkSpeedTest")
|
||||||
|
local code = 0
|
||||||
|
local result = {}
|
||||||
|
local history = LuciHttp.formvalue("history")
|
||||||
|
if history then
|
||||||
|
result["bandwidth"] = tonumber(XQPreference.get("BANDWIDTH", 0, "xiaoqiang"))
|
||||||
|
result["download"] = tonumber(string.format("%.2f", 128 * result.bandwidth))
|
||||||
|
else
|
||||||
|
local download = XQNSTUtil.downloadSpeedTest()
|
||||||
|
if download then
|
||||||
|
result["download"] = download
|
||||||
|
result["bandwidth"] = tonumber(string.format("%.2f", 8 * download/1024))
|
||||||
|
XQPreference.set("BANDWIDTH", tostring(result.bandwidth), "xiaoqiang")
|
||||||
|
else
|
||||||
|
code = 1588
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function uploadSpeed()
|
||||||
|
local XQNSTUtil = require("xiaoqiang.module.XQNetworkSpeedTest")
|
||||||
|
local code = 0
|
||||||
|
local result = {}
|
||||||
|
local upload = XQNSTUtil.uploadSpeedTest()
|
||||||
|
if upload then
|
||||||
|
result["upload"] = upload
|
||||||
|
result["bandwidth"] = tonumber(string.format("%.2f", 8 * upload/1024))
|
||||||
|
else
|
||||||
|
code = 1588
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
1310
1_1.mi_Lua/luci/controller/api/xqnetwork.lua
Normal file
1310
1_1.mi_Lua/luci/controller/api/xqnetwork.lua
Normal file
File diff suppressed because it is too large
Load Diff
316
1_1.mi_Lua/luci/controller/api/xqpassport.lua
Normal file
316
1_1.mi_Lua/luci/controller/api/xqpassport.lua
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
module("luci.controller.api.xqpassport", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("api","xqpassport")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = 400
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"api", "xqpassport"}, firstchild(), (""), 400)
|
||||||
|
entry({"api", "xqpassport", "login"}, call("passportLogin"), (""), 401, 0x01)
|
||||||
|
entry({"api", "xqpassport", "userInfo"}, call("getUserInfo"), (""), 402)
|
||||||
|
entry({"api", "xqpassport", "rigister"}, call("routerRegister"), (""), 405, 0x01)
|
||||||
|
entry({"api", "xqpassport", "binded"}, call("getBindInfo"), (""), 406, 0x01)
|
||||||
|
entry({"api", "xqpassport", "plugin_list"}, call("pluginList"), (""), 407)
|
||||||
|
entry({"api", "xqpassport", "plugin_enable"}, call("pluginEnable"), (""), 408)
|
||||||
|
entry({"api", "xqpassport", "plugin_disable"}, call("pluginDisable"), (""), 409)
|
||||||
|
entry({"api", "xqpassport", "plugin_detail"}, call("pluginDetail"), (""), 410)
|
||||||
|
entry({"api", "xqpassport", "unbound"}, call("unboundRouter"), (""), 411)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local XQErrorUtil = require("xiaoqiang.util.XQErrorUtil")
|
||||||
|
|
||||||
|
function getBindInfo()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local force = tonumber(LuciHttp.formvalue("force") or "0")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local bindUUID = XQSysUtil.getPassportBindInfo()
|
||||||
|
if bindUUID then
|
||||||
|
result["bind"] = 1
|
||||||
|
local info = XQSysUtil.getBindUserInfo()
|
||||||
|
if info == nil or force ~= 0 then
|
||||||
|
info = XQNetUtil.getUserInfo(uuid)
|
||||||
|
end
|
||||||
|
if info then
|
||||||
|
if info.miliaoNick and info.miliaoNick ~= "" then
|
||||||
|
info.aliasNick = info.miliaoNick
|
||||||
|
end
|
||||||
|
result["info"] = info
|
||||||
|
else
|
||||||
|
info = {}
|
||||||
|
info["aliasNick"] = bindUUID
|
||||||
|
info["miliaoIcon"] = ""
|
||||||
|
info["miliaoIconOrig"] = ""
|
||||||
|
info["miliaoNick"] = ""
|
||||||
|
info["userId"] = bindUUID
|
||||||
|
result["info"] = info
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result["bind"] = 0
|
||||||
|
end
|
||||||
|
result["routerName"] = XQSysUtil.getRouterName()
|
||||||
|
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function unboundRouter()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local uuid = LuciHttp.formvalue("uuid")
|
||||||
|
local password = LuciHttp.formvalue("password")
|
||||||
|
if uuid == nil or uuid == "" then
|
||||||
|
uuid = XQSysUtil.getBindUUID()
|
||||||
|
end
|
||||||
|
if password ~= nil then
|
||||||
|
local login = XQNetUtil.xiaomiLogin(uuid,password)
|
||||||
|
if login and login.code == 0 then
|
||||||
|
if XQSysUtil.getPassportBindInfo() then
|
||||||
|
local unbound = XQNetUtil.dismissAccount(nil,uuid)
|
||||||
|
if unbound and (tonumber(unbound.code) == 0 or tonumber(unbound.code) == 3001 or tonumber(unbound.code) == 3002) then
|
||||||
|
XQSysUtil.setPassportBound(false,uuid)
|
||||||
|
else
|
||||||
|
code = 1550
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1556
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1557
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
else
|
||||||
|
LuciHttp.header("Set-Cookie", "psp=admin|||2|||0;path=/;")
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function passportLogin()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local uuid = LuciHttp.formvalue("uuid")
|
||||||
|
local password = LuciHttp.formvalue("password")
|
||||||
|
local encrypt = LuciHttp.formvalue("encrypt")
|
||||||
|
local login = XQNetUtil.xiaomiLogin(uuid,password)
|
||||||
|
if login and login.code == 0 then
|
||||||
|
local bindInfo = XQSysUtil.getPassportBindInfo()
|
||||||
|
if bindInfo then
|
||||||
|
if login.uuid == bindInfo then
|
||||||
|
local adminList = XQNetUtil.getAdminList()
|
||||||
|
if adminList and type(adminList) == "table" then
|
||||||
|
if tonumber(adminList.code) == 0 then
|
||||||
|
code = 0
|
||||||
|
LuciHttp.header("Set-Cookie", "psp=" .. login.uuid .. "|||" .. 1 .. "|||" .. login.token .. ";path=/;")
|
||||||
|
elseif tonumber(adminList.code) == 401 then
|
||||||
|
code = 1551
|
||||||
|
else
|
||||||
|
code = 1549
|
||||||
|
XQSysUtil.setPassportBound(false,login.uuid)
|
||||||
|
LuciHttp.header("Set-Cookie", "psp=admin|||2|||0;path=/;")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1551
|
||||||
|
if adminList and adminList.msg then
|
||||||
|
result["errorDetail"] = adminList.msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1548
|
||||||
|
end
|
||||||
|
else
|
||||||
|
XQSysUtil.setBindUUID(login.uuid)
|
||||||
|
end
|
||||||
|
result["token"] = login.token
|
||||||
|
result["uuid"] = login.uuid
|
||||||
|
elseif login and login.code ~= 0 then
|
||||||
|
if login.code == 1 then
|
||||||
|
code = 1564
|
||||||
|
elseif login.code == 2 then
|
||||||
|
code = 1565
|
||||||
|
else
|
||||||
|
code = 1566
|
||||||
|
end
|
||||||
|
else
|
||||||
|
code = 1538
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
local XQFunction = require("xiaoqiang.common.XQFunction")
|
||||||
|
XQFunction.forkExec("/usr/sbin/ntpsetclock 99999 log >/dev/null 2>&1")
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function routerAdminList()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
if not XQSysUtil.getPassportBindInfo() then
|
||||||
|
code = 1542
|
||||||
|
else
|
||||||
|
local admin = XQNetUtil.getAdminList(uuid)
|
||||||
|
if admin and tonumber(admin.code) == 0 then
|
||||||
|
result["list"] = admin.adminList
|
||||||
|
elseif admin and tonumber(admin.code) == 401 then
|
||||||
|
code = 1581
|
||||||
|
else
|
||||||
|
code = 1543
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function routerRegister()
|
||||||
|
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local XQDBUtil = require("xiaoqiang.util.XQDBUtil")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local uuid = LuciHttp.formvalue("uuid")
|
||||||
|
local register = XQNetUtil.routerRegister(uuid)
|
||||||
|
local passport = XQNetUtil.getPassport(uuid)
|
||||||
|
if register and tonumber(register.code) == 0 then
|
||||||
|
result["deviceID"] = register.id
|
||||||
|
XQSysUtil.setPassportBound(true,passport.uuid)
|
||||||
|
else
|
||||||
|
XQSysUtil.setPassportBound(false,nil)
|
||||||
|
code = 1541
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
local XQFunction = require("xiaoqiang.common.XQFunction")
|
||||||
|
XQFunction.forkExec("/usr/sbin/ntpsetclock 99999 log >/dev/null 2>&1")
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
else
|
||||||
|
LuciHttp.header("Set-Cookie", "psp=" .. uuid .. "|||" .. 1 .. "|||" .. passport.token .. ";path=/;")
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getUserInfo()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local result = {}
|
||||||
|
local code = 0
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local info = XQNetUtil.getUserInfo(uuid)
|
||||||
|
if info then
|
||||||
|
result["userInfo"] = info
|
||||||
|
else
|
||||||
|
code = 1539
|
||||||
|
end
|
||||||
|
if code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(code)
|
||||||
|
end
|
||||||
|
result["code"] = code
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginList()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local result = {}
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local pList = XQNetUtil.pluginList(uuid)
|
||||||
|
if pList and tonumber(pList.code) == 0 then
|
||||||
|
result["code"] = 0
|
||||||
|
result["list"] = pList
|
||||||
|
elseif pList and tonumber(pList.code) == 401 then
|
||||||
|
result["code"] = 1581
|
||||||
|
elseif pList and tonumber(pList.code) == 3001 then
|
||||||
|
result["code"] = 1580
|
||||||
|
else
|
||||||
|
result["code"] = 1544
|
||||||
|
end
|
||||||
|
if result.code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
|
||||||
|
end
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginEnable()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local result = {}
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local pluginId = LuciHttp.formvalue("pluginId")
|
||||||
|
local enable = XQNetUtil.pluginEnable(uuid,pluginId)
|
||||||
|
if enable and tonumber(enable.code) == 0 then
|
||||||
|
result["code"] = 0
|
||||||
|
elseif enable and tonumber(enable.code) == 401 then
|
||||||
|
result["code"] = 1581
|
||||||
|
elseif enable and tonumber(enable.code) == 3001 then
|
||||||
|
result["code"] = 1580
|
||||||
|
else
|
||||||
|
result["code"] = 1545
|
||||||
|
end
|
||||||
|
if result.code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
|
||||||
|
end
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginDisable()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local result = {}
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local pluginId = LuciHttp.formvalue("pluginId")
|
||||||
|
local disable = XQNetUtil.pluginDisable(uuid,pluginId)
|
||||||
|
if disable and tonumber(disable.code) == 0 then
|
||||||
|
result["code"] = 0
|
||||||
|
elseif disable and tonumber(disable.code) == 401 then
|
||||||
|
result["code"] = 1581
|
||||||
|
elseif disable and tonumber(disable.code) == 3001 then
|
||||||
|
result["code"] = 1580
|
||||||
|
else
|
||||||
|
result["code"] = 1546
|
||||||
|
end
|
||||||
|
if result.code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
|
||||||
|
end
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pluginDetail()
|
||||||
|
local XQNetUtil = require("xiaoqiang.util.XQNetUtil")
|
||||||
|
local result = {}
|
||||||
|
local uuid = LuciHttp.formvalue("uuid") or ""
|
||||||
|
local pluginId = LuciHttp.formvalue("pluginId")
|
||||||
|
local plugin = XQNetUtil.pluginDetail(uuid,pluginId)
|
||||||
|
if plugin and tonumber(plugin.code) == 0 then
|
||||||
|
result["code"] = 0
|
||||||
|
result["detail"] = plugin
|
||||||
|
elseif plugin and tonumber(plugin.code) == 401 then
|
||||||
|
result["code"] = 1581
|
||||||
|
elseif plugin and tonumber(plugin.code) == 3001 then
|
||||||
|
result["code"] = 1580
|
||||||
|
else
|
||||||
|
result["code"] = 1547
|
||||||
|
end
|
||||||
|
if result.code ~= 0 then
|
||||||
|
result["msg"] = XQErrorUtil.getErrorMessage(result.code)
|
||||||
|
end
|
||||||
|
LuciHttp.write_json(result)
|
||||||
|
end
|
67
1_1.mi_Lua/luci/controller/api/xqsmarthome.lua
Normal file
67
1_1.mi_Lua/luci/controller/api/xqsmarthome.lua
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
module("luci.controller.api.xqsmarthome", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("api","xqsmarthome")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = 500
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"api", "xqsmarthome"}, firstchild(), _(""), 500)
|
||||||
|
entry({"api", "xqsmarthome", "request"}, call("tunnelSmartHomeRequest"), _(""), 501)
|
||||||
|
entry({"api", "xqsmarthome", "request_smartcontroller"}, call("tunnelSmartControllerRequest"), _(""), 502)
|
||||||
|
entry({"api", "xqsmarthome", "request_miio"}, call("tunnelMiioRequest"), _(""), 503)
|
||||||
|
entry({"api", "xqsmarthome", "request_mitv"}, call("requestMitv"), _(""), 504)
|
||||||
|
entry({"api", "xqsmarthome", "request_yeelink"}, call("tunnelYeelink"), _(""), 505)
|
||||||
|
entry({"api", "xqsmarthome", "request_camera"}, call("requestCamera"), _(""), 506)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local XQConfigs = require("xiaoqiang.common.XQConfigs")
|
||||||
|
local XQFunction = require("xiaoqiang.common.XQFunction")
|
||||||
|
|
||||||
|
function tunnelSmartHomeRequest()
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_SMARTHOME % payload
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function tunnelSmartControllerRequest()
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_SMARTHOME_CONTROLLER % payload
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function tunnelMiioRequest()
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_MIIO % payload
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function tunnelYeelink()
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
local payload = XQCryptoUtil.binaryBase64Enc(LuciHttp.formvalue("payload"))
|
||||||
|
-- merge yeelink daemon into miio, so tunnel into miio
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_MIIO % payload
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function requestMitv()
|
||||||
|
local payload = LuciHttp.formvalue("payload");
|
||||||
|
local MitvUtil = require("xiaoqiang.util.XQMitvUtil");
|
||||||
|
LuciHttp.write(MitvUtil.request(payload));
|
||||||
|
end
|
||||||
|
|
||||||
|
function requestCamera()
|
||||||
|
local payload = LuciHttp.formvalue("payload");
|
||||||
|
local CamUtil = require("xiaoqiang.util.XQCameraUtil");
|
||||||
|
LuciHttp.write(CamUtil.request(payload));
|
||||||
|
end
|
1598
1_1.mi_Lua/luci/controller/api/xqsystem.lua
Normal file
1598
1_1.mi_Lua/luci/controller/api/xqsystem.lua
Normal file
File diff suppressed because it is too large
Load Diff
53
1_1.mi_Lua/luci/controller/api/xqtunnel.lua
Normal file
53
1_1.mi_Lua/luci/controller/api/xqtunnel.lua
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
module("luci.controller.api.xqtunnel", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("api","xqtunnel")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = 300
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"api", "xqtunnel", "request"}, call("tunnelRequest"), _(""), 301)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local XQConfigs = require("xiaoqiang.common.XQConfigs")
|
||||||
|
|
||||||
|
local base64chars = {
|
||||||
|
['A']=true,['B']=true,['C']=true,['D']=true,
|
||||||
|
['E']=true,['F']=true,['G']=true,['H']=true,
|
||||||
|
['I']=true,['J']=true,['K']=true,['L']=true,
|
||||||
|
['M']=true,['N']=true,['O']=true,['P']=true,
|
||||||
|
['Q']=true,['R']=true,['S']=true,['T']=true,
|
||||||
|
['U']=true,['V']=true,['W']=true,['X']=true,
|
||||||
|
['Y']=true,['Z']=true,['a']=true,['b']=true,
|
||||||
|
['c']=true,['d']=true,['e']=true,['f']=true,
|
||||||
|
['g']=true,['h']=true,['i']=true,['j']=true,
|
||||||
|
['k']=true,['l']=true,['m']=true,['n']=true,
|
||||||
|
['o']=true,['p']=true,['q']=true,['r']=true,
|
||||||
|
['s']=true,['t']=true,['u']=true,['v']=true,
|
||||||
|
['w']=true,['x']=true,['y']=true,['z']=true,
|
||||||
|
['0']=true,['1']=true,['2']=true,['3']=true,
|
||||||
|
['4']=true,['5']=true,['6']=true,['7']=true,
|
||||||
|
['8']=true,['9']=true,['-']=true,['_']=true,
|
||||||
|
['+']=true,['/']=true,['=']=true
|
||||||
|
}
|
||||||
|
|
||||||
|
local function base64filter(input)
|
||||||
|
local result = ""
|
||||||
|
for i = 1, #input do
|
||||||
|
local c = input:sub(i,i)
|
||||||
|
if base64chars[c] ~= nil and base64chars[c] then
|
||||||
|
result = result .. c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function tunnelRequest()
|
||||||
|
local payload = LuciHttp.formvalue("payloadB64")
|
||||||
|
local cmd = XQConfigs.TUNNEL_TOOL % base64filter(payload)
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
18
1_1.mi_Lua/luci/controller/dispatch/index.lua
Normal file
18
1_1.mi_Lua/luci/controller/dispatch/index.lua
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module("luci.controller.dispatch.index", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local root = node()
|
||||||
|
if not root.target then
|
||||||
|
root.target = alias("dispatch")
|
||||||
|
root.index = true
|
||||||
|
end
|
||||||
|
local page = node("dispatch")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = _("")
|
||||||
|
page.order = 1
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.mediaurlbase = "/xiaoqiang/dispatch"
|
||||||
|
page.sysauth_authenticator = "htmlauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"dispatch"}, template("index"), _("跳转"), 1, 0x09)
|
||||||
|
end
|
23
1_1.mi_Lua/luci/controller/firewall.lua
Normal file
23
1_1.mi_Lua/luci/controller/firewall.lua
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module("luci.controller.firewall", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
-- entry({"admin", "network", "firewall"},
|
||||||
|
-- alias("admin", "network", "firewall", "zones"),
|
||||||
|
-- _("Firewall"), 60)
|
||||||
|
--
|
||||||
|
-- entry({"admin", "network", "firewall", "zones"},
|
||||||
|
-- arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),
|
||||||
|
-- _("General Settings"), 10).leaf = true
|
||||||
|
--
|
||||||
|
-- entry({"admin", "network", "firewall", "forwards"},
|
||||||
|
-- arcombine(cbi("firewall/forwards"), cbi("firewall/forward-details")),
|
||||||
|
-- _("Port Forwards"), 20).leaf = true
|
||||||
|
--
|
||||||
|
-- entry({"admin", "network", "firewall", "rules"},
|
||||||
|
-- arcombine(cbi("firewall/rules"), cbi("firewall/rule-details")),
|
||||||
|
-- _("Traffic Rules"), 30).leaf = true
|
||||||
|
--
|
||||||
|
-- entry({"admin", "network", "firewall", "custom"},
|
||||||
|
-- cbi("firewall/custom"),
|
||||||
|
-- _("Custom Rules"), 40).leaf = true
|
||||||
|
end
|
32
1_1.mi_Lua/luci/controller/mobile/index.lua
Normal file
32
1_1.mi_Lua/luci/controller/mobile/index.lua
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
module("luci.controller.mobile.index", package.seeall)
|
||||||
|
function index()
|
||||||
|
local root = node()
|
||||||
|
if not root.target then
|
||||||
|
root.target = alias("mobile")
|
||||||
|
root.index = true
|
||||||
|
end
|
||||||
|
local page = node("mobile")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = _("")
|
||||||
|
page.order = 110
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.mediaurlbase = "/xiaoqiang/mobile"
|
||||||
|
page.sysauth_authenticator = "htmlauth_moblie"
|
||||||
|
page.index = true
|
||||||
|
entry({"mobile"}, template("mobile/home"), _("首页"), 1, 0x08)
|
||||||
|
entry({"mobile", "logout"}, call("action_logout"), 2, 0x09)
|
||||||
|
entry({"mobile", "hello"}, template("mobile/init/hello"), _("初始化欢迎界面"), 3, 0x09)
|
||||||
|
entry({"mobile", "agreement"}, template("mobile/init/agreement"), _("查看协议"), 4, 0x09)
|
||||||
|
entry({"mobile", "guide"}, template("mobile/init/guide"), _("初始化引导"), 5, 0x08)
|
||||||
|
end
|
||||||
|
|
||||||
|
function action_logout()
|
||||||
|
local dsp = require "luci.dispatcher"
|
||||||
|
local sauth = require "luci.sauth"
|
||||||
|
if dsp.context.authsession then
|
||||||
|
sauth.kill(dsp.context.authsession)
|
||||||
|
dsp.context.urltoken.stok = nil
|
||||||
|
end
|
||||||
|
luci.http.header("Set-Cookie", "sysauth=; path=" .. dsp.build_url())
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url().."/mobile")
|
||||||
|
end
|
382
1_1.mi_Lua/luci/controller/service/datacenter.lua
Normal file
382
1_1.mi_Lua/luci/controller/service/datacenter.lua
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
module("luci.controller.service.datacenter", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local page = node("service","datacenter")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = ("")
|
||||||
|
page.order = nil
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"service", "datacenter", "download_file"}, call("downloadFile"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "device_id"}, call("getDeviceID"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "download_info"}, call("getDownloadInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "upload_file"}, call("uploadFile"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "batch_download_info"}, call("getBatchDownloadInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "config_info"}, call("getConfigInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "set_config"}, call("setConfigInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "plugin_enable"}, call("enablePlugin"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "plugin_download_info"}, call("pluginDownloadInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "plugin_disable"}, call("disablePlugin"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "plugin_control"}, call("controlPlugin"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "download_delete"}, call("deleteDownload"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "get_plugin_status"}, call("pluginStatus"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "get_connected_device"}, call("connectedDevice"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "get_router_mac"}, call("getMac"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "set_wan_access"}, call("setWanAccess"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "get_router_info"}, call("getRouterInfo"), _(""), nil, 0x11)
|
||||||
|
entry({"service", "datacenter", "xunlei_notify"}, call("xunleiNotify"), _(""), nil, 0x11)
|
||||||
|
end
|
||||||
|
|
||||||
|
local LuciHttp = require("luci.http")
|
||||||
|
local XQConfigs = require("xiaoqiang.common.XQConfigs")
|
||||||
|
local ServiceErrorUtil = require("service.util.ServiceErrorUtil")
|
||||||
|
|
||||||
|
function xunleiNotify()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 519
|
||||||
|
payload["info"] = LuciHttp.formvalue("tasks")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tunnelRequestDatacenter(payload)
|
||||||
|
local LuciJson = require("cjson")
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
payload = LuciJson.encode(payload)
|
||||||
|
payload = XQCryptoUtil.binaryBase64Enc(payload)
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
|
||||||
|
LuciHttp.write(LuciUtil.exec(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
function requestDatacenter(payload)
|
||||||
|
local LuciJson = require("cjson")
|
||||||
|
local LuciUtil = require("luci.util")
|
||||||
|
local XQCryptoUtil = require("xiaoqiang.util.XQCryptoUtil")
|
||||||
|
payload = LuciJson.encode(payload)
|
||||||
|
payload = XQCryptoUtil.binaryBase64Enc(payload)
|
||||||
|
local cmd = XQConfigs.THRIFT_TUNNEL_TO_DATACENTER % payload
|
||||||
|
return LuciUtil.exec(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
function downloadFile()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1101
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["path"] = LuciHttp.formvalue("path")
|
||||||
|
payload["url"] = LuciHttp.formvalue("url")
|
||||||
|
payload["name"] = LuciHttp.formvalue("downloadName")
|
||||||
|
payload["tag"] = LuciHttp.formvalue("tag")
|
||||||
|
payload["hidden"] = false
|
||||||
|
if LuciHttp.formvalue("hidden") == "true" then
|
||||||
|
payload["hidden"] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
payload["redownload"] = 0
|
||||||
|
if LuciHttp.formvalue("redownload") == "1" then
|
||||||
|
payload["redownload"] = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
payload["dupId"] = LuciHttp.formvalue("dupId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function setWanAccess()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 618
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["mac"] = LuciHttp.formvalue("mac")
|
||||||
|
payload["enable"] = false
|
||||||
|
if LuciHttp.formvalue("enable") == "true" then
|
||||||
|
payload["enable"] = true
|
||||||
|
end
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getDeviceID()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1103
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getMac()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 617
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getRouterInfo()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 622
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getOperateDeviceID()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1103
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
|
||||||
|
local result = requestDatacenter(payload)
|
||||||
|
if result then
|
||||||
|
local LuciJson = require("cjson")
|
||||||
|
result = LuciJson.decode(result)
|
||||||
|
if result then
|
||||||
|
if result.code == 0 then
|
||||||
|
local deviceid = result["deviceid"]
|
||||||
|
if deviceid then
|
||||||
|
return 0, deviceid
|
||||||
|
end
|
||||||
|
elseif result.code == 5 then
|
||||||
|
return 5, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1559, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function urlEncode(url)
|
||||||
|
if url then
|
||||||
|
url = string.gsub(url, "\n", "\r\n")
|
||||||
|
url = string.gsub(url, "([^0-9a-zA-Z/])",
|
||||||
|
function(c) return string.format ("%%%02X", string.byte(c)) end)
|
||||||
|
end
|
||||||
|
return url
|
||||||
|
end
|
||||||
|
|
||||||
|
function generateUrlFromPath(path)
|
||||||
|
if path then
|
||||||
|
path = urlEncode(path)
|
||||||
|
local url, count = string.gsub (path, "^/userdisk/data/", "http://miwifi.com/api-third-party/download/public/")
|
||||||
|
if count == 1 then
|
||||||
|
return url
|
||||||
|
end
|
||||||
|
|
||||||
|
url, count = string.gsub (path, "^/userdisk/appdata/", "http://miwifi.com/api-third-party/download/private/")
|
||||||
|
if count == 1 then
|
||||||
|
return url
|
||||||
|
end
|
||||||
|
|
||||||
|
url, count = string.gsub (path, "^/extdisks/", "http://miwifi.com/api-third-party/download/extdisks/")
|
||||||
|
if count == 1 then
|
||||||
|
return url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function generateResponseFromCode(code)
|
||||||
|
local response = {}
|
||||||
|
response["code"] = code
|
||||||
|
response["msg"] = ServiceErrorUtil.getErrorMessage(code)
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
function getDownloadInfo()
|
||||||
|
local LuciJson = require("cjson")
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1102
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["deviceId"] = LuciHttp.formvalue("deviceId")
|
||||||
|
payload["downloadId"] = LuciHttp.formvalue("downloadId")
|
||||||
|
payload["hidden"] = false
|
||||||
|
if LuciHttp.formvalue("hidden") == "true" then
|
||||||
|
payload["hidden"] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local response = {}
|
||||||
|
local result = requestDatacenter(payload)
|
||||||
|
if result then
|
||||||
|
result = LuciJson.decode(result)
|
||||||
|
if result and result.code == 0 then
|
||||||
|
local url = generateUrlFromPath(result["path"])
|
||||||
|
if url then
|
||||||
|
response["code"] = result["code"]
|
||||||
|
response["msg"] = result["msg"]
|
||||||
|
response["url"] = url
|
||||||
|
else
|
||||||
|
response = generateResponseFromCode(1559)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response = result
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response = generateResponseFromCode(1559)
|
||||||
|
end
|
||||||
|
|
||||||
|
LuciHttp.write_json(response)
|
||||||
|
LuciHttp.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function uploadFile()
|
||||||
|
local fp
|
||||||
|
local log = require("xiaoqiang.XQLog")
|
||||||
|
local fs = require("luci.fs")
|
||||||
|
local tmpfile = "/userdisk/upload.tmp"
|
||||||
|
if fs.isfile(tmpfile) then
|
||||||
|
fs.unlink(tmpfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
local filename
|
||||||
|
LuciHttp.setfilehandler(
|
||||||
|
function(meta, chunk, eof)
|
||||||
|
if not fp then
|
||||||
|
if meta and meta.name == "file" then
|
||||||
|
fp = io.open(tmpfile, "w")
|
||||||
|
filename = meta.file
|
||||||
|
filename = string.gsub(filename, "+", " ")
|
||||||
|
filename = string.gsub(filename, "%%(%x%x)",
|
||||||
|
function(h)
|
||||||
|
return string.char(tonumber(h, 16))
|
||||||
|
end)
|
||||||
|
filename = filename.gsub(filename, "\r\n", "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if chunk then
|
||||||
|
fp:write(chunk)
|
||||||
|
end
|
||||||
|
if eof then
|
||||||
|
fp:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
local code, deviceId = getOperateDeviceID()
|
||||||
|
if code ~= 0 then
|
||||||
|
return LuciHttp.write_json(generateResponseFromCode(code))
|
||||||
|
end
|
||||||
|
|
||||||
|
local path
|
||||||
|
local appid = LuciHttp.formvalue("appId")
|
||||||
|
local saveType = LuciHttp.formvalue("saveType")
|
||||||
|
if saveType == "public" then
|
||||||
|
path = "/userdisk/data/上传/"
|
||||||
|
elseif saveType == "private" then
|
||||||
|
path = "/userdisk/appdata/" .. appid .. "/"
|
||||||
|
else
|
||||||
|
return LuciHttp.write_json(generateResponseFromCode(3))
|
||||||
|
end
|
||||||
|
fs.mkdir(path, true)
|
||||||
|
|
||||||
|
local savename = fs.basename(filename)
|
||||||
|
if fs.isfile(path .. savename) then
|
||||||
|
local basename = savename
|
||||||
|
local index = basename:match(".+()%.%w+$")
|
||||||
|
if index then
|
||||||
|
basename = basename:sub(1, index - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local extension = savename:match(".+%.(%w+)$")
|
||||||
|
for i = 1, 100, 1 do
|
||||||
|
local tmpname = basename .. "(" .. i .. ")"
|
||||||
|
if extension then
|
||||||
|
tmpname = tmpname .. "." .. extension
|
||||||
|
end
|
||||||
|
if not fs.isfile(path .. tmpname) then
|
||||||
|
savename = tmpname
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local dest = path .. savename
|
||||||
|
log.log("dest=" .. dest)
|
||||||
|
fs.rename(tmpfile, dest)
|
||||||
|
|
||||||
|
local response = {}
|
||||||
|
response["code"] = 0
|
||||||
|
response["url"] = generateUrlFromPath(dest)
|
||||||
|
response["deviceId"] = deviceId
|
||||||
|
response["msg"] = ""
|
||||||
|
LuciHttp.write_json(response)
|
||||||
|
LuciHttp.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function getBatchDownloadInfo()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1105
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["ids"] = LuciHttp.formvalue("ids")
|
||||||
|
payload["hidden"] = false
|
||||||
|
if LuciHttp.formvalue("hidden") == "true" then
|
||||||
|
payload["hidden"] = true
|
||||||
|
end
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getConfigInfo()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1106
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["key"] = LuciHttp.formvalue("key")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function connectedDevice()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 616
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function setConfigInfo()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1107
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["key"] = LuciHttp.formvalue("key")
|
||||||
|
payload["value"] = LuciHttp.formvalue("value")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function enablePlugin()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1108
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["status"] = 5
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function disablePlugin ()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1108
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["status"] = 6
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function controlPlugin()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 600
|
||||||
|
payload["pluginID"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["info"] = LuciHttp.formvalue("info")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function deleteDownload()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1110
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["idList"] = LuciHttp.formvalue("idList")
|
||||||
|
payload["deletefile"] = false
|
||||||
|
if LuciHttp.formvalue("deletefile") == "true" then
|
||||||
|
payload["deletefile"] = true
|
||||||
|
end
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function pluginStatus()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1111
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
||||||
|
function pluginDownloadInfo()
|
||||||
|
local payload = {}
|
||||||
|
payload["api"] = 1109
|
||||||
|
payload["appid"] = LuciHttp.formvalue("appId")
|
||||||
|
payload["hidden"] = false
|
||||||
|
if LuciHttp.formvalue("hidden") == "true" then
|
||||||
|
payload["hidden"] = true
|
||||||
|
end
|
||||||
|
payload["lite"] = false
|
||||||
|
if LuciHttp.formvalue("lite") == "true" then
|
||||||
|
payload["lite"] = true
|
||||||
|
end
|
||||||
|
tunnelRequestDatacenter(payload)
|
||||||
|
end
|
10
1_1.mi_Lua/luci/controller/service/index.lua
Normal file
10
1_1.mi_Lua/luci/controller/service/index.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module("luci.controller.service.index", package.seeall)
|
||||||
|
function index()
|
||||||
|
local page = node("service")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = _("")
|
||||||
|
page.order = nil
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.sysauth_authenticator = "jsonauth"
|
||||||
|
page.index = true
|
||||||
|
end
|
96
1_1.mi_Lua/luci/controller/web/index.lua
Normal file
96
1_1.mi_Lua/luci/controller/web/index.lua
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
module("luci.controller.web.index", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
local root = node()
|
||||||
|
if not root.target then
|
||||||
|
root.target = alias("web")
|
||||||
|
root.index = true
|
||||||
|
end
|
||||||
|
local page = node("web")
|
||||||
|
page.target = firstchild()
|
||||||
|
page.title = _("")
|
||||||
|
page.order = 10
|
||||||
|
page.sysauth = "admin"
|
||||||
|
page.mediaurlbase = "/xiaoqiang/web"
|
||||||
|
page.sysauth_authenticator = "htmlauth"
|
||||||
|
page.index = true
|
||||||
|
entry({"web"}, template("web/index"), _("路由器状态"), 10, 0x08)
|
||||||
|
entry({"web", "home"}, template("web/index"), _("路由器状态"), 70, 0x08)
|
||||||
|
entry({"web", "manager"}, template("web/manager"), _("终端管理"), 71)
|
||||||
|
--entry({"web", "plugin"}, template("web/plugin"), _("插件管理"), 72)
|
||||||
|
--entry({"web", "plugin", "kuaipan"}, template("web/plugins/kuaipan"), _("插件管理_快盘"), 72)
|
||||||
|
--entry({"web", "plugin", "guest"}, template("web/plugins/guest"), _("插件管理_访客"), 72)
|
||||||
|
entry({"web", "logout"}, call("action_logout"), 11, 0x09)
|
||||||
|
entry({"web", "init"}, template("web/init/hello"), _("初始化引导"), 190, 0x09)
|
||||||
|
entry({"web", "init", "hello"}, template("web/init/hello"), _("欢迎界面"), 198, 0x09) --不需要登录
|
||||||
|
entry({"web", "init", "agreement"}, template("web/init/agreement"), _("用户协议"), 198, 0x09) --不需要登录
|
||||||
|
entry({"web", "init", "guide"}, template("web/init/guide"), _("引导模式"), 190, 0x08)
|
||||||
|
|
||||||
|
entry({"web", "netset"}, template("web/netset"), _("路由设置"), 73)
|
||||||
|
entry({"web", "sysset"}, template("web/sysset"), _("路由设置"), 73)
|
||||||
|
entry({"web", "sysset", "passport"}, template("web/setting/passport"), _("路由器权限"), 18)
|
||||||
|
entry({"web", "sysset", "reboot"}, template("web/setting/reboot"), _("重启路由器"), 73)
|
||||||
|
entry({"web", "sysset", "reset"}, template("web/setting/reset"), _("恢复出厂设置"), 73)
|
||||||
|
|
||||||
|
entry({"web", "netset", "wifi"}, template("web/setting/wifi_set"), _("WIFI网络设置"), 20)
|
||||||
|
entry({"web", "netset", "wifi_mini"}, template("web/setting/wifi_set_mini"), _("WIFI网络快捷设置"), 20)
|
||||||
|
entry({"web", "netset", "wifi_pro"}, template("web/setting/wifi_set_pro"), _("WIFI网络高级设置"), 60)
|
||||||
|
entry({"web", "netset", "wifi_txpwr"}, template("web/setting/wifi_txpwr"), _("WIFI强度设置"), 60)
|
||||||
|
entry({"web", "netset", "wifi_filter"}, template("web/setting/wifi_filter"), _("WIFI访问控制"), 60)
|
||||||
|
|
||||||
|
entry({"web", "netset" ,"net_wan"}, template("web/setting/net_wan"), _("网络设置WAN"), 20)
|
||||||
|
entry({"web", "netset", "net_lan"}, template("web/setting/net_lan"), _("网络设置LAN"), 30)
|
||||||
|
entry({"web", "netset", "mac"}, template("web/setting/net_setup_mac"), _("mac 设置"), 40)
|
||||||
|
entry({"web", "netset", "ipmacband"}, template("web/setting/net_ipmacband"), _("mac 设置"), 40)
|
||||||
|
entry({"web", "sysset", "qos_pro"}, template("web/setting/qos_pro"), _("QoS 设置"), 40)
|
||||||
|
|
||||||
|
|
||||||
|
entry({"web", "sysset", "upgrade"}, template("web/setting/upgrade"), _("路由器固件升级"), 198, 0x01)
|
||||||
|
entry({"web", "sysset", "upgrade_manual"}, template("web/setting/upgrade_manual", _("路由器手动升级"), 200))
|
||||||
|
entry({"web", "sysset", "log"}, template("web/setting/log", _("上传日志"), 201))
|
||||||
|
--entry({"web", "sysset", "upload_config"}, template("web/setting/upload_config"), _("上传配置信息"), 202)
|
||||||
|
|
||||||
|
--entry({"web", "setting", "sys_psp"}, template("web/setting/sys_psp"), _("管理小米账号"), 73)
|
||||||
|
entry({"web", "sysset", "sys_status"}, template("web/setting/sys_status"), _("系统状态"), 73)
|
||||||
|
|
||||||
|
entry({"web", "sysset", "diskformat"}, template("web/setting/diskformat"), _("格式化小强盘"), 202)
|
||||||
|
entry({"web", "sysset", "nginx"}, template("web/setting/nginx"), _("关闭NGINX"), 203)
|
||||||
|
entry({"web", "sysset", "upnp"}, template("web/setting/upnp"), _("upnp"), 204)
|
||||||
|
-- entry({"web", "sysset", "lamp"}, template("web/setting/lamp"), _("LAMP Settings"), 204)
|
||||||
|
entry({"web", "sysset", "qos"}, template("web/setting/qos"), _("应用限速"), 204)
|
||||||
|
entry({"web", "sysset", "vpn"}, template("web/setting/vpn"), _("VPN"), 204)
|
||||||
|
|
||||||
|
entry({"web", "sysset", "developer"}, template("web/setting/developer"), _("开发者选项"), 205)
|
||||||
|
entry({"web", "sysset", "dmz"}, template("web/setting/dmz"), _("DMZ"), 205)
|
||||||
|
entry({"web", "sysset", "ddns"}, template("web/setting/ddns"), _("DDNS"), 204)
|
||||||
|
entry({"web", "sysset", "nat"}, template("web/setting/nat"), _("端口转发"), 206)
|
||||||
|
entry({"web", "sysset", "noflushd"}, template("web/setting/noflushd"), _("磁盘休眠"), 207)
|
||||||
|
--entry({"web", "sysset", "predownload"}, template("web/setting/predownload"), _("预下载"), 208)
|
||||||
|
|
||||||
|
entry({"web", "detecte"}, template("web/netdetection"), _("网络检测"), 74, 0x01)
|
||||||
|
entry({"web", "detecte_pro"}, template("web/urldetection"), _("网络高级检测"), 75, 0x01)
|
||||||
|
entry({"web", "xmaccount"}, template("web/xmaccount"), _("小米帐号验证"), 75, 0x01)
|
||||||
|
-- entry({"web", "safeurl"}, call("action_safeurl"), _(""), 75, 0x09)
|
||||||
|
|
||||||
|
entry({"web", "webinitrdr"}, template("web/init/webinitrdr"), _("劫持页面"), 300, 0x09) --不需要登录
|
||||||
|
end
|
||||||
|
function action_logout()
|
||||||
|
local dsp = require "luci.dispatcher"
|
||||||
|
local sauth = require "luci.sauth"
|
||||||
|
if dsp.context.authsession then
|
||||||
|
sauth.kill(dsp.context.authsession)
|
||||||
|
dsp.context.urltoken.stok = nil
|
||||||
|
end
|
||||||
|
luci.http.header("Set-Cookie", "sysauth=; path=" .. dsp.build_url())
|
||||||
|
luci.http.header("Set-Cookie", "autologin_v2=;expires=-1;path=/;")
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url())
|
||||||
|
end
|
||||||
|
|
||||||
|
function action_safeurl()
|
||||||
|
|
||||||
|
local safeUrl = luci.http.formvalue("safeurl")
|
||||||
|
require("luci.template")
|
||||||
|
luci.template.render("web/safeurl", {safeurl=safeUrl})
|
||||||
|
|
||||||
|
end
|
||||||
|
|
BIN
1_1.mi_Lua/luci/datacentertunnel.so
Normal file
BIN
1_1.mi_Lua/luci/datacentertunnel.so
Normal file
Binary file not shown.
37
1_1.mi_Lua/luci/debug.lua
Normal file
37
1_1.mi_Lua/luci/debug.lua
Normal file
@ -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
|
||||||
|
|
1037
1_1.mi_Lua/luci/dispatcher.lua
Normal file
1037
1_1.mi_Lua/luci/dispatcher.lua
Normal file
File diff suppressed because it is too large
Load Diff
244
1_1.mi_Lua/luci/fs.lua
Normal file
244
1_1.mi_Lua/luci/fs.lua
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Filesystem tools
|
||||||
|
|
||||||
|
Description:
|
||||||
|
A module offering often needed filesystem manipulation functions
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: fs.lua 5134 2009-07-24 17:34:40Z Cyrus $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local io = require "io"
|
||||||
|
local os = require "os"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local nutil = require "nixio.util"
|
||||||
|
|
||||||
|
local type = type
|
||||||
|
|
||||||
|
--- LuCI filesystem library.
|
||||||
|
module "luci.fs"
|
||||||
|
|
||||||
|
--- Test for file access permission on given path.
|
||||||
|
-- @class function
|
||||||
|
-- @name access
|
||||||
|
-- @param str String value containing the path
|
||||||
|
-- @return Number containing the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description (if any)
|
||||||
|
-- @return Number containing the os specific errno (if any)
|
||||||
|
access = fs.access
|
||||||
|
|
||||||
|
--- Evaluate given shell glob pattern and return a table containing all matching
|
||||||
|
-- file and directory entries.
|
||||||
|
-- @class function
|
||||||
|
-- @name glob
|
||||||
|
-- @param filename String containing the path of the file to read
|
||||||
|
-- @return Table containing file and directory entries or nil if no matches
|
||||||
|
-- @return String containing the error description (if no matches)
|
||||||
|
-- @return Number containing the os specific errno (if no matches)
|
||||||
|
function glob(...)
|
||||||
|
local iter, code, msg = fs.glob(...)
|
||||||
|
if iter then
|
||||||
|
return nutil.consume(iter)
|
||||||
|
else
|
||||||
|
return nil, code, msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks wheather the given path exists and points to a regular file.
|
||||||
|
-- @param filename String containing the path of the file to test
|
||||||
|
-- @return Boolean indicating wheather given path points to regular file
|
||||||
|
function isfile(filename)
|
||||||
|
return fs.stat(filename, "type") == "reg"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks wheather the given path exists and points to a directory.
|
||||||
|
-- @param dirname String containing the path of the directory to test
|
||||||
|
-- @return Boolean indicating wheather given path points to directory
|
||||||
|
function isdirectory(dirname)
|
||||||
|
return fs.stat(dirname, "type") == "dir"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Read the whole content of the given file into memory.
|
||||||
|
-- @param filename String containing the path of the file to read
|
||||||
|
-- @return String containing the file contents or nil on error
|
||||||
|
-- @return String containing the error message on error
|
||||||
|
readfile = fs.readfile
|
||||||
|
|
||||||
|
--- Write the contents of given string to given file.
|
||||||
|
-- @param filename String containing the path of the file to read
|
||||||
|
-- @param data String containing the data to write
|
||||||
|
-- @return Boolean containing true on success or nil on error
|
||||||
|
-- @return String containing the error message on error
|
||||||
|
writefile = fs.writefile
|
||||||
|
|
||||||
|
--- Copies a file.
|
||||||
|
-- @param source Source file
|
||||||
|
-- @param dest Destination
|
||||||
|
-- @return Boolean containing true on success or nil on error
|
||||||
|
copy = fs.datacopy
|
||||||
|
|
||||||
|
--- Renames a file.
|
||||||
|
-- @param source Source file
|
||||||
|
-- @param dest Destination
|
||||||
|
-- @return Boolean containing true on success or nil on error
|
||||||
|
rename = fs.move
|
||||||
|
|
||||||
|
--- Get the last modification time of given file path in Unix epoch format.
|
||||||
|
-- @param path String containing the path of the file or directory to read
|
||||||
|
-- @return Number containing the epoch time or nil on error
|
||||||
|
-- @return String containing the error description (if any)
|
||||||
|
-- @return Number containing the os specific errno (if any)
|
||||||
|
function mtime(path)
|
||||||
|
return fs.stat(path, "mtime")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the last modification time of given file path in Unix epoch format.
|
||||||
|
-- @param path String containing the path of the file or directory to read
|
||||||
|
-- @param mtime Last modification timestamp
|
||||||
|
-- @param atime Last accessed timestamp
|
||||||
|
-- @return 0 in case of success nil on error
|
||||||
|
-- @return String containing the error description (if any)
|
||||||
|
-- @return Number containing the os specific errno (if any)
|
||||||
|
function utime(path, mtime, atime)
|
||||||
|
return fs.utimes(path, atime, mtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the last element - usually the filename - from the given path with
|
||||||
|
-- the directory component stripped.
|
||||||
|
-- @class function
|
||||||
|
-- @name basename
|
||||||
|
-- @param path String containing the path to strip
|
||||||
|
-- @return String containing the base name of given path
|
||||||
|
-- @see dirname
|
||||||
|
basename = fs.basename
|
||||||
|
|
||||||
|
--- Return the directory component of the given path with the last element
|
||||||
|
-- stripped of.
|
||||||
|
-- @class function
|
||||||
|
-- @name dirname
|
||||||
|
-- @param path String containing the path to strip
|
||||||
|
-- @return String containing the directory component of given path
|
||||||
|
-- @see basename
|
||||||
|
dirname = fs.dirname
|
||||||
|
|
||||||
|
--- Return a table containing all entries of the specified directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name dir
|
||||||
|
-- @param path String containing the path of the directory to scan
|
||||||
|
-- @return Table containing file and directory entries or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
function dir(...)
|
||||||
|
local iter, code, msg = fs.dir(...)
|
||||||
|
if iter then
|
||||||
|
local t = nutil.consume(iter)
|
||||||
|
t[#t+1] = "."
|
||||||
|
t[#t+1] = ".."
|
||||||
|
return t
|
||||||
|
else
|
||||||
|
return nil, code, msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new directory, recursively on demand.
|
||||||
|
-- @param path String with the name or path of the directory to create
|
||||||
|
-- @param recursive Create multiple directory levels (optional, default is true)
|
||||||
|
-- @return Number with the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
function mkdir(path, recursive)
|
||||||
|
return recursive and fs.mkdirr(path) or fs.mkdir(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the given empty directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name rmdir
|
||||||
|
-- @param path String containing the path of the directory to remove
|
||||||
|
-- @return Number with the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
rmdir = fs.rmdir
|
||||||
|
|
||||||
|
local stat_tr = {
|
||||||
|
reg = "regular",
|
||||||
|
dir = "directory",
|
||||||
|
lnk = "link",
|
||||||
|
chr = "character device",
|
||||||
|
blk = "block device",
|
||||||
|
fifo = "fifo",
|
||||||
|
sock = "socket"
|
||||||
|
}
|
||||||
|
--- Get information about given file or directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name stat
|
||||||
|
-- @param path String containing the path of the directory to query
|
||||||
|
-- @return Table containing file or directory properties or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
function stat(path, key)
|
||||||
|
local data, code, msg = fs.stat(path)
|
||||||
|
if data then
|
||||||
|
data.mode = data.modestr
|
||||||
|
data.type = stat_tr[data.type] or "?"
|
||||||
|
end
|
||||||
|
return key and data and data[key] or data, code, msg
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set permissions on given file or directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name chmod
|
||||||
|
-- @param path String containing the path of the directory
|
||||||
|
-- @param perm String containing the permissions to set ([ugoa][+-][rwx])
|
||||||
|
-- @return Number with the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
chmod = fs.chmod
|
||||||
|
|
||||||
|
--- Create a hard- or symlink from given file (or directory) to specified target
|
||||||
|
-- file (or directory) path.
|
||||||
|
-- @class function
|
||||||
|
-- @name link
|
||||||
|
-- @param path1 String containing the source path to link
|
||||||
|
-- @param path2 String containing the destination path for the link
|
||||||
|
-- @param symlink Boolean indicating wheather to create a symlink (optional)
|
||||||
|
-- @return Number with the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
function link(src, dest, sym)
|
||||||
|
return sym and fs.symlink(src, dest) or fs.link(src, dest)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the given file.
|
||||||
|
-- @class function
|
||||||
|
-- @name unlink
|
||||||
|
-- @param path String containing the path of the file to remove
|
||||||
|
-- @return Number with the return code, 0 on sucess or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
unlink = fs.unlink
|
||||||
|
|
||||||
|
--- Retrieve target of given symlink.
|
||||||
|
-- @class function
|
||||||
|
-- @name readlink
|
||||||
|
-- @param path String containing the path of the symlink to read
|
||||||
|
-- @return String containing the link target or nil on error
|
||||||
|
-- @return String containing the error description on error
|
||||||
|
-- @return Number containing the os specific errno on error
|
||||||
|
readlink = fs.readlink
|
336
1_1.mi_Lua/luci/http.lua
Normal file
336
1_1.mi_Lua/luci/http.lua
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - HTTP-Interaction
|
||||||
|
|
||||||
|
Description:
|
||||||
|
HTTP-Header manipulator and form variable preprocessor
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local protocol = require "luci.http.protocol"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local string = require "string"
|
||||||
|
local coroutine = require "coroutine"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
|
local ipairs, pairs, next, type, tostring, error =
|
||||||
|
ipairs, pairs, next, type, tostring, error
|
||||||
|
|
||||||
|
--- LuCI Web Framework high-level HTTP functions.
|
||||||
|
module ("luci.http", package.seeall)
|
||||||
|
|
||||||
|
context = util.threadlocal()
|
||||||
|
|
||||||
|
Request = util.class()
|
||||||
|
function Request.__init__(self, env, sourcein, sinkerr)
|
||||||
|
self.input = sourcein
|
||||||
|
self.error = sinkerr
|
||||||
|
|
||||||
|
|
||||||
|
-- File handler
|
||||||
|
self.filehandler = function() end
|
||||||
|
|
||||||
|
-- HTTP-Message table
|
||||||
|
self.message = {
|
||||||
|
env = env,
|
||||||
|
headers = {},
|
||||||
|
params = protocol.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)
|
||||||
|
local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
|
||||||
|
local p = ";" .. name .. "=(.-);"
|
||||||
|
local i, j, value = c:find(p)
|
||||||
|
return value and urldecode(value)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
function Request._parse_input(self)
|
||||||
|
protocol.parse_message_body(
|
||||||
|
self.input,
|
||||||
|
self.message,
|
||||||
|
self.filehandler
|
||||||
|
)
|
||||||
|
self.parsed_input = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Close the HTTP-Connection.
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Return the request content if the request was of unknown type.
|
||||||
|
-- @return HTTP request body
|
||||||
|
-- @return HTTP request body length
|
||||||
|
function content()
|
||||||
|
return context.request:content()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a certain HTTP input value or a table of all input values.
|
||||||
|
-- @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
|
||||||
|
function formvalue(name, noparse)
|
||||||
|
return context.request:formvalue(name, noparse)
|
||||||
|
end
|
||||||
|
|
||||||
|
function xqformvalue(name, noparse)
|
||||||
|
local XQSecureUtil = require("xiaoqiang.util.XQSecureUtil")
|
||||||
|
local value = context.request:formvalue(name, noparse)
|
||||||
|
return XQSecureUtil.xssCheck(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a table of all HTTP input values with a certain prefix.
|
||||||
|
-- @param prefix Prefix
|
||||||
|
-- @return Table of all HTTP input values with given prefix
|
||||||
|
function formvaluetable(prefix)
|
||||||
|
return context.request:formvaluetable(prefix)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the value of a certain HTTP-Cookie.
|
||||||
|
-- @param name Cookie Name
|
||||||
|
-- @return String containing cookie data
|
||||||
|
function getcookie(name)
|
||||||
|
return context.request:getcookie(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the value of a certain HTTP environment variable
|
||||||
|
-- or the environment table itself.
|
||||||
|
-- @param name Environment variable
|
||||||
|
-- @return HTTP environment value or environment table
|
||||||
|
function getenv(name)
|
||||||
|
return context.request:getenv(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set a handler function for incoming user file uploads.
|
||||||
|
-- @param callback Handler function
|
||||||
|
function setfilehandler(callback)
|
||||||
|
return context.request:setfilehandler(callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send a HTTP-Header.
|
||||||
|
-- @param key Header key
|
||||||
|
-- @param value Header value
|
||||||
|
function header(key, value)
|
||||||
|
if not context.headers then
|
||||||
|
context.headers = {}
|
||||||
|
end
|
||||||
|
context.headers[key:lower()] = value
|
||||||
|
coroutine.yield(2, key, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the mime type of following content data.
|
||||||
|
-- @param mime Mimetype of following content
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Get the RAW HTTP input source
|
||||||
|
-- @return HTTP LTN12 source
|
||||||
|
function source()
|
||||||
|
return context.request.input
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the HTTP status code and status message.
|
||||||
|
-- @param code Status code
|
||||||
|
-- @param message Status message
|
||||||
|
function status(code, message)
|
||||||
|
code = code or 200
|
||||||
|
message = message or "OK"
|
||||||
|
context.status = code
|
||||||
|
coroutine.yield(1, code, message)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
-- @param content Content chunk
|
||||||
|
-- @param src_err Error object from source (optional)
|
||||||
|
-- @see 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
|
||||||
|
|
||||||
|
context.eoh = true
|
||||||
|
coroutine.yield(3)
|
||||||
|
end
|
||||||
|
coroutine.yield(4, content)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Splice data from a filedescriptor to the client.
|
||||||
|
-- @param fp File descriptor
|
||||||
|
-- @param size Bytes to splice (optional)
|
||||||
|
function splice(fd, size)
|
||||||
|
coroutine.yield(6, fd, size)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Redirects the client to a new URL and closes the connection.
|
||||||
|
-- @param url Target URL
|
||||||
|
function redirect(url)
|
||||||
|
status(302, "Found")
|
||||||
|
header("Location", url)
|
||||||
|
close()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a querystring out of a table of key - value pairs.
|
||||||
|
-- @param table Query string source table
|
||||||
|
-- @return Encoded HTTP query string
|
||||||
|
function build_querystring(q)
|
||||||
|
local s = { "?" }
|
||||||
|
|
||||||
|
for k, v in pairs(q) do
|
||||||
|
if #s > 1 then s[#s+1] = "&" end
|
||||||
|
|
||||||
|
s[#s+1] = urldecode(k)
|
||||||
|
s[#s+1] = "="
|
||||||
|
s[#s+1] = urldecode(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(s, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the URL-decoded equivalent of a string.
|
||||||
|
-- @param str URL-encoded string
|
||||||
|
-- @param no_plus Don't decode + to " "
|
||||||
|
-- @return URL-decoded string
|
||||||
|
-- @see urlencode
|
||||||
|
urldecode = protocol.urldecode
|
||||||
|
|
||||||
|
--- Return the URL-encoded equivalent of a string.
|
||||||
|
-- @param str Source string
|
||||||
|
-- @return URL-encoded string
|
||||||
|
-- @see urldecode
|
||||||
|
urlencode = protocol.urlencode
|
||||||
|
|
||||||
|
function writeJsonNoLog(x)
|
||||||
|
if x == nil then
|
||||||
|
write("null")
|
||||||
|
elseif type(x) == "table" then
|
||||||
|
local json = require("luci.json")
|
||||||
|
write(json.encode(x))
|
||||||
|
elseif type(x) == "number" or type(x) == "boolean" then
|
||||||
|
if (x ~= x) then
|
||||||
|
-- NaN is the only value that doesn't equal to itself.
|
||||||
|
write("Number.NaN")
|
||||||
|
else
|
||||||
|
write(tostring(x))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
|
||||||
|
return '\\u%04x' % c:byte(1)
|
||||||
|
end))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send the given data as JSON encoded string.
|
||||||
|
-- @param data Data to send
|
||||||
|
function write_json(x)
|
||||||
|
local XQLog = require("xiaoqiang.XQLog")
|
||||||
|
XQLog.log(7,x)
|
||||||
|
writeJsonNoLog(x)
|
||||||
|
end
|
727
1_1.mi_Lua/luci/http/protocol.lua
Normal file
727
1_1.mi_Lua/luci/http/protocol.lua
Normal file
@ -0,0 +1,727 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
HTTP protocol implementation for LuCI
|
||||||
|
(c) 2008 Freifunk Leipzig / 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: protocol.lua 9195 2012-08-29 13:06:58Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI http protocol class.
|
||||||
|
-- This class contains several functions useful for http message- and content
|
||||||
|
-- decoding and to retrive form data from raw http messages.
|
||||||
|
module("luci.http.protocol", package.seeall)
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
local ltn12 = require("luci.ltn12")
|
||||||
|
|
||||||
|
HTTP_MAX_CONTENT = 1024*32 -- 8 kB maximum content size
|
||||||
|
|
||||||
|
--- Decode an urlencoded string - optionally without decoding
|
||||||
|
-- the "+" sign to " " - and return the decoded string.
|
||||||
|
-- @param str Input string in x-www-urlencoded format
|
||||||
|
-- @param no_plus Don't decode "+" signs to spaces
|
||||||
|
-- @return The decoded string
|
||||||
|
-- @see urlencode
|
||||||
|
function urldecode( str, no_plus )
|
||||||
|
|
||||||
|
local function __chrdec( hex )
|
||||||
|
return string.char( tonumber( hex, 16 ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(str) == "string" then
|
||||||
|
if not no_plus then
|
||||||
|
str = str:gsub( "+", " " )
|
||||||
|
end
|
||||||
|
|
||||||
|
str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
|
||||||
|
end
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
-- @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
|
||||||
|
function urldecode_params( url, tbl )
|
||||||
|
|
||||||
|
local params = tbl or { }
|
||||||
|
|
||||||
|
if url:find("?") then
|
||||||
|
url = url:gsub( "^.+%?([^?]+)", "%1" )
|
||||||
|
end
|
||||||
|
|
||||||
|
for pair in url:gmatch( "[^&;]+" ) do
|
||||||
|
|
||||||
|
-- find key and value
|
||||||
|
local key = urldecode( pair:match("^([^=]+)") )
|
||||||
|
local val = urldecode( pair:match("^[^=]+=(.+)$") )
|
||||||
|
|
||||||
|
-- store
|
||||||
|
if type(key) == "string" and key:len() > 0 then
|
||||||
|
if type(val) ~= "string" then val = "" end
|
||||||
|
|
||||||
|
if not params[key] then
|
||||||
|
params[key] = val
|
||||||
|
elseif type(params[key]) ~= "table" then
|
||||||
|
params[key] = { params[key], val }
|
||||||
|
else
|
||||||
|
table.insert( params[key], val )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return params
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Encode given string to x-www-urlencoded format.
|
||||||
|
-- @param str String to encode
|
||||||
|
-- @return String containing the encoded data
|
||||||
|
-- @see urldecode
|
||||||
|
function urlencode( str )
|
||||||
|
|
||||||
|
local function __chrenc( chr )
|
||||||
|
return string.format(
|
||||||
|
"%%%02x", string.byte( chr )
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(str) == "string" then
|
||||||
|
str = str:gsub(
|
||||||
|
"([^a-zA-Z0-9$_%-%.%+!*'(),])",
|
||||||
|
__chrenc
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
function xqurlencode(str)
|
||||||
|
if (str) then
|
||||||
|
--Ensure all newlines are in CRLF form
|
||||||
|
str = string.gsub (str, "\r?\n", "\r\n")
|
||||||
|
|
||||||
|
--Percent-encode all non-unreserved characters
|
||||||
|
--as per RFC 3986, Section 2.3
|
||||||
|
--(except for space, which gets plus-encoded)
|
||||||
|
str = string.gsub (str, "([^%w%-%.%_%~ ])",
|
||||||
|
function (c) return string.format ("%%%02X", string.byte(c)) end)
|
||||||
|
|
||||||
|
--Convert spaces to plus signs
|
||||||
|
str = string.gsub (str, " ", "+")
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
function xq_urlencode_params( tbl )
|
||||||
|
local enc = ""
|
||||||
|
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
for i, v2 in ipairs(v) do
|
||||||
|
enc = enc .. ( #enc > 0 and "&" or "" ) ..
|
||||||
|
urlencode(k) .. "=" .. xqurlencode(v2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
enc = (enc .. ( #enc > 0 and "&" or "" ) ..
|
||||||
|
urlencode(k) .. "=" .. xqurlencode(v))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return enc
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
-- @param tbl Table with the values
|
||||||
|
-- @return String containing encoded values
|
||||||
|
-- @see urldecode_params
|
||||||
|
function urlencode_params( tbl )
|
||||||
|
local enc = ""
|
||||||
|
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
for i, v2 in ipairs(v) do
|
||||||
|
enc = enc .. ( #enc > 0 and "&" or "" ) ..
|
||||||
|
urlencode(k) .. "=" .. urlencode(v2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
enc = (enc .. ( #enc > 0 and "&" or "" ) ..
|
||||||
|
urlencode(k) .. "=" .. urlencode(v))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return enc
|
||||||
|
end
|
||||||
|
|
||||||
|
-- (Internal function)
|
||||||
|
-- Initialize given parameter and coerce string into table when the parameter
|
||||||
|
-- already exists.
|
||||||
|
-- @param tbl Table where parameter should be created
|
||||||
|
-- @param key Parameter name
|
||||||
|
-- @return Always nil
|
||||||
|
local function __initval( tbl, key )
|
||||||
|
if tbl[key] == nil then
|
||||||
|
tbl[key] = ""
|
||||||
|
elseif type(tbl[key]) == "string" then
|
||||||
|
tbl[key] = { tbl[key], "" }
|
||||||
|
else
|
||||||
|
table.insert( tbl[key], "" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- (Internal function)
|
||||||
|
-- Append given data to given parameter, either by extending the string value
|
||||||
|
-- or by appending it to the last string in the parameter's value table.
|
||||||
|
-- @param tbl Table containing the previously initialized parameter value
|
||||||
|
-- @param key Parameter name
|
||||||
|
-- @param chunk String containing the data to append
|
||||||
|
-- @return Always nil
|
||||||
|
-- @see __initval
|
||||||
|
local function __appendval( tbl, key, chunk )
|
||||||
|
if type(tbl[key]) == "table" then
|
||||||
|
tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
|
||||||
|
else
|
||||||
|
tbl[key] = tbl[key] .. chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- (Internal function)
|
||||||
|
-- Finish the value of given parameter, either by transforming the string value
|
||||||
|
-- or - in the case of multi value parameters - the last element in the
|
||||||
|
-- associated values table.
|
||||||
|
-- @param tbl Table containing the previously initialized parameter value
|
||||||
|
-- @param key Parameter name
|
||||||
|
-- @param handler Function which transforms the parameter value
|
||||||
|
-- @return Always nil
|
||||||
|
-- @see __initval
|
||||||
|
-- @see __appendval
|
||||||
|
local function __finishval( tbl, key, handler )
|
||||||
|
if handler then
|
||||||
|
if type(tbl[key]) == "table" then
|
||||||
|
tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
|
||||||
|
else
|
||||||
|
tbl[key] = handler( tbl[key] )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Table of our process states
|
||||||
|
local process_states = { }
|
||||||
|
|
||||||
|
-- Extract "magic", the first line of a http message.
|
||||||
|
-- Extracts the message type ("get", "post" or "response"), the requested uri
|
||||||
|
-- or the status code if the line descripes a http response.
|
||||||
|
process_states['magic'] = function( msg, chunk, err )
|
||||||
|
|
||||||
|
if chunk ~= nil then
|
||||||
|
-- ignore empty lines before request
|
||||||
|
if #chunk == 0 then
|
||||||
|
return true, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Is it a request?
|
||||||
|
local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
|
||||||
|
|
||||||
|
-- Yup, it is
|
||||||
|
if method then
|
||||||
|
|
||||||
|
msg.type = "request"
|
||||||
|
msg.request_method = method:lower()
|
||||||
|
msg.request_uri = uri
|
||||||
|
msg.http_version = tonumber( http_ver )
|
||||||
|
msg.headers = { }
|
||||||
|
|
||||||
|
-- We're done, next state is header parsing
|
||||||
|
return true, function( chunk )
|
||||||
|
return process_states['headers']( msg, chunk )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Is it a response?
|
||||||
|
else
|
||||||
|
|
||||||
|
local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
|
||||||
|
|
||||||
|
-- Is a response
|
||||||
|
if code then
|
||||||
|
|
||||||
|
msg.type = "response"
|
||||||
|
msg.status_code = code
|
||||||
|
msg.status_message = message
|
||||||
|
msg.http_version = tonumber( http_ver )
|
||||||
|
msg.headers = { }
|
||||||
|
|
||||||
|
-- We're done, next state is header parsing
|
||||||
|
return true, function( chunk )
|
||||||
|
return process_states['headers']( msg, chunk )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Can't handle it
|
||||||
|
return nil, "Invalid HTTP message magic"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Extract headers from given string.
|
||||||
|
process_states['headers'] = function( msg, chunk )
|
||||||
|
|
||||||
|
if chunk ~= nil then
|
||||||
|
|
||||||
|
-- Look for a valid header format
|
||||||
|
local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" )
|
||||||
|
|
||||||
|
if type(hdr) == "string" and hdr:len() > 0 and
|
||||||
|
type(val) == "string" and val:len() > 0
|
||||||
|
then
|
||||||
|
msg.headers[hdr] = val
|
||||||
|
|
||||||
|
-- Valid header line, proceed
|
||||||
|
return true, nil
|
||||||
|
|
||||||
|
elseif #chunk == 0 then
|
||||||
|
-- Empty line, we won't accept data anymore
|
||||||
|
return false, nil
|
||||||
|
else
|
||||||
|
-- Junk data
|
||||||
|
return nil, "Invalid HTTP header received"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil, "Unexpected EOF"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Creates a ltn12 source from the given socket. The source will return it's
|
||||||
|
-- data line by line with the trailing \r\n stripped of.
|
||||||
|
-- @param sock Readable network socket
|
||||||
|
-- @return Ltn12 source function
|
||||||
|
function header_source( sock )
|
||||||
|
return ltn12.source.simplify( function()
|
||||||
|
|
||||||
|
local chunk, err, part = sock:receive("*l")
|
||||||
|
|
||||||
|
-- Line too long
|
||||||
|
if chunk == nil then
|
||||||
|
if err ~= "timeout" then
|
||||||
|
return nil, part
|
||||||
|
and "Line exceeds maximum allowed length"
|
||||||
|
or "Unexpected EOF"
|
||||||
|
else
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Line ok
|
||||||
|
elseif chunk ~= nil then
|
||||||
|
|
||||||
|
-- Strip trailing CR
|
||||||
|
chunk = chunk:gsub("\r$","")
|
||||||
|
|
||||||
|
return chunk, nil
|
||||||
|
end
|
||||||
|
end )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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 withing the given message object. Multiple parameter
|
||||||
|
-- values are stored as tables, ordinary ones as strings.
|
||||||
|
-- If an optional file callback function is given then it is feeded with the
|
||||||
|
-- file contents chunk by chunk and only the extracted file name is stored
|
||||||
|
-- within the params table. The callback function will be called subsequently
|
||||||
|
-- with three arguments:
|
||||||
|
-- o Table containing decoded (name, file) and raw (headers) mime header data
|
||||||
|
-- o String value containing a chunk of the file data
|
||||||
|
-- o Boolean which indicates wheather the current chunk is the last one (eof)
|
||||||
|
-- @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
|
||||||
|
function mimedecode_message_body( src, msg, filecb )
|
||||||
|
|
||||||
|
if msg and msg.env.CONTENT_TYPE then
|
||||||
|
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not msg.mime_boundary then
|
||||||
|
return nil, "Invalid Content-Type found"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local tlen = 0
|
||||||
|
local inhdr = false
|
||||||
|
local field = nil
|
||||||
|
local store = nil
|
||||||
|
local lchunk = nil
|
||||||
|
|
||||||
|
local function parse_headers( chunk, field )
|
||||||
|
|
||||||
|
local stat
|
||||||
|
repeat
|
||||||
|
chunk, stat = chunk:gsub(
|
||||||
|
"^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
|
||||||
|
function(k,v)
|
||||||
|
field.headers[k] = v
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
)
|
||||||
|
until stat == 0
|
||||||
|
|
||||||
|
chunk, stat = chunk:gsub("^\r\n","")
|
||||||
|
|
||||||
|
-- End of headers
|
||||||
|
if stat > 0 then
|
||||||
|
if field.headers["Content-Disposition"] then
|
||||||
|
if field.headers["Content-Disposition"]:match("^form%-data; ") then
|
||||||
|
field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
|
||||||
|
field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not field.headers["Content-Type"] then
|
||||||
|
field.headers["Content-Type"] = "text/plain"
|
||||||
|
end
|
||||||
|
|
||||||
|
if field.name and field.file and filecb then
|
||||||
|
__initval( msg.params, field.name )
|
||||||
|
__appendval( msg.params, field.name, field.file )
|
||||||
|
|
||||||
|
store = filecb
|
||||||
|
elseif field.name then
|
||||||
|
__initval( msg.params, field.name )
|
||||||
|
|
||||||
|
store = function( hdr, buf, eof )
|
||||||
|
__appendval( msg.params, field.name, buf )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
store = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk, true
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunk, false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function snk( chunk )
|
||||||
|
|
||||||
|
tlen = tlen + ( chunk and #chunk or 0 )
|
||||||
|
|
||||||
|
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
|
||||||
|
return nil, "Message body size exceeds Content-Length"
|
||||||
|
end
|
||||||
|
|
||||||
|
if chunk and not lchunk then
|
||||||
|
lchunk = "\r\n" .. chunk
|
||||||
|
|
||||||
|
elseif lchunk then
|
||||||
|
local data = lchunk .. ( chunk or "" )
|
||||||
|
local spos, epos, found
|
||||||
|
|
||||||
|
repeat
|
||||||
|
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
|
||||||
|
|
||||||
|
if not spos then
|
||||||
|
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if spos then
|
||||||
|
local predata = data:sub( 1, spos - 1 )
|
||||||
|
|
||||||
|
if inhdr then
|
||||||
|
predata, eof = parse_headers( predata, field )
|
||||||
|
|
||||||
|
if not eof then
|
||||||
|
return nil, "Invalid MIME section header"
|
||||||
|
elseif not field.name then
|
||||||
|
return nil, "Invalid Content-Disposition header"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if store then
|
||||||
|
store( field, predata, true )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
field = { headers = { } }
|
||||||
|
found = found or true
|
||||||
|
|
||||||
|
data, eof = parse_headers( data:sub( epos + 1, #data ), field )
|
||||||
|
inhdr = not eof
|
||||||
|
end
|
||||||
|
until not spos
|
||||||
|
|
||||||
|
if found then
|
||||||
|
-- We found at least some boundary. Save
|
||||||
|
-- the unparsed remaining data for the
|
||||||
|
-- next chunk.
|
||||||
|
lchunk, data = data, nil
|
||||||
|
else
|
||||||
|
-- There was a complete chunk without a boundary. Parse it as headers or
|
||||||
|
-- append it as data, depending on our current state.
|
||||||
|
if inhdr then
|
||||||
|
lchunk, eof = parse_headers( data, field )
|
||||||
|
inhdr = not eof
|
||||||
|
else
|
||||||
|
-- We're inside data, so append the data. Note that we only append
|
||||||
|
-- lchunk, not all of data, since there is a chance that chunk
|
||||||
|
-- contains half a boundary. Assuming that each chunk is at least the
|
||||||
|
-- boundary in size, this should prevent problems
|
||||||
|
store( field, lchunk, false )
|
||||||
|
lchunk, chunk = chunk, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return ltn12.pump.all( src, snk )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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 withing the given message object. Multiple parameter
|
||||||
|
-- values are stored as tables, ordinary ones as strings.
|
||||||
|
-- @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
|
||||||
|
function urldecode_message_body( src, msg )
|
||||||
|
|
||||||
|
local tlen = 0
|
||||||
|
local lchunk = nil
|
||||||
|
|
||||||
|
local function snk( chunk )
|
||||||
|
|
||||||
|
tlen = tlen + ( chunk and #chunk or 0 )
|
||||||
|
|
||||||
|
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
|
||||||
|
return nil, "Message body size exceeds Content-Length"
|
||||||
|
elseif tlen > HTTP_MAX_CONTENT then
|
||||||
|
return nil, "Message body size exceeds maximum allowed length"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not lchunk and chunk then
|
||||||
|
lchunk = chunk
|
||||||
|
|
||||||
|
elseif lchunk then
|
||||||
|
local data = lchunk .. ( chunk or "&" )
|
||||||
|
local spos, epos
|
||||||
|
|
||||||
|
repeat
|
||||||
|
spos, epos = data:find("^.-[;&]")
|
||||||
|
|
||||||
|
if spos then
|
||||||
|
local pair = data:sub( spos, epos - 1 )
|
||||||
|
local key = pair:match("^(.-)=")
|
||||||
|
local val = pair:match("=([^%s]*)%s*$")
|
||||||
|
|
||||||
|
if key and #key > 0 then
|
||||||
|
__initval( msg.params, key )
|
||||||
|
__appendval( msg.params, key, val )
|
||||||
|
__finishval( msg.params, key, urldecode )
|
||||||
|
else
|
||||||
|
key = "invalid_param"
|
||||||
|
__initval( msg.params, key )
|
||||||
|
__appendval( msg.params, key, pair )
|
||||||
|
__finishval( msg.params, key, urldecode )
|
||||||
|
end
|
||||||
|
|
||||||
|
data = data:sub( epos + 1, #data )
|
||||||
|
end
|
||||||
|
until not spos
|
||||||
|
|
||||||
|
lchunk = data
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return ltn12.pump.all( src, snk )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Try to extract an http message header including information like protocol
|
||||||
|
-- version, message headers and resulting CGI environment variables from the
|
||||||
|
-- given ltn12 source.
|
||||||
|
-- @param src Ltn12 source function
|
||||||
|
-- @return HTTP message object
|
||||||
|
-- @see parse_message_body
|
||||||
|
function parse_message_header( src )
|
||||||
|
|
||||||
|
local ok = true
|
||||||
|
local msg = { }
|
||||||
|
|
||||||
|
local sink = ltn12.sink.simplify(
|
||||||
|
function( chunk )
|
||||||
|
return process_states['magic']( msg, chunk )
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Pump input data...
|
||||||
|
while ok do
|
||||||
|
|
||||||
|
-- get data
|
||||||
|
ok, err = ltn12.pump.step( src, sink )
|
||||||
|
|
||||||
|
-- error
|
||||||
|
if not ok and err then
|
||||||
|
return nil, err
|
||||||
|
|
||||||
|
-- eof
|
||||||
|
elseif not ok then
|
||||||
|
|
||||||
|
-- Process get parameters
|
||||||
|
if ( msg.request_method == "get" or msg.request_method == "post" ) and
|
||||||
|
msg.request_uri:match("?")
|
||||||
|
then
|
||||||
|
msg.params = urldecode_params( msg.request_uri )
|
||||||
|
else
|
||||||
|
msg.params = { }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Populate common environment variables
|
||||||
|
msg.env = {
|
||||||
|
CONTENT_LENGTH = msg.headers['Content-Length'];
|
||||||
|
CONTENT_TYPE = msg.headers['Content-Type'] or msg.headers['Content-type'];
|
||||||
|
REQUEST_METHOD = msg.request_method:upper();
|
||||||
|
REQUEST_URI = msg.request_uri;
|
||||||
|
SCRIPT_NAME = msg.request_uri:gsub("?.+$","");
|
||||||
|
SCRIPT_FILENAME = ""; -- XXX implement me
|
||||||
|
SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version);
|
||||||
|
QUERY_STRING = msg.request_uri:match("?")
|
||||||
|
and msg.request_uri:gsub("^.+?","") or ""
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Populate HTTP_* environment variables
|
||||||
|
for i, hdr in ipairs( {
|
||||||
|
'Accept',
|
||||||
|
'Accept-Charset',
|
||||||
|
'Accept-Encoding',
|
||||||
|
'Accept-Language',
|
||||||
|
'Connection',
|
||||||
|
'Cookie',
|
||||||
|
'Host',
|
||||||
|
'Referer',
|
||||||
|
'User-Agent',
|
||||||
|
} ) do
|
||||||
|
local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
|
||||||
|
local val = msg.headers[hdr]
|
||||||
|
|
||||||
|
msg.env[var] = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
-- @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
|
||||||
|
function parse_message_body( src, msg, filecb )
|
||||||
|
-- Is it multipart/mime ?
|
||||||
|
if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
|
||||||
|
msg.env.CONTENT_TYPE:match("^multipart/form%-data")
|
||||||
|
then
|
||||||
|
|
||||||
|
return mimedecode_message_body( src, msg, filecb )
|
||||||
|
|
||||||
|
-- Is it application/x-www-form-urlencoded ?
|
||||||
|
elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
|
||||||
|
msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
|
||||||
|
then
|
||||||
|
return urldecode_message_body( src, msg, filecb )
|
||||||
|
|
||||||
|
|
||||||
|
-- Unhandled encoding
|
||||||
|
-- If a file callback is given then feed it chunk by chunk, else
|
||||||
|
-- store whole buffer in message.content
|
||||||
|
else
|
||||||
|
|
||||||
|
local sink
|
||||||
|
|
||||||
|
-- If we have a file callback then feed it
|
||||||
|
if type(filecb) == "function" then
|
||||||
|
sink = filecb
|
||||||
|
|
||||||
|
-- ... else append to .content
|
||||||
|
else
|
||||||
|
msg.content = ""
|
||||||
|
msg.content_length = 0
|
||||||
|
|
||||||
|
sink = function( chunk, err )
|
||||||
|
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 err then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Table containing human readable messages for several http status codes.
|
||||||
|
-- @class table
|
||||||
|
statusmsg = {
|
||||||
|
[200] = "OK",
|
||||||
|
[206] = "Partial Content",
|
||||||
|
[301] = "Moved Permanently",
|
||||||
|
[302] = "Found",
|
||||||
|
[304] = "Not Modified",
|
||||||
|
[400] = "Bad Request",
|
||||||
|
[403] = "Forbidden",
|
||||||
|
[404] = "Not Found",
|
||||||
|
[405] = "Method Not Allowed",
|
||||||
|
[408] = "Request Time-out",
|
||||||
|
[411] = "Length Required",
|
||||||
|
[412] = "Precondition Failed",
|
||||||
|
[416] = "Requested range not satisfiable",
|
||||||
|
[500] = "Internal Server Error",
|
||||||
|
[503] = "Server Unavailable",
|
||||||
|
}
|
153
1_1.mi_Lua/luci/http/protocol/conditionals.lua
Normal file
153
1_1.mi_Lua/luci/http/protocol/conditionals.lua
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
HTTP protocol implementation for LuCI - RFC2616 / 14.19, 14.24 - 14.28
|
||||||
|
(c) 2008 Freifunk Leipzig / 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: conditionals.lua 5637 2009-12-20 18:35:05Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI http protocol implementation - HTTP/1.1 bits.
|
||||||
|
-- This class provides basic ETag handling and implements most of the
|
||||||
|
-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
|
||||||
|
module("luci.http.protocol.conditionals", package.seeall)
|
||||||
|
|
||||||
|
local date = require("luci.http.protocol.date")
|
||||||
|
|
||||||
|
|
||||||
|
--- Implement 14.19 / ETag.
|
||||||
|
-- @param stat A file.stat structure
|
||||||
|
-- @return String containing the generated tag suitable for ETag headers
|
||||||
|
function mk_etag( stat )
|
||||||
|
if stat ~= nil then
|
||||||
|
return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 14.24 / If-Match
|
||||||
|
-- Test whether the given message object contains an "If-Match" header and
|
||||||
|
-- compare it against the given stat object.
|
||||||
|
-- @param req HTTP request message object
|
||||||
|
-- @param stat A file.stat object
|
||||||
|
-- @return Boolean indicating whether the precondition is ok
|
||||||
|
-- @return Alternative status code if the precondition failed
|
||||||
|
function if_match( req, stat )
|
||||||
|
local h = req.headers
|
||||||
|
local etag = mk_etag( stat )
|
||||||
|
|
||||||
|
-- Check for matching resource
|
||||||
|
if type(h['If-Match']) == "string" then
|
||||||
|
for ent in h['If-Match']:gmatch("([^, ]+)") do
|
||||||
|
if ( ent == '*' or ent == etag ) and stat ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, 412
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 14.25 / If-Modified-Since
|
||||||
|
-- Test whether the given message object contains an "If-Modified-Since" header
|
||||||
|
-- and compare it against the given stat object.
|
||||||
|
-- @param req HTTP request message object
|
||||||
|
-- @param stat A file.stat object
|
||||||
|
-- @return Boolean indicating whether the precondition is ok
|
||||||
|
-- @return Alternative status code if the precondition failed
|
||||||
|
-- @return Table containing extra HTTP headers if the precondition failed
|
||||||
|
function if_modified_since( req, stat )
|
||||||
|
local h = req.headers
|
||||||
|
|
||||||
|
-- Compare mtimes
|
||||||
|
if type(h['If-Modified-Since']) == "string" then
|
||||||
|
local since = date.to_unix( h['If-Modified-Since'] )
|
||||||
|
|
||||||
|
if stat == nil or since < stat.mtime then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, 304, {
|
||||||
|
["ETag"] = mk_etag( stat );
|
||||||
|
["Date"] = date.to_http( os.time() );
|
||||||
|
["Last-Modified"] = date.to_http( stat.mtime )
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 14.26 / If-None-Match
|
||||||
|
-- Test whether the given message object contains an "If-None-Match" header and
|
||||||
|
-- compare it against the given stat object.
|
||||||
|
-- @param req HTTP request message object
|
||||||
|
-- @param stat A file.stat object
|
||||||
|
-- @return Boolean indicating whether the precondition is ok
|
||||||
|
-- @return Alternative status code if the precondition failed
|
||||||
|
-- @return Table containing extra HTTP headers if the precondition failed
|
||||||
|
function if_none_match( req, stat )
|
||||||
|
local h = req.headers
|
||||||
|
local etag = mk_etag( stat )
|
||||||
|
local method = req.env and req.env.REQUEST_METHOD or "GET"
|
||||||
|
|
||||||
|
-- Check for matching resource
|
||||||
|
if type(h['If-None-Match']) == "string" then
|
||||||
|
for ent in h['If-None-Match']:gmatch("([^, ]+)") do
|
||||||
|
if ( ent == '*' or ent == etag ) and stat ~= nil then
|
||||||
|
if method == "GET" or method == "HEAD" then
|
||||||
|
return false, 304, {
|
||||||
|
["ETag"] = etag;
|
||||||
|
["Date"] = date.to_http( os.time() );
|
||||||
|
["Last-Modified"] = date.to_http( stat.mtime )
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false, 412
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 14.27 / If-Range
|
||||||
|
-- The If-Range header is currently not implemented due to the lack of general
|
||||||
|
-- byte range stuff in luci.http.protocol . This function will always return
|
||||||
|
-- false, 412 to indicate a failed precondition.
|
||||||
|
-- @param req HTTP request message object
|
||||||
|
-- @param stat A file.stat object
|
||||||
|
-- @return Boolean indicating whether the precondition is ok
|
||||||
|
-- @return Alternative status code if the precondition failed
|
||||||
|
function if_range( req, stat )
|
||||||
|
-- Sorry, no subranges (yet)
|
||||||
|
return false, 412
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 14.28 / If-Unmodified-Since
|
||||||
|
-- Test whether the given message object contains an "If-Unmodified-Since"
|
||||||
|
-- header and compare it against the given stat object.
|
||||||
|
-- @param req HTTP request message object
|
||||||
|
-- @param stat A file.stat object
|
||||||
|
-- @return Boolean indicating whether the precondition is ok
|
||||||
|
-- @return Alternative status code if the precondition failed
|
||||||
|
function if_unmodified_since( req, stat )
|
||||||
|
local h = req.headers
|
||||||
|
|
||||||
|
-- Compare mtimes
|
||||||
|
if type(h['If-Unmodified-Since']) == "string" then
|
||||||
|
local since = date.to_unix( h['If-Unmodified-Since'] )
|
||||||
|
|
||||||
|
if stat ~= nil and since <= stat.mtime then
|
||||||
|
return false, 412
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
115
1_1.mi_Lua/luci/http/protocol/date.lua
Normal file
115
1_1.mi_Lua/luci/http/protocol/date.lua
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
HTTP protocol implementation for LuCI - date handling
|
||||||
|
(c) 2008 Freifunk Leipzig / 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: date.lua 3860 2008-12-06 03:18:14Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI http protocol implementation - date helper class.
|
||||||
|
-- This class contains functions to parse, compare and format http dates.
|
||||||
|
module("luci.http.protocol.date", package.seeall)
|
||||||
|
|
||||||
|
require("luci.sys.zoneinfo")
|
||||||
|
|
||||||
|
|
||||||
|
MONTHS = {
|
||||||
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
|
||||||
|
"Sep", "Oct", "Nov", "Dec"
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Return the time offset in seconds between the UTC and given time zone.
|
||||||
|
-- @param tz Symbolic or numeric timezone specifier
|
||||||
|
-- @return Time offset to UTC in seconds
|
||||||
|
function tz_offset(tz)
|
||||||
|
|
||||||
|
if type(tz) == "string" then
|
||||||
|
|
||||||
|
-- check for a numeric identifier
|
||||||
|
local s, v = tz:match("([%+%-])([0-9]+)")
|
||||||
|
if s == '+' then s = 1 else s = -1 end
|
||||||
|
if v then v = tonumber(v) end
|
||||||
|
|
||||||
|
if s and v then
|
||||||
|
return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
|
||||||
|
|
||||||
|
-- lookup symbolic tz
|
||||||
|
elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
|
||||||
|
return luci.sys.zoneinfo.OFFSET[tz:lower()]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bad luck
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Parse given HTTP date string and convert it to unix epoch time.
|
||||||
|
-- @param data String containing the date
|
||||||
|
-- @return Unix epoch time
|
||||||
|
function to_unix(date)
|
||||||
|
|
||||||
|
local wd, day, mon, yr, hr, min, sec, tz = date:match(
|
||||||
|
"([A-Z][a-z][a-z]), ([0-9]+) " ..
|
||||||
|
"([A-Z][a-z][a-z]) ([0-9]+) " ..
|
||||||
|
"([0-9]+):([0-9]+):([0-9]+) " ..
|
||||||
|
"([A-Z0-9%+%-]+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if day and mon and yr and hr and min and sec then
|
||||||
|
-- find month
|
||||||
|
local month = 1
|
||||||
|
for i = 1, 12 do
|
||||||
|
if MONTHS[i] == mon then
|
||||||
|
month = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- convert to epoch time
|
||||||
|
return tz_offset(tz) + os.time( {
|
||||||
|
year = yr,
|
||||||
|
month = month,
|
||||||
|
day = day,
|
||||||
|
hour = hr,
|
||||||
|
min = min,
|
||||||
|
sec = sec
|
||||||
|
} )
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert the given unix epoch time to valid HTTP date string.
|
||||||
|
-- @param time Unix epoch time
|
||||||
|
-- @return String containing the formatted date
|
||||||
|
function to_http(time)
|
||||||
|
return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Compare two dates which can either be unix epoch times or HTTP date strings.
|
||||||
|
-- @param d1 The first date or epoch time to compare
|
||||||
|
-- @param d2 The first date or epoch time to compare
|
||||||
|
-- @return -1 - if d1 is lower then d2
|
||||||
|
-- @return 0 - if both dates are equal
|
||||||
|
-- @return 1 - if d1 is higher then d2
|
||||||
|
function compare(d1, d2)
|
||||||
|
|
||||||
|
if d1:match("[^0-9]") then d1 = to_unix(d1) end
|
||||||
|
if d2:match("[^0-9]") then d2 = to_unix(d2) end
|
||||||
|
|
||||||
|
if d1 == d2 then
|
||||||
|
return 0
|
||||||
|
elseif d1 < d2 then
|
||||||
|
return -1
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
99
1_1.mi_Lua/luci/http/protocol/mime.lua
Normal file
99
1_1.mi_Lua/luci/http/protocol/mime.lua
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
HTTP protocol implementation for LuCI - mime handling
|
||||||
|
(c) 2008 Freifunk Leipzig / 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: mime.lua 5327 2009-09-11 01:55:10Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI http protocol implementation - mime helper class.
|
||||||
|
-- This class provides functions to guess mime types from file extensions and
|
||||||
|
-- vice versa.
|
||||||
|
module("luci.http.protocol.mime", package.seeall)
|
||||||
|
|
||||||
|
require("luci.util")
|
||||||
|
|
||||||
|
--- MIME mapping table containg extension - mimetype relations.
|
||||||
|
-- @class table
|
||||||
|
MIME_TYPES = {
|
||||||
|
["txt"] = "text/plain";
|
||||||
|
["js"] = "text/javascript";
|
||||||
|
["css"] = "text/css";
|
||||||
|
["htm"] = "text/html";
|
||||||
|
["html"] = "text/html";
|
||||||
|
["patch"] = "text/x-patch";
|
||||||
|
["c"] = "text/x-csrc";
|
||||||
|
["h"] = "text/x-chdr";
|
||||||
|
["o"] = "text/x-object";
|
||||||
|
["ko"] = "text/x-object";
|
||||||
|
|
||||||
|
["bmp"] = "image/bmp";
|
||||||
|
["gif"] = "image/gif";
|
||||||
|
["png"] = "image/png";
|
||||||
|
["jpg"] = "image/jpeg";
|
||||||
|
["jpeg"] = "image/jpeg";
|
||||||
|
["svg"] = "image/svg+xml";
|
||||||
|
|
||||||
|
["zip"] = "application/zip";
|
||||||
|
["pdf"] = "application/pdf";
|
||||||
|
["xml"] = "application/xml";
|
||||||
|
["xsl"] = "application/xml";
|
||||||
|
["doc"] = "application/msword";
|
||||||
|
["ppt"] = "application/vnd.ms-powerpoint";
|
||||||
|
["xls"] = "application/vnd.ms-excel";
|
||||||
|
["odt"] = "application/vnd.oasis.opendocument.text";
|
||||||
|
["odp"] = "application/vnd.oasis.opendocument.presentation";
|
||||||
|
["pl"] = "application/x-perl";
|
||||||
|
["sh"] = "application/x-shellscript";
|
||||||
|
["php"] = "application/x-php";
|
||||||
|
["deb"] = "application/x-deb";
|
||||||
|
["iso"] = "application/x-cd-image";
|
||||||
|
["tgz"] = "application/x-compressed-tar";
|
||||||
|
|
||||||
|
["mp3"] = "audio/mpeg";
|
||||||
|
["ogg"] = "audio/x-vorbis+ogg";
|
||||||
|
["wav"] = "audio/x-wav";
|
||||||
|
|
||||||
|
["mpg"] = "video/mpeg";
|
||||||
|
["mpeg"] = "video/mpeg";
|
||||||
|
["avi"] = "video/x-msvideo";
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Extract extension from a filename and return corresponding mime-type or
|
||||||
|
-- "application/octet-stream" if the extension is unknown.
|
||||||
|
-- @param filename The filename for which the mime type is guessed
|
||||||
|
-- @return String containign the determined mime type
|
||||||
|
function to_mime(filename)
|
||||||
|
if type(filename) == "string" then
|
||||||
|
local ext = filename:match("[^%.]+$")
|
||||||
|
|
||||||
|
if ext and MIME_TYPES[ext:lower()] then
|
||||||
|
return MIME_TYPES[ext:lower()]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return "application/octet-stream"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return corresponding extension for a given mime type or nil if the
|
||||||
|
-- given mime-type is unknown.
|
||||||
|
-- @param mimetype The mimetype to retrieve the extension from
|
||||||
|
-- @return String with the extension or nil for unknown type
|
||||||
|
function to_ext(mimetype)
|
||||||
|
if type(mimetype) == "string" then
|
||||||
|
for ext, type in luci.util.kspairs( MIME_TYPES ) do
|
||||||
|
if type == mimetype then
|
||||||
|
return ext
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
104
1_1.mi_Lua/luci/i18n.lua
Normal file
104
1_1.mi_Lua/luci/i18n.lua
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Internationalisation
|
||||||
|
|
||||||
|
Description:
|
||||||
|
A very minimalistic but yet effective internationalisation module
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: i18n.lua 9558 2012-12-18 13:58:22Z jow $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI translation library.
|
||||||
|
module("luci.i18n", package.seeall)
|
||||||
|
require("luci.util")
|
||||||
|
|
||||||
|
local tparser = require "luci.template.parser"
|
||||||
|
|
||||||
|
table = {}
|
||||||
|
i18ndir = luci.util.libpath() .. "/i18n/"
|
||||||
|
loaded = {}
|
||||||
|
context = luci.util.threadlocal()
|
||||||
|
default = "en"
|
||||||
|
|
||||||
|
--- Clear the translation table.
|
||||||
|
function clear()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load a translation and copy its data into the translation table.
|
||||||
|
-- @param file Language file
|
||||||
|
-- @param lang Two-letter language code
|
||||||
|
-- @param force Force reload even if already loaded (optional)
|
||||||
|
-- @return Success status
|
||||||
|
function load(file, lang, force)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load a translation file using the default translation language.
|
||||||
|
-- Alternatively load the translation of the fallback language.
|
||||||
|
-- @param file Language file
|
||||||
|
-- @param force Force reload even if already loaded (optional)
|
||||||
|
function loadc(file, force)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the context default translation language.
|
||||||
|
-- @param lang Two-letter language code
|
||||||
|
function setlanguage(lang)
|
||||||
|
context.lang = lang:gsub("_", "-")
|
||||||
|
context.parent = (context.lang:match("^([a-z][a-z])_"))
|
||||||
|
if not tparser.load_catalog(context.lang, i18ndir) then
|
||||||
|
if context.parent then
|
||||||
|
tparser.load_catalog(context.parent, i18ndir)
|
||||||
|
return context.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return context.lang
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the translated value for a specific translation key.
|
||||||
|
-- @param key Default translation text
|
||||||
|
-- @return Translated string
|
||||||
|
function translate(key)
|
||||||
|
return tparser.translate(key) or key
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the translated value for a specific translation key and use it as sprintf pattern.
|
||||||
|
-- @param key Default translation text
|
||||||
|
-- @param ... Format parameters
|
||||||
|
-- @return Translated and formatted string
|
||||||
|
function translatef(key, ...)
|
||||||
|
return tostring(translate(key)):format(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the translated value for a specific translation key
|
||||||
|
-- and ensure that the returned value is a Lua string value.
|
||||||
|
-- This is the same as calling <code>tostring(translate(...))</code>
|
||||||
|
-- @param key Default translation text
|
||||||
|
-- @return Translated string
|
||||||
|
function string(key)
|
||||||
|
return tostring(translate(key))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the translated value for a specific translation key and use it as sprintf pattern.
|
||||||
|
-- Ensure that the returned value is a Lua string value.
|
||||||
|
-- This is the same as calling <code>tostring(translatef(...))</code>
|
||||||
|
-- @param key Default translation text
|
||||||
|
-- @param ... Format parameters
|
||||||
|
-- @return Translated and formatted string
|
||||||
|
function stringf(key, ...)
|
||||||
|
return tostring(translate(key)):format(...)
|
||||||
|
end
|
BIN
1_1.mi_Lua/luci/i18n/base.en.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/base.en.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/base.zh-cn.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/base.zh-cn.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.ca.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.ca.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.cs.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.cs.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.de.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.de.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.el.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.el.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.es.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.es.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.fr.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.fr.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.hu.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.hu.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.it.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.it.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.ja.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.ja.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.no.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.no.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.pl.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.pl.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.pt-br.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.pt-br.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.pt.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.pt.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.ro.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.ro.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.ru.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.ru.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.uk.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.uk.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.vi.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.vi.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.zh-cn.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.zh-cn.lmo
Normal file
Binary file not shown.
BIN
1_1.mi_Lua/luci/i18n/firewall.zh-tw.lmo
Normal file
BIN
1_1.mi_Lua/luci/i18n/firewall.zh-tw.lmo
Normal file
Binary file not shown.
39
1_1.mi_Lua/luci/init.lua
Normal file
39
1_1.mi_Lua/luci/init.lua
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Main class
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: init.lua 7365 2011-08-13 09:52:50Z jow $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local require = require
|
||||||
|
|
||||||
|
-- Make sure that bitlib is loaded
|
||||||
|
if not _G.bit then
|
||||||
|
_G.bit = require "bit"
|
||||||
|
end
|
||||||
|
|
||||||
|
module "luci"
|
||||||
|
|
||||||
|
local v = require "luci.version"
|
||||||
|
|
||||||
|
__version__ = v.luciversion or "trunk"
|
||||||
|
__appname__ = v.luciname or "LuCI"
|
684
1_1.mi_Lua/luci/ip.lua
Normal file
684
1_1.mi_Lua/luci/ip.lua
Normal file
@ -0,0 +1,684 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
LuCI ip calculation libarary
|
||||||
|
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||||
|
(c) 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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: ip.lua 8142 2012-01-01 15:51:37Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI IP calculation library.
|
||||||
|
module( "luci.ip", package.seeall )
|
||||||
|
|
||||||
|
require "nixio"
|
||||||
|
local bit = nixio.bit
|
||||||
|
local util = require "luci.util"
|
||||||
|
|
||||||
|
--- Boolean; true if system is little endian
|
||||||
|
LITTLE_ENDIAN = not util.bigendian()
|
||||||
|
|
||||||
|
--- Boolean; true if system is big endian
|
||||||
|
BIG_ENDIAN = not LITTLE_ENDIAN
|
||||||
|
|
||||||
|
--- Specifier for IPv4 address family
|
||||||
|
FAMILY_INET4 = 0x04
|
||||||
|
|
||||||
|
--- Specifier for IPv6 address family
|
||||||
|
FAMILY_INET6 = 0x06
|
||||||
|
|
||||||
|
|
||||||
|
local function __bless(x)
|
||||||
|
return setmetatable( x, {
|
||||||
|
__index = luci.ip.cidr,
|
||||||
|
__add = luci.ip.cidr.add,
|
||||||
|
__sub = luci.ip.cidr.sub,
|
||||||
|
__lt = luci.ip.cidr.lower,
|
||||||
|
__eq = luci.ip.cidr.equal,
|
||||||
|
__le =
|
||||||
|
function(...)
|
||||||
|
return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...)
|
||||||
|
end
|
||||||
|
} )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __array16( x, family )
|
||||||
|
local list
|
||||||
|
|
||||||
|
if type(x) == "number" then
|
||||||
|
list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
|
||||||
|
|
||||||
|
elseif type(x) == "string" then
|
||||||
|
if x:find(":") then x = IPv6(x) else x = IPv4(x) end
|
||||||
|
if x then
|
||||||
|
assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
|
||||||
|
list = { unpack(x[2]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif type(x) == "table" and type(x[2]) == "table" then
|
||||||
|
assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
|
||||||
|
list = { unpack(x[2]) }
|
||||||
|
|
||||||
|
elseif type(x) == "table" then
|
||||||
|
list = { unpack(x) }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert( list, "Invalid operand" )
|
||||||
|
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __mask16(bits)
|
||||||
|
return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __not16(bits)
|
||||||
|
return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __maxlen(family)
|
||||||
|
return ( family == FAMILY_INET4 ) and 32 or 128
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __sublen(family)
|
||||||
|
return ( family == FAMILY_INET4 ) and 30 or 127
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Convert given short value to network byte order on little endian hosts
|
||||||
|
-- @param x Unsigned integer value between 0x0000 and 0xFFFF
|
||||||
|
-- @return Byte-swapped value
|
||||||
|
-- @see htonl
|
||||||
|
-- @see ntohs
|
||||||
|
function htons(x)
|
||||||
|
if LITTLE_ENDIAN then
|
||||||
|
return bit.bor(
|
||||||
|
bit.rshift( x, 8 ),
|
||||||
|
bit.band( bit.lshift( x, 8 ), 0xFF00 )
|
||||||
|
)
|
||||||
|
else
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert given long value to network byte order on little endian hosts
|
||||||
|
-- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF
|
||||||
|
-- @return Byte-swapped value
|
||||||
|
-- @see htons
|
||||||
|
-- @see ntohl
|
||||||
|
function htonl(x)
|
||||||
|
if LITTLE_ENDIAN then
|
||||||
|
return bit.bor(
|
||||||
|
bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ),
|
||||||
|
htons( bit.rshift( x, 16 ) )
|
||||||
|
)
|
||||||
|
else
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert given short value to host byte order on little endian hosts
|
||||||
|
-- @class function
|
||||||
|
-- @name ntohs
|
||||||
|
-- @param x Unsigned integer value between 0x0000 and 0xFFFF
|
||||||
|
-- @return Byte-swapped value
|
||||||
|
-- @see htonl
|
||||||
|
-- @see ntohs
|
||||||
|
ntohs = htons
|
||||||
|
|
||||||
|
--- Convert given short value to host byte order on little endian hosts
|
||||||
|
-- @class function
|
||||||
|
-- @name ntohl
|
||||||
|
-- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF
|
||||||
|
-- @return Byte-swapped value
|
||||||
|
-- @see htons
|
||||||
|
-- @see ntohl
|
||||||
|
ntohl = htonl
|
||||||
|
|
||||||
|
|
||||||
|
--- Parse given IPv4 address in dotted quad or CIDR notation. If an optional
|
||||||
|
-- netmask is given as second argument and the IP address is encoded in CIDR
|
||||||
|
-- notation then the netmask parameter takes precedence. If neither a CIDR
|
||||||
|
-- encoded prefix nor a netmask parameter is given, then a prefix length of
|
||||||
|
-- 32 bit is assumed.
|
||||||
|
-- @param address IPv4 address in dotted quad or CIDR notation
|
||||||
|
-- @param netmask IPv4 netmask in dotted quad notation (optional)
|
||||||
|
-- @return luci.ip.cidr instance or nil if given address was invalid
|
||||||
|
-- @see IPv6
|
||||||
|
-- @see Hex
|
||||||
|
function IPv4(address, netmask)
|
||||||
|
address = address or "0.0.0.0/0"
|
||||||
|
|
||||||
|
local obj = __bless({ FAMILY_INET4 })
|
||||||
|
|
||||||
|
local data = {}
|
||||||
|
local prefix = address:match("/(.+)")
|
||||||
|
address = address:gsub("/.+","")
|
||||||
|
address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
|
||||||
|
|
||||||
|
if netmask then
|
||||||
|
prefix = obj:prefix(netmask)
|
||||||
|
elseif prefix then
|
||||||
|
prefix = tonumber(prefix)
|
||||||
|
if not prefix or prefix < 0 or prefix > 32 then return nil end
|
||||||
|
else
|
||||||
|
prefix = 32
|
||||||
|
end
|
||||||
|
|
||||||
|
local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||||
|
|
||||||
|
b1 = tonumber(b1)
|
||||||
|
b2 = tonumber(b2)
|
||||||
|
b3 = tonumber(b3)
|
||||||
|
b4 = tonumber(b4)
|
||||||
|
|
||||||
|
if b1 and b1 <= 255 and
|
||||||
|
b2 and b2 <= 255 and
|
||||||
|
b3 and b3 <= 255 and
|
||||||
|
b4 and b4 <= 255 and
|
||||||
|
prefix
|
||||||
|
then
|
||||||
|
table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 })
|
||||||
|
table.insert(obj, prefix)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Parse given IPv6 address in full, compressed, mixed or CIDR notation.
|
||||||
|
-- If an optional netmask is given as second argument and the IP address is
|
||||||
|
-- encoded in CIDR notation then the netmask parameter takes precedence.
|
||||||
|
-- If neither a CIDR encoded prefix nor a netmask parameter is given, then a
|
||||||
|
-- prefix length of 128 bit is assumed.
|
||||||
|
-- @param address IPv6 address in full/compressed/mixed or CIDR notation
|
||||||
|
-- @param netmask IPv6 netmask in full/compressed/mixed notation (optional)
|
||||||
|
-- @return luci.ip.cidr instance or nil if given address was invalid
|
||||||
|
-- @see IPv4
|
||||||
|
-- @see Hex
|
||||||
|
function IPv6(address, netmask)
|
||||||
|
address = address or "::/0"
|
||||||
|
|
||||||
|
local obj = __bless({ FAMILY_INET6 })
|
||||||
|
|
||||||
|
local data = {}
|
||||||
|
local prefix = address:match("/(.+)")
|
||||||
|
address = address:gsub("/.+","")
|
||||||
|
address = address:gsub("^%[(.*)%]$", "%1")
|
||||||
|
|
||||||
|
if netmask then
|
||||||
|
prefix = obj:prefix(netmask)
|
||||||
|
elseif prefix then
|
||||||
|
prefix = tonumber(prefix)
|
||||||
|
if not prefix or prefix < 0 or prefix > 128 then return nil end
|
||||||
|
else
|
||||||
|
prefix = 128
|
||||||
|
end
|
||||||
|
|
||||||
|
local borderl = address:sub(1, 1) == ":" and 2 or 1
|
||||||
|
local borderh, zeroh, chunk, block, i
|
||||||
|
|
||||||
|
if #address > 45 then return nil end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
borderh = address:find(":", borderl, true)
|
||||||
|
if not borderh then break end
|
||||||
|
|
||||||
|
block = tonumber(address:sub(borderl, borderh - 1), 16)
|
||||||
|
if block and block <= 0xFFFF then
|
||||||
|
data[#data+1] = block
|
||||||
|
else
|
||||||
|
if zeroh or borderh - borderl > 1 then return nil end
|
||||||
|
zeroh = #data + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
borderl = borderh + 1
|
||||||
|
until #data == 7
|
||||||
|
|
||||||
|
chunk = address:sub(borderl)
|
||||||
|
if #chunk > 0 and #chunk <= 4 then
|
||||||
|
block = tonumber(chunk, 16)
|
||||||
|
if not block or block > 0xFFFF then return nil end
|
||||||
|
|
||||||
|
data[#data+1] = block
|
||||||
|
elseif #chunk > 4 then
|
||||||
|
if #data == 7 or #chunk > 15 then return nil end
|
||||||
|
borderl = 1
|
||||||
|
for i=1, 4 do
|
||||||
|
borderh = chunk:find(".", borderl, true)
|
||||||
|
if not borderh and i < 4 then return nil end
|
||||||
|
borderh = borderh and borderh - 1
|
||||||
|
|
||||||
|
block = tonumber(chunk:sub(borderl, borderh))
|
||||||
|
if not block or block > 255 then return nil end
|
||||||
|
|
||||||
|
if i == 1 or i == 3 then
|
||||||
|
data[#data+1] = block * 256
|
||||||
|
else
|
||||||
|
data[#data] = data[#data] + block
|
||||||
|
end
|
||||||
|
|
||||||
|
borderl = borderh and borderh + 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if zeroh then
|
||||||
|
if #data == 8 then return nil end
|
||||||
|
while #data < 8 do
|
||||||
|
table.insert(data, zeroh, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #data == 8 and prefix then
|
||||||
|
table.insert(obj, data)
|
||||||
|
table.insert(obj, prefix)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Transform given hex-encoded value to luci.ip.cidr instance of specified
|
||||||
|
-- address family.
|
||||||
|
-- @param hex String containing hex encoded value
|
||||||
|
-- @param prefix Prefix length of CIDR instance (optional, default is 32/128)
|
||||||
|
-- @param family Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6
|
||||||
|
-- @param swap Bool indicating whether to swap byteorder on low endian host
|
||||||
|
-- @return luci.ip.cidr instance or nil if given value was invalid
|
||||||
|
-- @see IPv4
|
||||||
|
-- @see IPv6
|
||||||
|
function Hex( hex, prefix, family, swap )
|
||||||
|
family = ( family ~= nil ) and family or FAMILY_INET4
|
||||||
|
swap = ( swap == nil ) and true or swap
|
||||||
|
prefix = prefix or __maxlen(family)
|
||||||
|
|
||||||
|
local len = __maxlen(family)
|
||||||
|
local tmp = ""
|
||||||
|
local data = { }
|
||||||
|
local i
|
||||||
|
|
||||||
|
for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
|
||||||
|
|
||||||
|
if swap and LITTLE_ENDIAN then
|
||||||
|
for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end
|
||||||
|
else
|
||||||
|
tmp = tmp .. hex
|
||||||
|
end
|
||||||
|
|
||||||
|
hex = tmp
|
||||||
|
|
||||||
|
for i = 1, ( len / 4 ), 4 do
|
||||||
|
local n = tonumber( hex:sub( i, i+3 ), 16 )
|
||||||
|
if n then
|
||||||
|
data[#data+1] = n
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return __bless({ family, data, prefix })
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI IP Library / CIDR instances
|
||||||
|
-- @class module
|
||||||
|
-- @cstyle instance
|
||||||
|
-- @name luci.ip.cidr
|
||||||
|
cidr = util.class()
|
||||||
|
|
||||||
|
--- Test whether the instance is a IPv4 address.
|
||||||
|
-- @return Boolean indicating a IPv4 address type
|
||||||
|
-- @see cidr.is6
|
||||||
|
function cidr.is4( self )
|
||||||
|
return self[1] == FAMILY_INET4
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether this instance is an IPv4 RFC1918 private address
|
||||||
|
-- @return Boolean indicating whether this instance is an RFC1918 address
|
||||||
|
function cidr.is4rfc1918( self )
|
||||||
|
if self[1] == FAMILY_INET4 then
|
||||||
|
return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
|
||||||
|
((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
|
||||||
|
(self[2][1] == 0xC0A8)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether this instance is an IPv4 link-local address (Zeroconf)
|
||||||
|
-- @return Boolean indicating whether this instance is IPv4 link-local
|
||||||
|
function cidr.is4linklocal( self )
|
||||||
|
if self[1] == FAMILY_INET4 then
|
||||||
|
return (self[2][1] == 0xA9FE)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether the instance is a IPv6 address.
|
||||||
|
-- @return Boolean indicating a IPv6 address type
|
||||||
|
-- @see cidr.is4
|
||||||
|
function cidr.is6( self )
|
||||||
|
return self[1] == FAMILY_INET6
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether this instance is an IPv6 link-local address
|
||||||
|
-- @return Boolean indicating whether this instance is IPv6 link-local
|
||||||
|
function cidr.is6linklocal( self )
|
||||||
|
if self[1] == FAMILY_INET6 then
|
||||||
|
return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a corresponding string representation of the instance.
|
||||||
|
-- If the prefix length is lower then the maximum possible prefix length for the
|
||||||
|
-- corresponding address type then the address is returned in CIDR notation,
|
||||||
|
-- otherwise the prefix will be left out.
|
||||||
|
function cidr.string( self )
|
||||||
|
local str
|
||||||
|
if self:is4() then
|
||||||
|
str = string.format(
|
||||||
|
"%d.%d.%d.%d",
|
||||||
|
bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF),
|
||||||
|
bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF)
|
||||||
|
)
|
||||||
|
if self[3] < 32 then
|
||||||
|
str = str .. "/" .. self[3]
|
||||||
|
end
|
||||||
|
elseif self:is6() then
|
||||||
|
str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) )
|
||||||
|
if self[3] < 128 then
|
||||||
|
str = str .. "/" .. self[3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether the value of the instance is lower then the given address.
|
||||||
|
-- This function will throw an exception if the given address has a different
|
||||||
|
-- family than this instance.
|
||||||
|
-- @param addr A luci.ip.cidr instance to compare against
|
||||||
|
-- @return Boolean indicating whether this instance is lower
|
||||||
|
-- @see cidr.higher
|
||||||
|
-- @see cidr.equal
|
||||||
|
function cidr.lower( self, addr )
|
||||||
|
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
|
||||||
|
local i
|
||||||
|
for i = 1, #self[2] do
|
||||||
|
if self[2][i] ~= addr[2][i] then
|
||||||
|
return self[2][i] < addr[2][i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether the value of the instance is higher then the given address.
|
||||||
|
-- This function will throw an exception if the given address has a different
|
||||||
|
-- family than this instance.
|
||||||
|
-- @param addr A luci.ip.cidr instance to compare against
|
||||||
|
-- @return Boolean indicating whether this instance is higher
|
||||||
|
-- @see cidr.lower
|
||||||
|
-- @see cidr.equal
|
||||||
|
function cidr.higher( self, addr )
|
||||||
|
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
|
||||||
|
local i
|
||||||
|
for i = 1, #self[2] do
|
||||||
|
if self[2][i] ~= addr[2][i] then
|
||||||
|
return self[2][i] > addr[2][i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether the value of the instance is equal to the given address.
|
||||||
|
-- This function will throw an exception if the given address is a different
|
||||||
|
-- family than this instance.
|
||||||
|
-- @param addr A luci.ip.cidr instance to compare against
|
||||||
|
-- @return Boolean indicating whether this instance is equal
|
||||||
|
-- @see cidr.lower
|
||||||
|
-- @see cidr.higher
|
||||||
|
function cidr.equal( self, addr )
|
||||||
|
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
|
||||||
|
local i
|
||||||
|
for i = 1, #self[2] do
|
||||||
|
if self[2][i] ~= addr[2][i] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the prefix length of this CIDR instance.
|
||||||
|
-- @param mask Override instance prefix with given netmask (optional)
|
||||||
|
-- @return Prefix length in bit
|
||||||
|
function cidr.prefix( self, mask )
|
||||||
|
local prefix = self[3]
|
||||||
|
|
||||||
|
if mask then
|
||||||
|
prefix = 0
|
||||||
|
|
||||||
|
local stop = false
|
||||||
|
local obj = type(mask) ~= "table"
|
||||||
|
and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
|
||||||
|
|
||||||
|
if not obj then return nil end
|
||||||
|
|
||||||
|
local _, word
|
||||||
|
for _, word in ipairs(obj[2]) do
|
||||||
|
if word == 0xFFFF then
|
||||||
|
prefix = prefix + 16
|
||||||
|
else
|
||||||
|
local bitmask = bit.lshift(1, 15)
|
||||||
|
while bit.band(word, bitmask) == bitmask do
|
||||||
|
prefix = prefix + 1
|
||||||
|
bitmask = bit.lshift(1, 15 - (prefix % 16))
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a corresponding CIDR representing the network address of this
|
||||||
|
-- instance.
|
||||||
|
-- @param bits Override prefix length of this instance (optional)
|
||||||
|
-- @return CIDR instance containing the network address
|
||||||
|
-- @see cidr.host
|
||||||
|
-- @see cidr.broadcast
|
||||||
|
-- @see cidr.mask
|
||||||
|
function cidr.network( self, bits )
|
||||||
|
local data = { }
|
||||||
|
bits = bits or self[3]
|
||||||
|
|
||||||
|
local i
|
||||||
|
for i = 1, math.floor( bits / 16 ) do
|
||||||
|
data[#data+1] = self[2][i]
|
||||||
|
end
|
||||||
|
|
||||||
|
if #data < #self[2] then
|
||||||
|
data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
|
||||||
|
|
||||||
|
for i = #data + 1, #self[2] do
|
||||||
|
data[#data+1] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return __bless({ self[1], data, __maxlen(self[1]) })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a corresponding CIDR representing the host address of this
|
||||||
|
-- instance. This is intended to extract the host address from larger subnet.
|
||||||
|
-- @return CIDR instance containing the network address
|
||||||
|
-- @see cidr.network
|
||||||
|
-- @see cidr.broadcast
|
||||||
|
-- @see cidr.mask
|
||||||
|
function cidr.host( self )
|
||||||
|
return __bless({ self[1], self[2], __maxlen(self[1]) })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a corresponding CIDR representing the netmask of this instance.
|
||||||
|
-- @param bits Override prefix length of this instance (optional)
|
||||||
|
-- @return CIDR instance containing the netmask
|
||||||
|
-- @see cidr.network
|
||||||
|
-- @see cidr.host
|
||||||
|
-- @see cidr.broadcast
|
||||||
|
function cidr.mask( self, bits )
|
||||||
|
local data = { }
|
||||||
|
bits = bits or self[3]
|
||||||
|
|
||||||
|
for i = 1, math.floor( bits / 16 ) do
|
||||||
|
data[#data+1] = 0xFFFF
|
||||||
|
end
|
||||||
|
|
||||||
|
if #data < #self[2] then
|
||||||
|
data[#data+1] = __mask16(bits)
|
||||||
|
|
||||||
|
for i = #data + 1, #self[2] do
|
||||||
|
data[#data+1] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return __bless({ self[1], data, __maxlen(self[1]) })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return CIDR containing the broadcast address of this instance.
|
||||||
|
-- @return CIDR instance containing the netmask, always nil for IPv6
|
||||||
|
-- @see cidr.network
|
||||||
|
-- @see cidr.host
|
||||||
|
-- @see cidr.mask
|
||||||
|
function cidr.broadcast( self )
|
||||||
|
-- IPv6 has no broadcast addresses (XXX: assert() instead?)
|
||||||
|
if self[1] == FAMILY_INET4 then
|
||||||
|
local data = { unpack(self[2]) }
|
||||||
|
local offset = math.floor( self[3] / 16 ) + 1
|
||||||
|
|
||||||
|
if offset <= #data then
|
||||||
|
data[offset] = bit.bor( data[offset], __not16(self[3]) )
|
||||||
|
for i = offset + 1, #data do data[i] = 0xFFFF end
|
||||||
|
|
||||||
|
return __bless({ self[1], data, __maxlen(self[1]) })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether this instance fully contains the given CIDR instance.
|
||||||
|
-- @param addr CIDR instance to test against
|
||||||
|
-- @return Boolean indicating whether this instance contains the given CIDR
|
||||||
|
function cidr.contains( self, addr )
|
||||||
|
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
|
||||||
|
|
||||||
|
if self:prefix() <= addr:prefix() then
|
||||||
|
return self:network() == addr:network(self:prefix())
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add specified amount of hosts to this instance.
|
||||||
|
-- @param amount Number of hosts to add to this instance
|
||||||
|
-- @param inplace Boolen indicating whether to alter values inplace (optional)
|
||||||
|
-- @return CIDR representing the new address or nil on overflow error
|
||||||
|
-- @see cidr.sub
|
||||||
|
function cidr.add( self, amount, inplace )
|
||||||
|
local pos
|
||||||
|
local data = { unpack(self[2]) }
|
||||||
|
local shorts = __array16( amount, self[1] )
|
||||||
|
|
||||||
|
for pos = #data, 1, -1 do
|
||||||
|
local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
|
||||||
|
if ( data[pos] + add ) > 0xFFFF then
|
||||||
|
data[pos] = ( data[pos] + add ) % 0xFFFF
|
||||||
|
if pos > 1 then
|
||||||
|
data[pos-1] = data[pos-1] + ( add - data[pos] )
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
data[pos] = data[pos] + add
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if inplace then
|
||||||
|
self[2] = data
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
return __bless({ self[1], data, self[3] })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Substract specified amount of hosts from this instance.
|
||||||
|
-- @param amount Number of hosts to substract from this instance
|
||||||
|
-- @param inplace Boolen indicating whether to alter values inplace (optional)
|
||||||
|
-- @return CIDR representing the new address or nil on underflow error
|
||||||
|
-- @see cidr.add
|
||||||
|
function cidr.sub( self, amount, inplace )
|
||||||
|
local pos
|
||||||
|
local data = { unpack(self[2]) }
|
||||||
|
local shorts = __array16( amount, self[1] )
|
||||||
|
|
||||||
|
for pos = #data, 1, -1 do
|
||||||
|
local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
|
||||||
|
if ( data[pos] - sub ) < 0 then
|
||||||
|
data[pos] = ( sub - data[pos] ) % 0xFFFF
|
||||||
|
if pos > 1 then
|
||||||
|
data[pos-1] = data[pos-1] - ( sub + data[pos] )
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
data[pos] = data[pos] - sub
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if inplace then
|
||||||
|
self[2] = data
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
return __bless({ self[1], data, self[3] })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return CIDR containing the lowest available host address within this subnet.
|
||||||
|
-- @return CIDR containing the host address, nil if subnet is too small
|
||||||
|
-- @see cidr.maxhost
|
||||||
|
function cidr.minhost( self )
|
||||||
|
if self[3] <= __sublen(self[1]) then
|
||||||
|
-- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
|
||||||
|
return self:network():add(1, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return CIDR containing the highest available host address within the subnet.
|
||||||
|
-- @return CIDR containing the host address, nil if subnet is too small
|
||||||
|
-- @see cidr.minhost
|
||||||
|
function cidr.maxhost( self )
|
||||||
|
if self[3] <= __sublen(self[1]) then
|
||||||
|
local i
|
||||||
|
local data = { unpack(self[2]) }
|
||||||
|
local offset = math.floor( self[3] / 16 ) + 1
|
||||||
|
|
||||||
|
data[offset] = bit.bor( data[offset], __not16(self[3]) )
|
||||||
|
for i = offset + 1, #data do data[i] = 0xFFFF end
|
||||||
|
data = __bless({ self[1], data, __maxlen(self[1]) })
|
||||||
|
|
||||||
|
-- Last address in reserved for Broadcast Address in IPv4
|
||||||
|
if data[1] == FAMILY_INET4 then data:sub(1, true) end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function iptonl( ip )
|
||||||
|
local a,b,c,d = ip:match("^(%d+).(%d+).(%d+).(%d+)")
|
||||||
|
local ipnl = bit.lshift(a, 24) + bit.lshift(b, 16) + bit.lshift(c, 8) + d
|
||||||
|
return ipnl
|
||||||
|
end
|
||||||
|
|
||||||
|
function ipnot ( ip )
|
||||||
|
local ipnl = iptonl(ip)
|
||||||
|
return bit.band(bit.bnot(ipnl),0xFFFFFFFF)
|
||||||
|
end
|
566
1_1.mi_Lua/luci/json.lua
Normal file
566
1_1.mi_Lua/luci/json.lua
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
--[[
|
||||||
|
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: json.lua 6988 2011-04-17 11:39:17Z jow $
|
||||||
|
|
||||||
|
Decoder:
|
||||||
|
Info:
|
||||||
|
null will be decoded to luci.json.null if first parameter of Decoder() is true
|
||||||
|
|
||||||
|
Example:
|
||||||
|
decoder = luci.json.Decoder()
|
||||||
|
luci.ltn12.pump.all(luci.ltn12.source.string("decodableJSON"), decoder:sink())
|
||||||
|
luci.util.dumptable(decoder:get())
|
||||||
|
|
||||||
|
Known issues:
|
||||||
|
does not support unicode conversion \uXXYY with XX != 00 will be ignored
|
||||||
|
|
||||||
|
|
||||||
|
Encoder:
|
||||||
|
Info:
|
||||||
|
Accepts numbers, strings, nil, booleans as they are
|
||||||
|
Accepts luci.json.null as replacement for nil
|
||||||
|
Accepts full associative and full numerically indexed tables
|
||||||
|
Mixed tables will loose their associative values during conversion
|
||||||
|
Iterator functions will be encoded as an array of their return values
|
||||||
|
Non-iterator functions will probably corrupt the encoder
|
||||||
|
|
||||||
|
Example:
|
||||||
|
encoder = luci.json.Encoder(encodableData)
|
||||||
|
luci.ltn12.pump.all(encoder:source(), luci.ltn12.sink.file(io.open("someFile", w)))
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
local table = require "table"
|
||||||
|
local string = require "string"
|
||||||
|
local coroutine = require "coroutine"
|
||||||
|
|
||||||
|
local assert = assert
|
||||||
|
local tonumber = tonumber
|
||||||
|
local tostring = tostring
|
||||||
|
local error = error
|
||||||
|
local type = type
|
||||||
|
local pairs = pairs
|
||||||
|
local ipairs = ipairs
|
||||||
|
local next = next
|
||||||
|
local pcall = pcall
|
||||||
|
|
||||||
|
local getmetatable = getmetatable
|
||||||
|
|
||||||
|
--- LuCI JSON-Library
|
||||||
|
-- @cstyle instance
|
||||||
|
module "luci.json"
|
||||||
|
|
||||||
|
|
||||||
|
--- Directly decode a JSON string
|
||||||
|
-- @param json JSON-String
|
||||||
|
-- @return Lua object
|
||||||
|
function decode(json, ...)
|
||||||
|
local a = ActiveDecoder(function() return nil end, ...)
|
||||||
|
a.chunk = json
|
||||||
|
local s, obj = pcall(a.get, a)
|
||||||
|
return s and obj or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Direcly encode a Lua object into a JSON string.
|
||||||
|
-- @param obj Lua Object
|
||||||
|
-- @return JSON string
|
||||||
|
function encode(obj, ...)
|
||||||
|
local out = {}
|
||||||
|
local e = Encoder(obj, 1, ...):source()
|
||||||
|
local chnk, err
|
||||||
|
repeat
|
||||||
|
chnk, err = e()
|
||||||
|
out[#out+1] = chnk
|
||||||
|
until not chnk
|
||||||
|
return not err and table.concat(out) or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Null replacement function
|
||||||
|
-- @return null
|
||||||
|
function null()
|
||||||
|
return null
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new JSON-Encoder.
|
||||||
|
-- @class function
|
||||||
|
-- @name Encoder
|
||||||
|
-- @param data Lua-Object to be encoded.
|
||||||
|
-- @param buffersize Blocksize of returned data source.
|
||||||
|
-- @param fastescape Use non-standard escaping (don't escape control chars)
|
||||||
|
-- @return JSON-Encoder
|
||||||
|
Encoder = util.class()
|
||||||
|
|
||||||
|
function Encoder.__init__(self, data, buffersize, fastescape)
|
||||||
|
self.data = data
|
||||||
|
self.buffersize = buffersize or 512
|
||||||
|
self.buffer = ""
|
||||||
|
self.fastescape = fastescape
|
||||||
|
|
||||||
|
getmetatable(self).__call = Encoder.source
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create an LTN12 source providing the encoded JSON-Data.
|
||||||
|
-- @return LTN12 source
|
||||||
|
function Encoder.source(self)
|
||||||
|
local source = coroutine.create(self.dispatch)
|
||||||
|
return function()
|
||||||
|
local res, data = coroutine.resume(source, self, self.data, true)
|
||||||
|
if res then
|
||||||
|
return data
|
||||||
|
else
|
||||||
|
return nil, data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.dispatch(self, data, start)
|
||||||
|
local parser = self.parsers[type(data)]
|
||||||
|
|
||||||
|
parser(self, data)
|
||||||
|
|
||||||
|
if start then
|
||||||
|
if #self.buffer > 0 then
|
||||||
|
coroutine.yield(self.buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.put(self, chunk)
|
||||||
|
if self.buffersize < 2 then
|
||||||
|
coroutine.yield(chunk)
|
||||||
|
else
|
||||||
|
if #self.buffer + #chunk > self.buffersize then
|
||||||
|
local written = 0
|
||||||
|
local fbuffer = self.buffersize - #self.buffer
|
||||||
|
|
||||||
|
coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
|
||||||
|
written = fbuffer
|
||||||
|
|
||||||
|
while #chunk - written > self.buffersize do
|
||||||
|
fbuffer = written + self.buffersize
|
||||||
|
coroutine.yield(chunk:sub(written + 1, fbuffer))
|
||||||
|
written = fbuffer
|
||||||
|
end
|
||||||
|
|
||||||
|
self.buffer = chunk:sub(written + 1)
|
||||||
|
else
|
||||||
|
self.buffer = self.buffer .. chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.parse_nil(self)
|
||||||
|
self:put("null")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.parse_bool(self, obj)
|
||||||
|
self:put(obj and "true" or "false")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.parse_number(self, obj)
|
||||||
|
self:put(tostring(obj))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.parse_string(self, obj)
|
||||||
|
if self.fastescape then
|
||||||
|
self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
|
||||||
|
else
|
||||||
|
self:put('"' ..
|
||||||
|
obj:gsub('[%c\\"]',
|
||||||
|
function(char)
|
||||||
|
return '\\u00%02x' % char:byte()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
.. '"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Encoder.parse_iter(self, obj)
|
||||||
|
if obj == null then
|
||||||
|
return self:put("null")
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(obj) == "table" and (#obj == 0 and next(obj)) then
|
||||||
|
self:put("{")
|
||||||
|
local first = true
|
||||||
|
|
||||||
|
for key, entry in pairs(obj) do
|
||||||
|
first = first or self:put(",")
|
||||||
|
first = first and false
|
||||||
|
self:parse_string(tostring(key))
|
||||||
|
self:put(":")
|
||||||
|
self:dispatch(entry)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:put("}")
|
||||||
|
else
|
||||||
|
self:put("[")
|
||||||
|
local first = true
|
||||||
|
|
||||||
|
if type(obj) == "table" then
|
||||||
|
for i=1, #obj do
|
||||||
|
first = first or self:put(",")
|
||||||
|
first = first and nil
|
||||||
|
self:dispatch(obj[i])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for entry in obj do
|
||||||
|
first = first or self:put(",")
|
||||||
|
first = first and nil
|
||||||
|
self:dispatch(entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:put("]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Encoder.parsers = {
|
||||||
|
['nil'] = Encoder.parse_nil,
|
||||||
|
['table'] = Encoder.parse_iter,
|
||||||
|
['number'] = Encoder.parse_number,
|
||||||
|
['string'] = Encoder.parse_string,
|
||||||
|
['boolean'] = Encoder.parse_bool,
|
||||||
|
['function'] = Encoder.parse_iter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- Create a new JSON-Decoder.
|
||||||
|
-- @class function
|
||||||
|
-- @name Decoder
|
||||||
|
-- @param customnull Use luci.json.null instead of nil for decoding null
|
||||||
|
-- @return JSON-Decoder
|
||||||
|
Decoder = util.class()
|
||||||
|
|
||||||
|
function Decoder.__init__(self, customnull)
|
||||||
|
self.cnull = customnull
|
||||||
|
getmetatable(self).__call = Decoder.sink
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
|
||||||
|
-- @return LTN12 sink
|
||||||
|
function Decoder.sink(self)
|
||||||
|
local sink = coroutine.create(self.dispatch)
|
||||||
|
return function(...)
|
||||||
|
return coroutine.resume(sink, self, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the decoded data packets after the rawdata has been sent to the sink.
|
||||||
|
-- @return Decoded data
|
||||||
|
function Decoder.get(self)
|
||||||
|
return self.data
|
||||||
|
end
|
||||||
|
|
||||||
|
function Decoder.dispatch(self, chunk, src_err, strict)
|
||||||
|
local robject, object
|
||||||
|
local oset = false
|
||||||
|
|
||||||
|
while chunk do
|
||||||
|
while chunk and #chunk < 1 do
|
||||||
|
chunk = self:fetch()
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(not strict or chunk, "Unexpected EOS")
|
||||||
|
if not chunk then break end
|
||||||
|
|
||||||
|
local char = chunk:sub(1, 1)
|
||||||
|
local parser = self.parsers[char]
|
||||||
|
or (char:match("%s") and self.parse_space)
|
||||||
|
or (char:match("[0-9-]") and self.parse_number)
|
||||||
|
or error("Unexpected char '%s'" % char)
|
||||||
|
|
||||||
|
chunk, robject = parser(self, chunk)
|
||||||
|
|
||||||
|
if parser ~= self.parse_space then
|
||||||
|
assert(not oset, "Scope violation: Too many objects")
|
||||||
|
object = robject
|
||||||
|
oset = true
|
||||||
|
|
||||||
|
if strict then
|
||||||
|
return chunk, object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(not src_err, src_err)
|
||||||
|
assert(oset, "Unexpected EOS")
|
||||||
|
|
||||||
|
self.data = 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", self.cnull and 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 == "u" then
|
||||||
|
chunk = self:fetch_atleast(chunk, 4)
|
||||||
|
local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
|
||||||
|
s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
|
||||||
|
assert(s1 and s2, "Invalid Unicode character")
|
||||||
|
|
||||||
|
-- ToDo: Unicode support
|
||||||
|
return chunk:sub(5), s1 == 0 and string.char(s2) or ""
|
||||||
|
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"
|
||||||
|
else
|
||||||
|
error("Unexpected escaping sequence '\\%s'" % char)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_array(self, chunk)
|
||||||
|
chunk = chunk:sub(2)
|
||||||
|
local array = {}
|
||||||
|
local nextp = 1
|
||||||
|
|
||||||
|
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, nextp, object)
|
||||||
|
nextp = nextp + 1
|
||||||
|
|
||||||
|
chunk, object = self:parse_delimiter(chunk, ",%]")
|
||||||
|
assert(object, "Delimiter expected")
|
||||||
|
until object == "]"
|
||||||
|
|
||||||
|
return chunk, array
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
function Decoder.parse_delimiter(self, chunk, delimiter)
|
||||||
|
while true do
|
||||||
|
chunk = self:fetch_atleast(chunk, 1)
|
||||||
|
local char = chunk:sub(1, 1)
|
||||||
|
if char:match("%s") then
|
||||||
|
chunk = self:parse_space(chunk)
|
||||||
|
assert(chunk, "Unexpected EOS")
|
||||||
|
elseif char:match("[%s]" % delimiter) then
|
||||||
|
return chunk:sub(2), char
|
||||||
|
else
|
||||||
|
return chunk, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Decoder.parsers = {
|
||||||
|
['"'] = Decoder.parse_string,
|
||||||
|
['t'] = Decoder.parse_true,
|
||||||
|
['f'] = Decoder.parse_false,
|
||||||
|
['n'] = Decoder.parse_null,
|
||||||
|
['['] = Decoder.parse_array,
|
||||||
|
['{'] = Decoder.parse_object
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- Create a new Active JSON-Decoder.
|
||||||
|
-- @class function
|
||||||
|
-- @name ActiveDecoder
|
||||||
|
-- @param customnull Use luci.json.null instead of nil for decoding null
|
||||||
|
-- @return Active JSON-Decoder
|
||||||
|
ActiveDecoder = util.class(Decoder)
|
||||||
|
|
||||||
|
function ActiveDecoder.__init__(self, source, customnull)
|
||||||
|
Decoder.__init__(self, customnull)
|
||||||
|
self.source = source
|
||||||
|
self.chunk = nil
|
||||||
|
getmetatable(self).__call = self.get
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Fetches one JSON-object from given source
|
||||||
|
-- @return Decoded object
|
||||||
|
function ActiveDecoder.get(self)
|
||||||
|
local chunk, src_err, object
|
||||||
|
if not self.chunk then
|
||||||
|
chunk, src_err = self.source()
|
||||||
|
else
|
||||||
|
chunk = self.chunk
|
||||||
|
end
|
||||||
|
|
||||||
|
self.chunk, object = self:dispatch(chunk, src_err, true)
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ActiveDecoder.fetch(self)
|
||||||
|
local chunk, src_err = self.source()
|
||||||
|
assert(chunk or not src_err, src_err)
|
||||||
|
return chunk
|
||||||
|
end
|
391
1_1.mi_Lua/luci/ltn12.lua
Normal file
391
1_1.mi_Lua/luci/ltn12.lua
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
--[[
|
||||||
|
LuaSocket 2.0.2 license
|
||||||
|
Copyright <EFBFBD> 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: ltn12.lua 3212 2008-09-09 12:44:41Z Cyrus $
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- Declare module
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
local string = require("string")
|
||||||
|
local table = require("table")
|
||||||
|
local base = _G
|
||||||
|
|
||||||
|
--- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
|
||||||
|
-- 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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- LTN12 Filter constructors
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.ltn12.filter
|
||||||
|
|
||||||
|
--- Return a high level filter that cycles a low-level filter
|
||||||
|
-- by passing it each chunk and updating a context between calls.
|
||||||
|
-- @param low Low-level filter
|
||||||
|
-- @param ctx Context
|
||||||
|
-- @param extra Extra argument passed to the low-level filter
|
||||||
|
-- @return LTN12 filter
|
||||||
|
function filter.cycle(low, ctx, extra)
|
||||||
|
base.assert(low)
|
||||||
|
return function(chunk)
|
||||||
|
local ret
|
||||||
|
ret, ctx = low(ctx, chunk, extra)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Chain a bunch of filters together.
|
||||||
|
-- (thanks to Wim Couwenberg)
|
||||||
|
-- @param ... filters to be chained
|
||||||
|
-- @return LTN12 filter
|
||||||
|
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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- LTN12 Source constructors
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.ltn12.source
|
||||||
|
|
||||||
|
-- create an empty source
|
||||||
|
local function empty()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create an empty source.
|
||||||
|
-- @return LTN12 source
|
||||||
|
function source.empty()
|
||||||
|
return empty
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a source that just outputs an error.
|
||||||
|
-- @param err Error object
|
||||||
|
-- @return LTN12 source
|
||||||
|
function source.error(err)
|
||||||
|
return function()
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a file source.
|
||||||
|
-- @param handle File handle ready for reading
|
||||||
|
-- @param io_err IO error object
|
||||||
|
-- @return LTN12 source
|
||||||
|
function source.file(handle, io_err)
|
||||||
|
if handle then
|
||||||
|
return function()
|
||||||
|
local chunk = handle:read(BLOCKSIZE)
|
||||||
|
if not chunk then handle:close() end
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
else return source.error(io_err or "unable to open file") end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Turn a fancy source into a simple source.
|
||||||
|
-- @param src fancy source
|
||||||
|
-- @return LTN12 source
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Create a string source.
|
||||||
|
-- @param s Data
|
||||||
|
-- @return LTN12 source
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Creates rewindable source.
|
||||||
|
-- @param src LTN12 source to be made rewindable
|
||||||
|
-- @return LTN12 source
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Chain a source and a filter together.
|
||||||
|
-- @param src LTN12 source
|
||||||
|
-- @param f LTN12 filter
|
||||||
|
-- @return LTN12 source
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Create a source that produces contents of several sources.
|
||||||
|
-- Sources will be used one after the other, as if they were concatenated
|
||||||
|
-- (thanks to Wim Couwenberg)
|
||||||
|
-- @param ... LTN12 sources
|
||||||
|
-- @return LTN12 source
|
||||||
|
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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- LTN12 sink constructors
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.ltn12.sink
|
||||||
|
|
||||||
|
--- Create a sink that stores into a table.
|
||||||
|
-- @param t output table to store into
|
||||||
|
-- @return LTN12 sink
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Turn a fancy sink into a simple sink.
|
||||||
|
-- @param snk fancy sink
|
||||||
|
-- @return LTN12 sink
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Create a file sink.
|
||||||
|
-- @param handle file handle to write to
|
||||||
|
-- @param io_err IO error
|
||||||
|
-- @return LTN12 sink
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Create a sink that discards data.
|
||||||
|
-- @return LTN12 sink
|
||||||
|
function sink.null()
|
||||||
|
return null
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a sink that just returns an error.
|
||||||
|
-- @param err Error object
|
||||||
|
-- @return LTN12 sink
|
||||||
|
function sink.error(err)
|
||||||
|
return function()
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Chain a sink with a filter.
|
||||||
|
-- @param f LTN12 filter
|
||||||
|
-- @param snk LTN12 sink
|
||||||
|
-- @return LTN12 sink
|
||||||
|
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
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- LTN12 pump functions
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.ltn12.pump
|
||||||
|
|
||||||
|
--- Pump one chunk from the source to the sink.
|
||||||
|
-- @param src LTN12 source
|
||||||
|
-- @param snk LTN12 sink
|
||||||
|
-- @return Chunk of data or nil if an error occured
|
||||||
|
-- @return Error object
|
||||||
|
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
|
||||||
|
|
||||||
|
--- Pump all data from a source to a sink, using a step function.
|
||||||
|
-- @param src LTN12 source
|
||||||
|
-- @param snk LTN12 sink
|
||||||
|
-- @param step step function (optional)
|
||||||
|
-- @return 1 if the operation succeeded otherwise nil
|
||||||
|
-- @return Error object
|
||||||
|
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
|
||||||
|
|
88
1_1.mi_Lua/luci/model/cbi/admin_network/proto_dhcp.lua
Normal file
88
1_1.mi_Lua/luci/model/cbi/admin_network/proto_dhcp.lua
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
local ifc = net:get_interface()
|
||||||
|
|
||||||
|
local hostname, accept_ra, send_rs
|
||||||
|
local bcast, defaultroute, peerdns, dns, metric, clientid, vendorclass
|
||||||
|
|
||||||
|
|
||||||
|
hostname = section:taboption("general", Value, "hostname",
|
||||||
|
translate("Hostname to send when requesting DHCP"))
|
||||||
|
|
||||||
|
hostname.placeholder = luci.sys.hostname()
|
||||||
|
hostname.datatype = "hostname"
|
||||||
|
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
accept_ra = s:taboption("general", Flag, "accept_ra", translate("Accept router advertisements"))
|
||||||
|
accept_ra.default = accept_ra.enabled
|
||||||
|
|
||||||
|
|
||||||
|
send_rs = s:taboption("general", Flag, "send_rs", translate("Send router solicitations"))
|
||||||
|
send_rs.default = send_rs.disabled
|
||||||
|
send_rs:depends("accept_ra", "")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
bcast = section:taboption("advanced", Flag, "broadcast",
|
||||||
|
translate("Use broadcast flag"),
|
||||||
|
translate("Required for certain ISPs, e.g. Charter with DOCSIS 3"))
|
||||||
|
|
||||||
|
bcast.default = bcast.disabled
|
||||||
|
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
clientid = section:taboption("advanced", Value, "clientid",
|
||||||
|
translate("Client ID to send when requesting DHCP"))
|
||||||
|
|
||||||
|
|
||||||
|
vendorclass = section:taboption("advanced", Value, "vendorid",
|
||||||
|
translate("Vendor Class to send when requesting DHCP"))
|
||||||
|
|
||||||
|
|
||||||
|
luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
69
1_1.mi_Lua/luci/model/cbi/admin_network/proto_l2tp.lua
Normal file
69
1_1.mi_Lua/luci/model/cbi/admin_network/proto_l2tp.lua
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
|
||||||
|
local server, username, password
|
||||||
|
local ipv6, defaultroute, metric, peerdns, dns, mtu
|
||||||
|
|
||||||
|
|
||||||
|
server = section:taboption("general", Value, "server", translate("L2TP Server"))
|
||||||
|
server.datatype = "host"
|
||||||
|
|
||||||
|
|
||||||
|
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
|
||||||
|
|
||||||
|
|
||||||
|
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
|
||||||
|
password.password = true
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
ipv6 = section:taboption("advanced", Flag, "ipv6",
|
||||||
|
translate("Enable IPv6 negotiation on the PPP link"))
|
||||||
|
|
||||||
|
ipv6.default = ipv6.disabled
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
metric:depends("defaultroute", defaultroute.enabled)
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
13
1_1.mi_Lua/luci/model/cbi/admin_network/proto_none.lua
Normal file
13
1_1.mi_Lua/luci/model/cbi/admin_network/proto_none.lua
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
136
1_1.mi_Lua/luci/model/cbi/admin_network/proto_ppp.lua
Normal file
136
1_1.mi_Lua/luci/model/cbi/admin_network/proto_ppp.lua
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
|
||||||
|
local device, username, password
|
||||||
|
local ipv6, defaultroute, metric, peerdns, dns,
|
||||||
|
keepalive_failure, keepalive_interval, demand, mtu
|
||||||
|
|
||||||
|
|
||||||
|
device = section:taboption("general", Value, "device", translate("Modem device"))
|
||||||
|
device.rmempty = false
|
||||||
|
|
||||||
|
local device_suggestions = nixio.fs.glob("/dev/tty*S*")
|
||||||
|
or nixio.fs.glob("/dev/tts/*")
|
||||||
|
|
||||||
|
if device_suggestions then
|
||||||
|
local node
|
||||||
|
for node in device_suggestions do
|
||||||
|
device:value(node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
|
||||||
|
|
||||||
|
|
||||||
|
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
|
||||||
|
password.password = true
|
||||||
|
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
ipv6 = section:taboption("advanced", Flag, "ipv6",
|
||||||
|
translate("Enable IPv6 negotiation on the PPP link"))
|
||||||
|
|
||||||
|
ipv6.default = ipv6.disabled
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
metric:depends("defaultroute", defaultroute.enabled)
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
|
||||||
|
translate("LCP echo failure threshold"),
|
||||||
|
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
|
||||||
|
|
||||||
|
function keepalive_failure.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_failure.write() end
|
||||||
|
function keepalive_failure.remove() end
|
||||||
|
|
||||||
|
keepalive_failure.placeholder = "0"
|
||||||
|
keepalive_failure.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
|
||||||
|
translate("LCP echo interval"),
|
||||||
|
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
|
||||||
|
|
||||||
|
function keepalive_interval.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^%d+[ ,]+(%d+)"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_interval.write(self, section, value)
|
||||||
|
local f = tonumber(keepalive_failure:formvalue(section)) or 0
|
||||||
|
local i = tonumber(value) or 5
|
||||||
|
if i < 1 then i = 1 end
|
||||||
|
if f > 0 then
|
||||||
|
m:set(section, "keepalive", "%d %d" %{ f, i })
|
||||||
|
else
|
||||||
|
m:del(section, "keepalive")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keepalive_interval.remove = keepalive_interval.write
|
||||||
|
keepalive_interval.placeholder = "5"
|
||||||
|
keepalive_interval.datatype = "min(1)"
|
||||||
|
|
||||||
|
|
||||||
|
demand = section:taboption("advanced", Value, "demand",
|
||||||
|
translate("Inactivity timeout"),
|
||||||
|
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
|
||||||
|
|
||||||
|
demand.placeholder = "0"
|
||||||
|
demand.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
142
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pppoa.lua
Normal file
142
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pppoa.lua
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
|
||||||
|
local encaps, atmdev, vci, vpi, username, password
|
||||||
|
local ipv6, defaultroute, metric, peerdns, dns,
|
||||||
|
keepalive_failure, keepalive_interval, demand, mtu
|
||||||
|
|
||||||
|
|
||||||
|
encaps = section:taboption("general", ListValue, "encaps", translate("PPPoA Encapsulation"))
|
||||||
|
encaps:value("vc", "VC-Mux")
|
||||||
|
encaps:value("llc", "LLC")
|
||||||
|
|
||||||
|
|
||||||
|
atmdev = section:taboption("general", Value, "atmdev", translate("ATM device number"))
|
||||||
|
atmdev.default = "0"
|
||||||
|
atmdev.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
vci = section:taboption("general", Value, "vci", translate("ATM Virtual Channel Identifier (VCI)"))
|
||||||
|
vci.default = "35"
|
||||||
|
vci.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
vpi = section:taboption("general", Value, "vpi", translate("ATM Virtual Path Identifier (VPI)"))
|
||||||
|
vpi.default = "8"
|
||||||
|
vpi.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
|
||||||
|
|
||||||
|
|
||||||
|
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
|
||||||
|
password.password = true
|
||||||
|
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
ipv6 = section:taboption("advanced", Flag, "ipv6",
|
||||||
|
translate("Enable IPv6 negotiation on the PPP link"))
|
||||||
|
|
||||||
|
ipv6.default = ipv6.disabled
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
metric:depends("defaultroute", defaultroute.enabled)
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
|
||||||
|
translate("LCP echo failure threshold"),
|
||||||
|
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
|
||||||
|
|
||||||
|
function keepalive_failure.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_failure.write() end
|
||||||
|
function keepalive_failure.remove() end
|
||||||
|
|
||||||
|
keepalive_failure.placeholder = "0"
|
||||||
|
keepalive_failure.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
|
||||||
|
translate("LCP echo interval"),
|
||||||
|
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
|
||||||
|
|
||||||
|
function keepalive_interval.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^%d+[ ,]+(%d+)"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_interval.write(self, section, value)
|
||||||
|
local f = tonumber(keepalive_failure:formvalue(section)) or 0
|
||||||
|
local i = tonumber(value) or 5
|
||||||
|
if i < 1 then i = 1 end
|
||||||
|
if f > 0 then
|
||||||
|
m:set(section, "keepalive", "%d %d" %{ f, i })
|
||||||
|
else
|
||||||
|
m:del(section, "keepalive")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keepalive_interval.remove = keepalive_interval.write
|
||||||
|
keepalive_interval.placeholder = "5"
|
||||||
|
keepalive_interval.datatype = "min(1)"
|
||||||
|
|
||||||
|
|
||||||
|
demand = section:taboption("advanced", Value, "demand",
|
||||||
|
translate("Inactivity timeout"),
|
||||||
|
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
|
||||||
|
|
||||||
|
demand.placeholder = "0"
|
||||||
|
demand.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
136
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pppoe.lua
Normal file
136
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pppoe.lua
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
|
||||||
|
local username, password, ac, service
|
||||||
|
local ipv6, defaultroute, metric, peerdns, dns,
|
||||||
|
keepalive_failure, keepalive_interval, demand, mtu
|
||||||
|
|
||||||
|
|
||||||
|
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
|
||||||
|
|
||||||
|
|
||||||
|
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
|
||||||
|
password.password = true
|
||||||
|
|
||||||
|
|
||||||
|
ac = section:taboption("general", Value, "ac",
|
||||||
|
translate("Access Concentrator"),
|
||||||
|
translate("Leave empty to autodetect"))
|
||||||
|
|
||||||
|
ac.placeholder = translate("auto")
|
||||||
|
|
||||||
|
|
||||||
|
service = section:taboption("general", Value, "service",
|
||||||
|
translate("Service Name"),
|
||||||
|
translate("Leave empty to autodetect"))
|
||||||
|
|
||||||
|
service.placeholder = translate("auto")
|
||||||
|
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
ipv6 = section:taboption("advanced", Flag, "ipv6",
|
||||||
|
translate("Enable IPv6 negotiation on the PPP link"))
|
||||||
|
|
||||||
|
ipv6.default = ipv6.disabled
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
metric:depends("defaultroute", defaultroute.enabled)
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
|
||||||
|
translate("LCP echo failure threshold"),
|
||||||
|
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
|
||||||
|
|
||||||
|
function keepalive_failure.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_failure.write() end
|
||||||
|
function keepalive_failure.remove() end
|
||||||
|
|
||||||
|
keepalive_failure.placeholder = "0"
|
||||||
|
keepalive_failure.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
|
||||||
|
translate("LCP echo interval"),
|
||||||
|
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
|
||||||
|
|
||||||
|
function keepalive_interval.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^%d+[ ,]+(%d+)"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_interval.write(self, section, value)
|
||||||
|
local f = tonumber(keepalive_failure:formvalue(section)) or 0
|
||||||
|
local i = tonumber(value) or 5
|
||||||
|
if i < 1 then i = 1 end
|
||||||
|
if f > 0 then
|
||||||
|
m:set(section, "keepalive", "%d %d" %{ f, i })
|
||||||
|
else
|
||||||
|
m:del(section, "keepalive")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keepalive_interval.remove = keepalive_interval.write
|
||||||
|
keepalive_interval.placeholder = "5"
|
||||||
|
keepalive_interval.datatype = "min(1)"
|
||||||
|
|
||||||
|
|
||||||
|
demand = section:taboption("advanced", Value, "demand",
|
||||||
|
translate("Inactivity timeout"),
|
||||||
|
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
|
||||||
|
|
||||||
|
demand.placeholder = "0"
|
||||||
|
demand.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
116
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pptp.lua
Normal file
116
1_1.mi_Lua/luci/model/cbi/admin_network/proto_pptp.lua
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
|
||||||
|
local server, username, password
|
||||||
|
local defaultroute, metric, peerdns, dns,
|
||||||
|
keepalive_failure, keepalive_interval, demand, mtu
|
||||||
|
|
||||||
|
|
||||||
|
server = section:taboption("general", Value, "server", translate("VPN Server"))
|
||||||
|
server.datatype = "host"
|
||||||
|
|
||||||
|
|
||||||
|
username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
|
||||||
|
|
||||||
|
|
||||||
|
password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
|
||||||
|
password.password = true
|
||||||
|
|
||||||
|
|
||||||
|
defaultroute = section:taboption("advanced", Flag, "defaultroute",
|
||||||
|
translate("Use default gateway"),
|
||||||
|
translate("If unchecked, no default route is configured"))
|
||||||
|
|
||||||
|
defaultroute.default = defaultroute.enabled
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
||||||
|
metric:depends("defaultroute", defaultroute.enabled)
|
||||||
|
|
||||||
|
|
||||||
|
peerdns = section:taboption("advanced", Flag, "peerdns",
|
||||||
|
translate("Use DNS servers advertised by peer"),
|
||||||
|
translate("If unchecked, the advertised DNS server addresses are ignored"))
|
||||||
|
|
||||||
|
peerdns.default = peerdns.enabled
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("advanced", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns:depends("peerdns", "")
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure",
|
||||||
|
translate("LCP echo failure threshold"),
|
||||||
|
translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures"))
|
||||||
|
|
||||||
|
function keepalive_failure.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^(%d+)[ ,]+%d+") or v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_failure.write() end
|
||||||
|
function keepalive_failure.remove() end
|
||||||
|
|
||||||
|
keepalive_failure.placeholder = "0"
|
||||||
|
keepalive_failure.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval",
|
||||||
|
translate("LCP echo interval"),
|
||||||
|
translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold"))
|
||||||
|
|
||||||
|
function keepalive_interval.cfgvalue(self, section)
|
||||||
|
local v = m:get(section, "keepalive")
|
||||||
|
if v and #v > 0 then
|
||||||
|
return tonumber(v:match("^%d+[ ,]+(%d+)"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function keepalive_interval.write(self, section, value)
|
||||||
|
local f = tonumber(keepalive_failure:formvalue(section)) or 0
|
||||||
|
local i = tonumber(value) or 5
|
||||||
|
if i < 1 then i = 1 end
|
||||||
|
if f > 0 then
|
||||||
|
m:set(section, "keepalive", "%d %d" %{ f, i })
|
||||||
|
else
|
||||||
|
m:del(section, "keepalive")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keepalive_interval.remove = keepalive_interval.write
|
||||||
|
keepalive_interval.placeholder = "5"
|
||||||
|
keepalive_interval.datatype = "min(1)"
|
||||||
|
|
||||||
|
|
||||||
|
demand = section:taboption("advanced", Value, "demand",
|
||||||
|
translate("Inactivity timeout"),
|
||||||
|
translate("Close inactive connection after the given amount of seconds, use 0 to persist connection"))
|
||||||
|
|
||||||
|
demand.placeholder = "0"
|
||||||
|
demand.datatype = "uinteger"
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
83
1_1.mi_Lua/luci/model/cbi/admin_network/proto_static.lua
Normal file
83
1_1.mi_Lua/luci/model/cbi/admin_network/proto_static.lua
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local map, section, net = ...
|
||||||
|
local ifc = net:get_interface()
|
||||||
|
|
||||||
|
local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
|
||||||
|
local mtu, metric
|
||||||
|
|
||||||
|
|
||||||
|
ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
|
||||||
|
ipaddr.datatype = "ip4addr"
|
||||||
|
|
||||||
|
|
||||||
|
netmask = section:taboption("general", Value, "netmask",
|
||||||
|
translate("IPv4 netmask"))
|
||||||
|
|
||||||
|
netmask.datatype = "ip4addr"
|
||||||
|
netmask:value("255.255.255.0")
|
||||||
|
netmask:value("255.255.0.0")
|
||||||
|
netmask:value("255.0.0.0")
|
||||||
|
|
||||||
|
|
||||||
|
gateway = section:taboption("general", Value, "gateway", translate("IPv4 gateway"))
|
||||||
|
gateway.datatype = "ip4addr"
|
||||||
|
|
||||||
|
|
||||||
|
broadcast = section:taboption("general", Value, "broadcast", translate("IPv4 broadcast"))
|
||||||
|
broadcast.datatype = "ip4addr"
|
||||||
|
|
||||||
|
|
||||||
|
dns = section:taboption("general", DynamicList, "dns",
|
||||||
|
translate("Use custom DNS servers"))
|
||||||
|
|
||||||
|
dns.datatype = "ipaddr"
|
||||||
|
dns.cast = "string"
|
||||||
|
|
||||||
|
|
||||||
|
if luci.model.network:has_ipv6() then
|
||||||
|
|
||||||
|
accept_ra = s:taboption("general", Flag, "accept_ra", translate("Accept router advertisements"))
|
||||||
|
accept_ra.default = accept_ra.disabled
|
||||||
|
|
||||||
|
|
||||||
|
send_rs = s:taboption("general", Flag, "send_rs", translate("Send router solicitations"))
|
||||||
|
send_rs.default = send_rs.enabled
|
||||||
|
send_rs:depends("accept_ra", "")
|
||||||
|
|
||||||
|
|
||||||
|
ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
|
||||||
|
ip6addr.datatype = "ip6addr"
|
||||||
|
ip6addr:depends("accept_ra", "")
|
||||||
|
|
||||||
|
|
||||||
|
ip6gw = section:taboption("general", Value, "ip6gw", translate("IPv6 gateway"))
|
||||||
|
ip6gw.datatype = "ip6addr"
|
||||||
|
ip6gw:depends("accept_ra", "")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
|
||||||
|
|
||||||
|
|
||||||
|
mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
|
||||||
|
mtu.placeholder = "1500"
|
||||||
|
mtu.datatype = "max(1500)"
|
||||||
|
|
||||||
|
|
||||||
|
metric = section:taboption("advanced", Value, "metric",
|
||||||
|
translate("Use gateway metric"))
|
||||||
|
|
||||||
|
metric.placeholder = "0"
|
||||||
|
metric.datatype = "uinteger"
|
38
1_1.mi_Lua/luci/model/cbi/firewall/custom.lua
Normal file
38
1_1.mi_Lua/luci/model/cbi/firewall/custom.lua
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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: custom.lua 8108 2011-12-19 21:16:31Z jow $
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
local f = SimpleForm("firewall",
|
||||||
|
translate("Firewall - Custom Rules"),
|
||||||
|
translate("Custom rules allow you to execute arbritary iptables commands \
|
||||||
|
which are not otherwise covered by the firewall framework. \
|
||||||
|
The commands are executed after each firewall restart, right after \
|
||||||
|
the default ruleset has been loaded."))
|
||||||
|
|
||||||
|
local o = f:field(Value, "_custom")
|
||||||
|
|
||||||
|
o.template = "cbi/tvalue"
|
||||||
|
o.rows = 20
|
||||||
|
|
||||||
|
function o.cfgvalue(self, section)
|
||||||
|
return fs.readfile("/etc/firewall.user")
|
||||||
|
end
|
||||||
|
|
||||||
|
function o.write(self, section, value)
|
||||||
|
value = value:gsub("\r\n?", "\n")
|
||||||
|
fs.writefile("/etc/firewall.user", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
return f
|
178
1_1.mi_Lua/luci/model/cbi/firewall/forward-details.lua
Normal file
178
1_1.mi_Lua/luci/model/cbi/firewall/forward-details.lua
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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: forward-details.lua 8962 2012-08-09 10:03:32Z jow $
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local dsp = require "luci.dispatcher"
|
||||||
|
local ft = require "luci.tools.firewall"
|
||||||
|
|
||||||
|
local m, s, o
|
||||||
|
|
||||||
|
arg[1] = arg[1] or ""
|
||||||
|
|
||||||
|
m = Map("firewall",
|
||||||
|
translate("Firewall - Port Forwards"),
|
||||||
|
translate("This page allows you to change advanced properties of the port \
|
||||||
|
forwarding entry. In most cases there is no need to modify \
|
||||||
|
those settings."))
|
||||||
|
|
||||||
|
m.redirect = dsp.build_url("admin/network/firewall/forwards")
|
||||||
|
|
||||||
|
if m.uci:get("firewall", arg[1]) ~= "redirect" then
|
||||||
|
luci.http.redirect(m.redirect)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
|
||||||
|
if not name or #name == 0 then
|
||||||
|
name = translate("(Unnamed Entry)")
|
||||||
|
end
|
||||||
|
m.title = "%s - %s" %{ translate("Firewall - Port Forwards"), name }
|
||||||
|
end
|
||||||
|
|
||||||
|
local wan_zone = nil
|
||||||
|
|
||||||
|
m.uci:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
local n = s.network or s.name
|
||||||
|
if n then
|
||||||
|
local i
|
||||||
|
for i in n:gmatch("%S+") do
|
||||||
|
if i == "wan" then
|
||||||
|
wan_zone = s.name
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
s = m:section(NamedSection, arg[1], "redirect", "")
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Button)
|
||||||
|
ft.opt_name(s, Value, translate("Name"))
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "proto", translate("Protocol"))
|
||||||
|
o:value("tcp udp", "TCP+UDP")
|
||||||
|
o:value("tcp", "TCP")
|
||||||
|
o:value("udp", "UDP")
|
||||||
|
o:value("icmp", "ICMP")
|
||||||
|
|
||||||
|
function o.cfgvalue(...)
|
||||||
|
local v = Value.cfgvalue(...)
|
||||||
|
if not v or v == "tcpudp" then
|
||||||
|
return "tcp udp"
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src", translate("Source zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.default = "wan"
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(DynamicList, "src_mac",
|
||||||
|
translate("Source MAC address"),
|
||||||
|
translate("Only match incoming traffic from these MACs."))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(macaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.mac_hints(function(mac, name)
|
||||||
|
o:value(mac, "%s (%s)" %{ mac, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_ip",
|
||||||
|
translate("Source IP address"),
|
||||||
|
translate("Only match incoming traffic from this IP or range."))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(ip4addr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_port",
|
||||||
|
translate("Source port"),
|
||||||
|
translate("Only match incoming traffic originating from the given source port or port range on the client host"))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(portrange)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_dip",
|
||||||
|
translate("External IP address"),
|
||||||
|
translate("Only match incoming traffic directed at the given IP address."))
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(ip4addr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_dport", translate("External port"),
|
||||||
|
translate("Match incoming traffic directed at the given " ..
|
||||||
|
"destination port or port range on this host"))
|
||||||
|
o.datatype = "neg(portrange)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest", translate("Internal zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.default = "lan"
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_ip", translate("Internal IP address"),
|
||||||
|
translate("Redirect matched incoming traffic to the specified \
|
||||||
|
internal host"))
|
||||||
|
o.datatype = "ip4addr"
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_port",
|
||||||
|
translate("Internal port"),
|
||||||
|
translate("Redirect matched incoming traffic to the given port on \
|
||||||
|
the internal host"))
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
o.datatype = "portrange"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Flag, "reflection", translate("Enable NAT Loopback"))
|
||||||
|
o.rmempty = true
|
||||||
|
o.default = o.enabled
|
||||||
|
o:depends("src", wan_zone)
|
||||||
|
o.cfgvalue = function(...)
|
||||||
|
return Flag.cfgvalue(...) or "1"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
s:option(Value, "extra",
|
||||||
|
translate("Extra arguments"),
|
||||||
|
translate("Passes additional arguments to iptables. Use with care!"))
|
||||||
|
|
||||||
|
|
||||||
|
return m
|
144
1_1.mi_Lua/luci/model/cbi/firewall/forwards.lua
Normal file
144
1_1.mi_Lua/luci/model/cbi/firewall/forwards.lua
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ds = require "luci.dispatcher"
|
||||||
|
local ft = require "luci.tools.firewall"
|
||||||
|
|
||||||
|
m = Map("firewall", translate("Firewall - Port Forwards"),
|
||||||
|
translate("Port forwarding allows remote computers on the Internet to \
|
||||||
|
connect to a specific computer or service within the \
|
||||||
|
private LAN."))
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Port Forwards
|
||||||
|
--
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "redirect", translate("Port Forwards"))
|
||||||
|
s.template = "cbi/tblsection"
|
||||||
|
s.addremove = true
|
||||||
|
s.anonymous = true
|
||||||
|
s.sortable = true
|
||||||
|
s.extedit = ds.build_url("admin/network/firewall/forwards/%s")
|
||||||
|
s.template_addremove = "firewall/cbi_addforward"
|
||||||
|
|
||||||
|
function s.create(self, section)
|
||||||
|
local n = m:formvalue("_newfwd.name")
|
||||||
|
local p = m:formvalue("_newfwd.proto")
|
||||||
|
local E = m:formvalue("_newfwd.extzone")
|
||||||
|
local e = m:formvalue("_newfwd.extport")
|
||||||
|
local I = m:formvalue("_newfwd.intzone")
|
||||||
|
local a = m:formvalue("_newfwd.intaddr")
|
||||||
|
local i = m:formvalue("_newfwd.intport")
|
||||||
|
|
||||||
|
if p == "other" or (p and a) then
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
|
||||||
|
self.map:set(created, "target", "DNAT")
|
||||||
|
self.map:set(created, "src", E or "wan")
|
||||||
|
self.map:set(created, "dest", I or "lan")
|
||||||
|
self.map:set(created, "proto", (p ~= "other") and p or "all")
|
||||||
|
self.map:set(created, "src_dport", e)
|
||||||
|
self.map:set(created, "dest_ip", a)
|
||||||
|
self.map:set(created, "dest_port", i)
|
||||||
|
self.map:set(created, "name", n)
|
||||||
|
end
|
||||||
|
|
||||||
|
if p ~= "other" then
|
||||||
|
created = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.parse(self, ...)
|
||||||
|
TypedSection.parse(self, ...)
|
||||||
|
if created then
|
||||||
|
m.uci:save("firewall")
|
||||||
|
luci.http.redirect(ds.build_url(
|
||||||
|
"admin/network/firewall/redirect", created
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.filter(self, sid)
|
||||||
|
return (self.map:get(sid, "target") ~= "SNAT")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
ft.opt_name(s, DummyValue, translate("Name"))
|
||||||
|
|
||||||
|
|
||||||
|
local function forward_proto_txt(self, s)
|
||||||
|
return "%s-%s" %{
|
||||||
|
translate("IPv4"),
|
||||||
|
ft.fmt_proto(self.map:get(s, "proto"),
|
||||||
|
self.map:get(s, "icmp_type")) or "TCP+UDP"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function forward_src_txt(self, s)
|
||||||
|
local z = ft.fmt_zone(self.map:get(s, "src"), translate("any zone"))
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "src_ip"), translate("any host"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "src_port"))
|
||||||
|
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
|
||||||
|
|
||||||
|
if p and m then
|
||||||
|
return translatef("From %s in %s with source %s and %s", a, z, p, m)
|
||||||
|
elseif p or m then
|
||||||
|
return translatef("From %s in %s with source %s", a, z, p or m)
|
||||||
|
else
|
||||||
|
return translatef("From %s in %s", a, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function forward_via_txt(self, s)
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "src_dip"), translate("any router IP"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "src_dport"))
|
||||||
|
|
||||||
|
if p then
|
||||||
|
return translatef("Via %s at %s", a, p)
|
||||||
|
else
|
||||||
|
return translatef("Via %s", a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
match = s:option(DummyValue, "match", translate("Match"))
|
||||||
|
match.rawhtml = true
|
||||||
|
match.width = "50%"
|
||||||
|
function match.cfgvalue(self, s)
|
||||||
|
return "<small>%s<br />%s<br />%s</small>" % {
|
||||||
|
forward_proto_txt(self, s),
|
||||||
|
forward_src_txt(self, s),
|
||||||
|
forward_via_txt(self, s)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
dest = s:option(DummyValue, "dest", translate("Forward to"))
|
||||||
|
dest.rawhtml = true
|
||||||
|
dest.width = "40%"
|
||||||
|
function dest.cfgvalue(self, s)
|
||||||
|
local z = ft.fmt_zone(self.map:get(s, "dest"), translate("any zone"))
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
|
||||||
|
ft.fmt_port(self.map:get(s, "src_dport"))
|
||||||
|
|
||||||
|
if p then
|
||||||
|
return translatef("%s, %s in %s", a, p, z)
|
||||||
|
else
|
||||||
|
return translatef("%s in %s", a, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
|
||||||
|
|
||||||
|
return m
|
338
1_1.mi_Lua/luci/model/cbi/firewall/rule-details.lua
Normal file
338
1_1.mi_Lua/luci/model/cbi/firewall/rule-details.lua
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local dsp = require "luci.dispatcher"
|
||||||
|
local nxo = require "nixio"
|
||||||
|
|
||||||
|
local ft = require "luci.tools.firewall"
|
||||||
|
local nw = require "luci.model.network"
|
||||||
|
local m, s, o, k, v
|
||||||
|
|
||||||
|
arg[1] = arg[1] or ""
|
||||||
|
|
||||||
|
m = Map("firewall",
|
||||||
|
translate("Firewall - Traffic Rules"),
|
||||||
|
translate("This page allows you to change advanced properties of the \
|
||||||
|
traffic rule entry, such as matched source and destination \
|
||||||
|
hosts."))
|
||||||
|
|
||||||
|
m.redirect = dsp.build_url("admin/network/firewall/rules")
|
||||||
|
|
||||||
|
nw.init(m.uci)
|
||||||
|
|
||||||
|
local rule_type = m.uci:get("firewall", arg[1])
|
||||||
|
if rule_type == "redirect" and m:get(arg[1], "target") ~= "SNAT" then
|
||||||
|
rule_type = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not rule_type then
|
||||||
|
luci.http.redirect(m.redirect)
|
||||||
|
return
|
||||||
|
|
||||||
|
--
|
||||||
|
-- SNAT
|
||||||
|
--
|
||||||
|
elseif rule_type == "redirect" then
|
||||||
|
|
||||||
|
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
|
||||||
|
if not name or #name == 0 then
|
||||||
|
name = translate("(Unnamed SNAT)")
|
||||||
|
else
|
||||||
|
name = "SNAT %s" % name
|
||||||
|
end
|
||||||
|
|
||||||
|
m.title = "%s - %s" %{ translate("Firewall - Traffic Rules"), name }
|
||||||
|
|
||||||
|
local wan_zone = nil
|
||||||
|
|
||||||
|
m.uci:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
local n = s.network or s.name
|
||||||
|
if n then
|
||||||
|
local i
|
||||||
|
for i in n:gmatch("%S+") do
|
||||||
|
if i == "wan" then
|
||||||
|
wan_zone = s.name
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
s = m:section(NamedSection, arg[1], "redirect", "")
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Button)
|
||||||
|
ft.opt_name(s, Value, translate("Name"))
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "proto",
|
||||||
|
translate("Protocol"),
|
||||||
|
translate("You may specify multiple by selecting \"-- custom --\" and \
|
||||||
|
then entering protocols separated by space."))
|
||||||
|
|
||||||
|
o:value("all", "All protocols")
|
||||||
|
o:value("tcp udp", "TCP+UDP")
|
||||||
|
o:value("tcp", "TCP")
|
||||||
|
o:value("udp", "UDP")
|
||||||
|
o:value("icmp", "ICMP")
|
||||||
|
|
||||||
|
function o.cfgvalue(...)
|
||||||
|
local v = Value.cfgvalue(...)
|
||||||
|
if not v or v == "tcpudp" then
|
||||||
|
return "tcp udp"
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src", translate("Source zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.default = "wan"
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(DynamicList, "src_mac", translate("Source MAC address"))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(macaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.mac_hints(function(mac, name)
|
||||||
|
o:value(mac, "%s (%s)" %{ mac, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_ip", translate("Source IP address"))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(ipaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_port",
|
||||||
|
translate("Source port"),
|
||||||
|
translate("Match incoming traffic originating from the given source \
|
||||||
|
port or port range on the client host."))
|
||||||
|
o.rmempty = true
|
||||||
|
o.datatype = "neg(portrange)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest", translate("Destination zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.default = "lan"
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_ip", translate("Destination IP address"))
|
||||||
|
o.datatype = "neg(ip4addr)"
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_port",
|
||||||
|
translate("Destination port"),
|
||||||
|
translate("Match forwarded traffic to the given destination port or \
|
||||||
|
port range."))
|
||||||
|
|
||||||
|
o.rmempty = true
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
o.datatype = "neg(portrange)"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_dip",
|
||||||
|
translate("SNAT IP address"),
|
||||||
|
translate("Rewrite matched traffic to the given address."))
|
||||||
|
o.rmempty = false
|
||||||
|
o.datatype = "ip4addr"
|
||||||
|
|
||||||
|
for k, v in ipairs(nw:get_interfaces()) do
|
||||||
|
local a
|
||||||
|
for k, a in ipairs(v:ipaddrs()) do
|
||||||
|
o:value(a:host():string(), '%s (%s)' %{
|
||||||
|
a:host():string(), v:shortname()
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_dport", translate("SNAT port"),
|
||||||
|
translate("Rewrite matched traffic to the given source port. May be \
|
||||||
|
left empty to only rewrite the IP address."))
|
||||||
|
o.datatype = "portrange"
|
||||||
|
o.rmempty = true
|
||||||
|
o.placeholder = translate('Do not rewrite')
|
||||||
|
|
||||||
|
|
||||||
|
s:option(Value, "extra",
|
||||||
|
translate("Extra arguments"),
|
||||||
|
translate("Passes additional arguments to iptables. Use with care!"))
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Rule
|
||||||
|
--
|
||||||
|
else
|
||||||
|
local name = m:get(arg[1], "name") or m:get(arg[1], "_name")
|
||||||
|
if not name or #name == 0 then
|
||||||
|
name = translate("(Unnamed Rule)")
|
||||||
|
end
|
||||||
|
|
||||||
|
m.title = "%s - %s" %{ translate("Firewall - Traffic Rules"), name }
|
||||||
|
|
||||||
|
|
||||||
|
s = m:section(NamedSection, arg[1], "rule", "")
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Button)
|
||||||
|
ft.opt_name(s, Value, translate("Name"))
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(ListValue, "family", translate("Restrict to address family"))
|
||||||
|
o.rmempty = true
|
||||||
|
o:value("", translate("IPv4 and IPv6"))
|
||||||
|
o:value("ipv4", translate("IPv4 only"))
|
||||||
|
o:value("ipv6", translate("IPv6 only"))
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "proto", translate("Protocol"))
|
||||||
|
o:value("all", translate("Any"))
|
||||||
|
o:value("tcp udp", "TCP+UDP")
|
||||||
|
o:value("tcp", "TCP")
|
||||||
|
o:value("udp", "UDP")
|
||||||
|
o:value("icmp", "ICMP")
|
||||||
|
|
||||||
|
function o.cfgvalue(...)
|
||||||
|
local v = Value.cfgvalue(...)
|
||||||
|
if not v or v == "tcpudp" then
|
||||||
|
return "tcp udp"
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(DynamicList, "icmp_type", translate("Match ICMP type"))
|
||||||
|
o:value("", "any")
|
||||||
|
o:value("echo-reply")
|
||||||
|
o:value("destination-unreachable")
|
||||||
|
o:value("network-unreachable")
|
||||||
|
o:value("host-unreachable")
|
||||||
|
o:value("protocol-unreachable")
|
||||||
|
o:value("port-unreachable")
|
||||||
|
o:value("fragmentation-needed")
|
||||||
|
o:value("source-route-failed")
|
||||||
|
o:value("network-unknown")
|
||||||
|
o:value("host-unknown")
|
||||||
|
o:value("network-prohibited")
|
||||||
|
o:value("host-prohibited")
|
||||||
|
o:value("TOS-network-unreachable")
|
||||||
|
o:value("TOS-host-unreachable")
|
||||||
|
o:value("communication-prohibited")
|
||||||
|
o:value("host-precedence-violation")
|
||||||
|
o:value("precedence-cutoff")
|
||||||
|
o:value("source-quench")
|
||||||
|
o:value("redirect")
|
||||||
|
o:value("network-redirect")
|
||||||
|
o:value("host-redirect")
|
||||||
|
o:value("TOS-network-redirect")
|
||||||
|
o:value("TOS-host-redirect")
|
||||||
|
o:value("echo-request")
|
||||||
|
o:value("router-advertisement")
|
||||||
|
o:value("router-solicitation")
|
||||||
|
o:value("time-exceeded")
|
||||||
|
o:value("ttl-zero-during-transit")
|
||||||
|
o:value("ttl-zero-during-reassembly")
|
||||||
|
o:value("parameter-problem")
|
||||||
|
o:value("ip-header-bad")
|
||||||
|
o:value("required-option-missing")
|
||||||
|
o:value("timestamp-request")
|
||||||
|
o:value("timestamp-reply")
|
||||||
|
o:value("address-mask-request")
|
||||||
|
o:value("address-mask-reply")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src", translate("Source zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.allowany = true
|
||||||
|
o.default = "wan"
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_mac", translate("Source MAC address"))
|
||||||
|
o.datatype = "list(macaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.mac_hints(function(mac, name)
|
||||||
|
o:value(mac, "%s (%s)" %{ mac, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_ip", translate("Source address"))
|
||||||
|
o.datatype = "neg(ipaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "src_port", translate("Source port"))
|
||||||
|
o.datatype = "list(neg(portrange))"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest", translate("Destination zone"))
|
||||||
|
o.nocreate = true
|
||||||
|
o.allowany = true
|
||||||
|
o.allowlocal = true
|
||||||
|
o.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_ip", translate("Destination address"))
|
||||||
|
o.datatype = "neg(ipaddr)"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
luci.sys.net.ipv4_hints(function(ip, name)
|
||||||
|
o:value(ip, "%s (%s)" %{ ip, name })
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "dest_port", translate("Destination port"))
|
||||||
|
o.datatype = "list(neg(portrange))"
|
||||||
|
o.placeholder = translate("any")
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(ListValue, "target", translate("Action"))
|
||||||
|
o.default = "ACCEPT"
|
||||||
|
o:value("DROP", translate("drop"))
|
||||||
|
o:value("ACCEPT", translate("accept"))
|
||||||
|
o:value("REJECT", translate("reject"))
|
||||||
|
o:value("NOTRACK", translate("don't track"))
|
||||||
|
|
||||||
|
|
||||||
|
s:option(Value, "extra",
|
||||||
|
translate("Extra arguments"),
|
||||||
|
translate("Passes additional arguments to iptables. Use with care!"))
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
269
1_1.mi_Lua/luci/model/cbi/firewall/rules.lua
Normal file
269
1_1.mi_Lua/luci/model/cbi/firewall/rules.lua
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ds = require "luci.dispatcher"
|
||||||
|
local ft = require "luci.tools.firewall"
|
||||||
|
|
||||||
|
m = Map("firewall",
|
||||||
|
translate("Firewall - Traffic Rules"),
|
||||||
|
translate("Traffic rules define policies for packets traveling between \
|
||||||
|
different zones, for example to reject traffic between certain hosts \
|
||||||
|
or to open WAN ports on the router."))
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Rules
|
||||||
|
--
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "rule", translate("Traffic Rules"))
|
||||||
|
s.addremove = true
|
||||||
|
s.anonymous = true
|
||||||
|
s.sortable = true
|
||||||
|
s.template = "cbi/tblsection"
|
||||||
|
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
|
||||||
|
s.defaults.target = "ACCEPT"
|
||||||
|
s.template_addremove = "firewall/cbi_addrule"
|
||||||
|
|
||||||
|
|
||||||
|
function s.create(self, section)
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.parse(self, ...)
|
||||||
|
TypedSection.parse(self, ...)
|
||||||
|
|
||||||
|
local i_n = m:formvalue("_newopen.name")
|
||||||
|
local i_p = m:formvalue("_newopen.proto")
|
||||||
|
local i_e = m:formvalue("_newopen.extport")
|
||||||
|
local i_x = m:formvalue("_newopen.submit")
|
||||||
|
|
||||||
|
local f_n = m:formvalue("_newfwd.name")
|
||||||
|
local f_s = m:formvalue("_newfwd.src")
|
||||||
|
local f_d = m:formvalue("_newfwd.dest")
|
||||||
|
local f_x = m:formvalue("_newfwd.submit")
|
||||||
|
|
||||||
|
if i_x then
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
|
||||||
|
self.map:set(created, "target", "ACCEPT")
|
||||||
|
self.map:set(created, "src", "wan")
|
||||||
|
self.map:set(created, "proto", (i_p ~= "other") and i_p or "all")
|
||||||
|
self.map:set(created, "dest_port", i_e)
|
||||||
|
self.map:set(created, "name", i_n)
|
||||||
|
|
||||||
|
if i_p ~= "other" and i_e and #i_e > 0 then
|
||||||
|
created = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif f_x then
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
|
||||||
|
self.map:set(created, "target", "ACCEPT")
|
||||||
|
self.map:set(created, "src", f_s)
|
||||||
|
self.map:set(created, "dest", f_d)
|
||||||
|
self.map:set(created, "name", f_n)
|
||||||
|
end
|
||||||
|
|
||||||
|
if created then
|
||||||
|
m.uci:save("firewall")
|
||||||
|
luci.http.redirect(ds.build_url(
|
||||||
|
"admin/network/firewall/rules", created
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ft.opt_name(s, DummyValue, translate("Name"))
|
||||||
|
|
||||||
|
local function rule_proto_txt(self, s)
|
||||||
|
local f = self.map:get(s, "family")
|
||||||
|
local p = ft.fmt_proto(self.map:get(s, "proto"),
|
||||||
|
self.map:get(s, "icmp_type")) or "TCP+UDP"
|
||||||
|
|
||||||
|
if f and f:match("4") then
|
||||||
|
return "%s-%s" %{ translate("IPv4"), p }
|
||||||
|
elseif f and f:match("6") then
|
||||||
|
return "%s-%s" %{ translate("IPv6"), p }
|
||||||
|
else
|
||||||
|
return "%s %s" %{ translate("Any"), p }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rule_src_txt(self, s)
|
||||||
|
local z = ft.fmt_zone(self.map:get(s, "src"), translate("any zone"))
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "src_ip"), translate("any host"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "src_port"))
|
||||||
|
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
|
||||||
|
|
||||||
|
if p and m then
|
||||||
|
return translatef("From %s in %s with source %s and %s", a, z, p, m)
|
||||||
|
elseif p or m then
|
||||||
|
return translatef("From %s in %s with source %s", a, z, p or m)
|
||||||
|
else
|
||||||
|
return translatef("From %s in %s", a, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rule_dest_txt(self, s)
|
||||||
|
local z = ft.fmt_zone(self.map:get(s, "dest"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "dest_port"))
|
||||||
|
|
||||||
|
-- Forward
|
||||||
|
if z then
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
|
||||||
|
if p then
|
||||||
|
return translatef("To %s, %s in %s", a, p, z)
|
||||||
|
else
|
||||||
|
return translatef("To %s in %s", a, z)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Input
|
||||||
|
else
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "dest_ip"),
|
||||||
|
translate("any router IP"))
|
||||||
|
|
||||||
|
if p then
|
||||||
|
return translatef("To %s at %s on <var>this device</var>", a, p)
|
||||||
|
else
|
||||||
|
return translatef("To %s on <var>this device</var>", a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function snat_dest_txt(self, s)
|
||||||
|
local z = ft.fmt_zone(self.map:get(s, "dest"), translate("any zone"))
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "dest_ip"), translate("any host"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
|
||||||
|
ft.fmt_port(self.map:get(s, "src_dport"))
|
||||||
|
|
||||||
|
if p then
|
||||||
|
return translatef("To %s, %s in %s", a, p, z)
|
||||||
|
else
|
||||||
|
return translatef("To %s in %s", a, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
match = s:option(DummyValue, "match", translate("Match"))
|
||||||
|
match.rawhtml = true
|
||||||
|
match.width = "70%"
|
||||||
|
function match.cfgvalue(self, s)
|
||||||
|
return "<small>%s<br />%s<br />%s</small>" % {
|
||||||
|
rule_proto_txt(self, s),
|
||||||
|
rule_src_txt(self, s),
|
||||||
|
rule_dest_txt(self, s)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
target = s:option(DummyValue, "target", translate("Action"))
|
||||||
|
target.rawhtml = true
|
||||||
|
target.width = "20%"
|
||||||
|
function target.cfgvalue(self, s)
|
||||||
|
local t = ft.fmt_target(self.map:get(s, "target"), self.map:get(s, "dest"))
|
||||||
|
local l = ft.fmt_limit(self.map:get(s, "limit"),
|
||||||
|
self.map:get(s, "limit_burst"))
|
||||||
|
|
||||||
|
if l then
|
||||||
|
return translatef("<var>%s</var> and limit to %s", t, l)
|
||||||
|
else
|
||||||
|
return "<var>%s</var>" % t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- SNAT
|
||||||
|
--
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "redirect",
|
||||||
|
translate("Source NAT"),
|
||||||
|
translate("Source NAT is a specific form of masquerading which allows \
|
||||||
|
fine grained control over the source IP used for outgoing traffic, \
|
||||||
|
for example to map multiple WAN addresses to internal subnets."))
|
||||||
|
s.template = "cbi/tblsection"
|
||||||
|
s.addremove = true
|
||||||
|
s.anonymous = true
|
||||||
|
s.sortable = true
|
||||||
|
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
|
||||||
|
s.template_addremove = "firewall/cbi_addsnat"
|
||||||
|
|
||||||
|
function s.create(self, section)
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.parse(self, ...)
|
||||||
|
TypedSection.parse(self, ...)
|
||||||
|
|
||||||
|
local n = m:formvalue("_newsnat.name")
|
||||||
|
local s = m:formvalue("_newsnat.src")
|
||||||
|
local d = m:formvalue("_newsnat.dest")
|
||||||
|
local a = m:formvalue("_newsnat.dip")
|
||||||
|
local p = m:formvalue("_newsnat.dport")
|
||||||
|
local x = m:formvalue("_newsnat.submit")
|
||||||
|
|
||||||
|
if x and a and #a > 0 then
|
||||||
|
created = TypedSection.create(self, section)
|
||||||
|
|
||||||
|
self.map:set(created, "target", "SNAT")
|
||||||
|
self.map:set(created, "src", s)
|
||||||
|
self.map:set(created, "dest", d)
|
||||||
|
self.map:set(created, "proto", "all")
|
||||||
|
self.map:set(created, "src_dip", a)
|
||||||
|
self.map:set(created, "src_dport", p)
|
||||||
|
self.map:set(created, "name", n)
|
||||||
|
end
|
||||||
|
|
||||||
|
if created then
|
||||||
|
m.uci:save("firewall")
|
||||||
|
luci.http.redirect(ds.build_url(
|
||||||
|
"admin/network/firewall/rules", created
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.filter(self, sid)
|
||||||
|
return (self.map:get(sid, "target") == "SNAT")
|
||||||
|
end
|
||||||
|
|
||||||
|
ft.opt_name(s, DummyValue, translate("Name"))
|
||||||
|
|
||||||
|
match = s:option(DummyValue, "match", translate("Match"))
|
||||||
|
match.rawhtml = true
|
||||||
|
match.width = "70%"
|
||||||
|
function match.cfgvalue(self, s)
|
||||||
|
return "<small>%s<br />%s<br />%s</small>" % {
|
||||||
|
rule_proto_txt(self, s),
|
||||||
|
rule_src_txt(self, s),
|
||||||
|
snat_dest_txt(self, s)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
snat = s:option(DummyValue, "via", translate("Action"))
|
||||||
|
snat.rawhtml = true
|
||||||
|
snat.width = "20%"
|
||||||
|
function snat.cfgvalue(self, s)
|
||||||
|
local a = ft.fmt_ip(self.map:get(s, "src_dip"))
|
||||||
|
local p = ft.fmt_port(self.map:get(s, "src_dport"))
|
||||||
|
|
||||||
|
if a and p then
|
||||||
|
return translatef("Rewrite to source %s, %s", a, p)
|
||||||
|
else
|
||||||
|
return translatef("Rewrite to source %s", a or p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ft.opt_enabled(s, Flag, translate("Enable")).width = "1%"
|
||||||
|
|
||||||
|
|
||||||
|
return m
|
243
1_1.mi_Lua/luci/model/cbi/firewall/zone-details.lua
Normal file
243
1_1.mi_Lua/luci/model/cbi/firewall/zone-details.lua
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2010-2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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: zone-details.lua 8169 2012-01-09 05:48:27Z jow $
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local nw = require "luci.model.network"
|
||||||
|
local fw = require "luci.model.firewall"
|
||||||
|
local ds = require "luci.dispatcher"
|
||||||
|
local ut = require "luci.util"
|
||||||
|
|
||||||
|
local m, p, i, v
|
||||||
|
local s, name, net, family, msrc, mdest, log, lim
|
||||||
|
local s2, out, inp
|
||||||
|
|
||||||
|
|
||||||
|
m = Map("firewall", translate("Firewall - Zone Settings"))
|
||||||
|
m.redirect = luci.dispatcher.build_url("admin/network/firewall/zones")
|
||||||
|
|
||||||
|
fw.init(m.uci)
|
||||||
|
nw.init(m.uci)
|
||||||
|
|
||||||
|
|
||||||
|
local zone = fw:get_zone(arg[1])
|
||||||
|
if not zone then
|
||||||
|
luci.http.redirect(dsp.build_url("admin/network/firewall/zones"))
|
||||||
|
return
|
||||||
|
else
|
||||||
|
m.title = "%s - %s" %{
|
||||||
|
translate("Firewall - Zone Settings"),
|
||||||
|
translatef("Zone %q", zone:name() or "?")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
s = m:section(NamedSection, zone.sid, "zone",
|
||||||
|
translatef("Zone %q", zone:name()),
|
||||||
|
translatef("This section defines common properties of %q. \
|
||||||
|
The <em>input</em> and <em>output</em> options set the default \
|
||||||
|
policies for traffic entering and leaving this zone while the \
|
||||||
|
<em>forward</em> option describes the policy for forwarded traffic \
|
||||||
|
between different networks within the zone. \
|
||||||
|
<em>Covered networks</em> specifies which available networks are \
|
||||||
|
member of this zone.", zone:name()))
|
||||||
|
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
m.on_commit = function(map)
|
||||||
|
local zone = fw:get_zone(arg[1])
|
||||||
|
if zone then
|
||||||
|
s.section = zone.sid
|
||||||
|
s2.section = zone.sid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
s:tab("general", translate("General Settings"))
|
||||||
|
s:tab("advanced", translate("Advanced Settings"))
|
||||||
|
|
||||||
|
|
||||||
|
name = s:taboption("general", Value, "name", translate("Name"))
|
||||||
|
name.optional = false
|
||||||
|
name.forcewrite = true
|
||||||
|
name.datatype = "uciname"
|
||||||
|
|
||||||
|
function name.write(self, section, value)
|
||||||
|
if zone:name() ~= value then
|
||||||
|
fw:rename_zone(zone:name(), value)
|
||||||
|
out.exclude = value
|
||||||
|
inp.exclude = value
|
||||||
|
end
|
||||||
|
|
||||||
|
m.redirect = ds.build_url("admin/network/firewall/zones", value)
|
||||||
|
m.title = "%s - %s" %{
|
||||||
|
translate("Firewall - Zone Settings"),
|
||||||
|
translatef("Zone %q", value or "?")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
p = {
|
||||||
|
s:taboption("general", ListValue, "input", translate("Input")),
|
||||||
|
s:taboption("general", ListValue, "output", translate("Output")),
|
||||||
|
s:taboption("general", ListValue, "forward", translate("Forward"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v in ipairs(p) do
|
||||||
|
v:value("REJECT", translate("reject"))
|
||||||
|
v:value("DROP", translate("drop"))
|
||||||
|
v:value("ACCEPT", translate("accept"))
|
||||||
|
end
|
||||||
|
|
||||||
|
s:taboption("general", Flag, "masq", translate("Masquerading"))
|
||||||
|
s:taboption("general", Flag, "mtu_fix", translate("MSS clamping"))
|
||||||
|
|
||||||
|
net = s:taboption("general", Value, "network", translate("Covered networks"))
|
||||||
|
net.template = "cbi/network_netlist"
|
||||||
|
net.widget = "checkbox"
|
||||||
|
net.cast = "string"
|
||||||
|
|
||||||
|
function net.formvalue(self, section)
|
||||||
|
return Value.formvalue(self, section) or "-"
|
||||||
|
end
|
||||||
|
|
||||||
|
function net.cfgvalue(self, section)
|
||||||
|
return Value.cfgvalue(self, section) or name:cfgvalue(section)
|
||||||
|
end
|
||||||
|
|
||||||
|
function net.write(self, section, value)
|
||||||
|
zone:clear_networks()
|
||||||
|
|
||||||
|
local n
|
||||||
|
for n in ut.imatch(value) do
|
||||||
|
zone:add_network(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
family = s:taboption("advanced", ListValue, "family",
|
||||||
|
translate("Restrict to address family"))
|
||||||
|
|
||||||
|
family.rmempty = true
|
||||||
|
family:value("", translate("IPv4 and IPv6"))
|
||||||
|
family:value("ipv4", translate("IPv4 only"))
|
||||||
|
family:value("ipv6", translate("IPv6 only"))
|
||||||
|
|
||||||
|
msrc = s:taboption("advanced", DynamicList, "masq_src",
|
||||||
|
translate("Restrict Masquerading to given source subnets"))
|
||||||
|
|
||||||
|
msrc.optional = true
|
||||||
|
msrc.datatype = "list(neg(or(uciname,hostname,ip4addr)))"
|
||||||
|
msrc.placeholder = "0.0.0.0/0"
|
||||||
|
msrc:depends("family", "")
|
||||||
|
msrc:depends("family", "ipv4")
|
||||||
|
|
||||||
|
mdest = s:taboption("advanced", DynamicList, "masq_dest",
|
||||||
|
translate("Restrict Masquerading to given destination subnets"))
|
||||||
|
|
||||||
|
mdest.optional = true
|
||||||
|
mdest.datatype = "list(neg(or(uciname,hostname,ip4addr)))"
|
||||||
|
mdest.placeholder = "0.0.0.0/0"
|
||||||
|
mdest:depends("family", "")
|
||||||
|
mdest:depends("family", "ipv4")
|
||||||
|
|
||||||
|
s:taboption("advanced", Flag, "conntrack",
|
||||||
|
translate("Force connection tracking"))
|
||||||
|
|
||||||
|
log = s:taboption("advanced", Flag, "log",
|
||||||
|
translate("Enable logging on this zone"))
|
||||||
|
|
||||||
|
log.rmempty = true
|
||||||
|
log.enabled = "1"
|
||||||
|
|
||||||
|
lim = s:taboption("advanced", Value, "log_limit",
|
||||||
|
translate("Limit log messages"))
|
||||||
|
|
||||||
|
lim.placeholder = "10/minute"
|
||||||
|
lim:depends("log", "1")
|
||||||
|
|
||||||
|
|
||||||
|
s2 = m:section(NamedSection, zone.sid, "fwd_out",
|
||||||
|
translate("Inter-Zone Forwarding"),
|
||||||
|
translatef("The options below control the forwarding policies between \
|
||||||
|
this zone (%s) and other zones. <em>Destination zones</em> cover \
|
||||||
|
forwarded traffic <strong>originating from %q</strong>. \
|
||||||
|
<em>Source zones</em> match forwarded traffic from other zones \
|
||||||
|
<strong>targeted at %q</strong>. The forwarding rule is \
|
||||||
|
<em>unidirectional</em>, e.g. a forward from lan to wan does \
|
||||||
|
<em>not</em> imply a permission to forward from wan to lan as well.",
|
||||||
|
zone:name(), zone:name(), zone:name()
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
|
out = s2:option(Value, "out",
|
||||||
|
translate("Allow forward to <em>destination zones</em>:"))
|
||||||
|
|
||||||
|
out.nocreate = true
|
||||||
|
out.widget = "checkbox"
|
||||||
|
out.exclude = zone:name()
|
||||||
|
out.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
inp = s2:option(Value, "in",
|
||||||
|
translate("Allow forward from <em>source zones</em>:"))
|
||||||
|
|
||||||
|
inp.nocreate = true
|
||||||
|
inp.widget = "checkbox"
|
||||||
|
inp.exclude = zone:name()
|
||||||
|
inp.template = "cbi/firewall_zonelist"
|
||||||
|
|
||||||
|
function out.cfgvalue(self, section)
|
||||||
|
local v = { }
|
||||||
|
local f
|
||||||
|
for _, f in ipairs(zone:get_forwardings_by("src")) do
|
||||||
|
v[#v+1] = f:dest()
|
||||||
|
end
|
||||||
|
return table.concat(v, " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
function inp.cfgvalue(self, section)
|
||||||
|
local v = { }
|
||||||
|
local f
|
||||||
|
for _, f in ipairs(zone:get_forwardings_by("dest")) do
|
||||||
|
v[#v+1] = f:src()
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
function out.formvalue(self, section)
|
||||||
|
return Value.formvalue(self, section) or "-"
|
||||||
|
end
|
||||||
|
|
||||||
|
function inp.formvalue(self, section)
|
||||||
|
return Value.formvalue(self, section) or "-"
|
||||||
|
end
|
||||||
|
|
||||||
|
function out.write(self, section, value)
|
||||||
|
zone:del_forwardings_by("src")
|
||||||
|
|
||||||
|
local f
|
||||||
|
for f in ut.imatch(value) do
|
||||||
|
zone:add_forwarding_to(f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function inp.write(self, section, value)
|
||||||
|
zone:del_forwardings_by("dest")
|
||||||
|
|
||||||
|
local f
|
||||||
|
for f in ut.imatch(value) do
|
||||||
|
zone:add_forwarding_from(f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
88
1_1.mi_Lua/luci/model/cbi/firewall/zones.lua
Normal file
88
1_1.mi_Lua/luci/model/cbi/firewall/zones.lua
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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: zones.lua 8108 2011-12-19 21:16:31Z jow $
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ds = require "luci.dispatcher"
|
||||||
|
local fw = require "luci.model.firewall"
|
||||||
|
|
||||||
|
local m, s, o, p, i, v
|
||||||
|
|
||||||
|
m = Map("firewall",
|
||||||
|
translate("Firewall - Zone Settings"),
|
||||||
|
translate("The firewall creates zones over your network interfaces to control network traffic flow."))
|
||||||
|
|
||||||
|
fw.init(m.uci)
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "defaults", translate("General Settings"))
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
s:option(Flag, "syn_flood", translate("Enable SYN-flood protection"))
|
||||||
|
|
||||||
|
o = s:option(Flag, "drop_invalid", translate("Drop invalid packets"))
|
||||||
|
o.default = o.disabled
|
||||||
|
|
||||||
|
p = {
|
||||||
|
s:option(ListValue, "input", translate("Input")),
|
||||||
|
s:option(ListValue, "output", translate("Output")),
|
||||||
|
s:option(ListValue, "forward", translate("Forward"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v in ipairs(p) do
|
||||||
|
v:value("REJECT", translate("reject"))
|
||||||
|
v:value("DROP", translate("drop"))
|
||||||
|
v:value("ACCEPT", translate("accept"))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "zone", translate("Zones"))
|
||||||
|
s.template = "cbi/tblsection"
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = true
|
||||||
|
s.extedit = ds.build_url("admin", "network", "firewall", "zones", "%s")
|
||||||
|
|
||||||
|
function s.create(self)
|
||||||
|
local z = fw:new_zone()
|
||||||
|
if z then
|
||||||
|
luci.http.redirect(
|
||||||
|
ds.build_url("admin", "network", "firewall", "zones", z.sid)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function s.remove(self, section)
|
||||||
|
return fw:del_zone(section)
|
||||||
|
end
|
||||||
|
|
||||||
|
o = s:option(DummyValue, "_info", translate("Zone ⇒ Forwardings"))
|
||||||
|
o.template = "cbi/firewall_zoneforwards"
|
||||||
|
o.cfgvalue = function(self, section)
|
||||||
|
return self.map:get(section, "name")
|
||||||
|
end
|
||||||
|
|
||||||
|
p = {
|
||||||
|
s:option(ListValue, "input", translate("Input")),
|
||||||
|
s:option(ListValue, "output", translate("Output")),
|
||||||
|
s:option(ListValue, "forward", translate("Forward"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v in ipairs(p) do
|
||||||
|
v:value("REJECT", translate("reject"))
|
||||||
|
v:value("DROP", translate("drop"))
|
||||||
|
v:value("ACCEPT", translate("accept"))
|
||||||
|
end
|
||||||
|
|
||||||
|
s:option(Flag, "masq", translate("Masquerading"))
|
||||||
|
s:option(Flag, "mtu_fix", translate("MSS clamping"))
|
||||||
|
|
||||||
|
return m
|
582
1_1.mi_Lua/luci/model/firewall.lua
Normal file
582
1_1.mi_Lua/luci/model/firewall.lua
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Firewall model
|
||||||
|
|
||||||
|
Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local type, pairs, ipairs, table, luci, math
|
||||||
|
= type, pairs, ipairs, table, luci, math
|
||||||
|
|
||||||
|
local tpl = require "luci.template.parser"
|
||||||
|
local utl = require "luci.util"
|
||||||
|
local uci = require "luci.model.uci"
|
||||||
|
|
||||||
|
module "luci.model.firewall"
|
||||||
|
|
||||||
|
|
||||||
|
local uci_r, uci_s
|
||||||
|
|
||||||
|
function _valid_id(x)
|
||||||
|
return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$"))
|
||||||
|
end
|
||||||
|
|
||||||
|
function _get(c, s, o)
|
||||||
|
return uci_r:get(c, s, o)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _set(c, s, o, v)
|
||||||
|
if v ~= nil then
|
||||||
|
if type(v) == "boolean" then v = v and "1" or "0" end
|
||||||
|
return uci_r:set(c, s, o, v)
|
||||||
|
else
|
||||||
|
return uci_r:delete(c, s, o)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function init(cursor)
|
||||||
|
uci_r = cursor or uci_r or uci.cursor()
|
||||||
|
uci_s = uci_r:substate()
|
||||||
|
|
||||||
|
return _M
|
||||||
|
end
|
||||||
|
|
||||||
|
function save(self, ...)
|
||||||
|
uci_r:save(...)
|
||||||
|
uci_r:load(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function commit(self, ...)
|
||||||
|
uci_r:commit(...)
|
||||||
|
uci_r:load(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_defaults()
|
||||||
|
return defaults()
|
||||||
|
end
|
||||||
|
|
||||||
|
function new_zone(self)
|
||||||
|
local name = "newzone"
|
||||||
|
local count = 1
|
||||||
|
|
||||||
|
while self:get_zone(name) do
|
||||||
|
count = count + 1
|
||||||
|
name = "newzone%d" % count
|
||||||
|
end
|
||||||
|
|
||||||
|
return self:add_zone(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_zone(self, n)
|
||||||
|
if _valid_id(n) and not self:get_zone(n) then
|
||||||
|
local d = defaults()
|
||||||
|
local z = uci_r:section("firewall", "zone", nil, {
|
||||||
|
name = n,
|
||||||
|
network = " ",
|
||||||
|
input = d:input() or "DROP",
|
||||||
|
forward = d:forward() or "DROP",
|
||||||
|
output = d:output() or "DROP"
|
||||||
|
})
|
||||||
|
|
||||||
|
return z and zone(z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_zone(self, n)
|
||||||
|
if uci_r:get("firewall", n) == "zone" then
|
||||||
|
return zone(n)
|
||||||
|
else
|
||||||
|
local z
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if n and s.name == n then
|
||||||
|
z = s['.name']
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return z and zone(z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_zones(self)
|
||||||
|
local zones = { }
|
||||||
|
local znl = { }
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if s.name then
|
||||||
|
znl[s.name] = zone(s['.name'])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local z
|
||||||
|
for z in utl.kspairs(znl) do
|
||||||
|
zones[#zones+1] = znl[z]
|
||||||
|
end
|
||||||
|
|
||||||
|
return zones
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_zone_by_network(self, net)
|
||||||
|
local z
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if s.name and net then
|
||||||
|
local n
|
||||||
|
for n in utl.imatch(s.network or s.name) do
|
||||||
|
if n == net then
|
||||||
|
z = s['.name']
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return z and zone(z)
|
||||||
|
end
|
||||||
|
|
||||||
|
function del_zone(self, n)
|
||||||
|
local r = false
|
||||||
|
|
||||||
|
if uci_r:get("firewall", n) == "zone" then
|
||||||
|
local z = uci_r:get("firewall", n, "name")
|
||||||
|
r = uci_r:delete("firewall", n)
|
||||||
|
n = z
|
||||||
|
else
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if n and s.name == n then
|
||||||
|
r = uci_r:delete("firewall", s['.name'])
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if r then
|
||||||
|
uci_r:foreach("firewall", "rule",
|
||||||
|
function(s)
|
||||||
|
if s.src == n or s.dest == n then
|
||||||
|
uci_r:delete("firewall", s['.name'])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "redirect",
|
||||||
|
function(s)
|
||||||
|
if s.src == n or s.dest == n then
|
||||||
|
uci_r:delete("firewall", s['.name'])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "forwarding",
|
||||||
|
function(s)
|
||||||
|
if s.src == n or s.dest == n then
|
||||||
|
uci_r:delete("firewall", s['.name'])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function rename_zone(self, old, new)
|
||||||
|
local r = false
|
||||||
|
|
||||||
|
if _valid_id(new) and not self:get_zone(new) then
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if old and s.name == old then
|
||||||
|
if not s.network then
|
||||||
|
uci_r:set("firewall", s['.name'], "network", old)
|
||||||
|
end
|
||||||
|
uci_r:set("firewall", s['.name'], "name", new)
|
||||||
|
r = true
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if r then
|
||||||
|
uci_r:foreach("firewall", "rule",
|
||||||
|
function(s)
|
||||||
|
if s.src == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "src", new)
|
||||||
|
end
|
||||||
|
if s.dest == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "dest", new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "redirect",
|
||||||
|
function(s)
|
||||||
|
if s.src == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "src", new)
|
||||||
|
end
|
||||||
|
if s.dest == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "dest", new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "forwarding",
|
||||||
|
function(s)
|
||||||
|
if s.src == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "src", new)
|
||||||
|
end
|
||||||
|
if s.dest == old then
|
||||||
|
uci_r:set("firewall", s['.name'], "dest", new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function del_network(self, net)
|
||||||
|
local z
|
||||||
|
if net then
|
||||||
|
for _, z in ipairs(self:get_zones()) do
|
||||||
|
z:del_network(net)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
defaults = utl.class()
|
||||||
|
function defaults.__init__(self)
|
||||||
|
uci_r:foreach("firewall", "defaults",
|
||||||
|
function(s)
|
||||||
|
self.sid = s['.name']
|
||||||
|
return false
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { })
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.get(self, opt)
|
||||||
|
return _get("firewall", self.sid, opt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.set(self, opt, val)
|
||||||
|
return _set("firewall", self.sid, opt, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.syn_flood(self)
|
||||||
|
return (self:get("syn_flood") == "1")
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.drop_invalid(self)
|
||||||
|
return (self:get("drop_invalid") == "1")
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.input(self)
|
||||||
|
return self:get("input") or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.forward(self)
|
||||||
|
return self:get("forward") or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaults.output(self)
|
||||||
|
return self:get("output") or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
zone = utl.class()
|
||||||
|
function zone.__init__(self, z)
|
||||||
|
if uci_r:get("firewall", z) == "zone" then
|
||||||
|
self.sid = z
|
||||||
|
self.data = uci_r:get_all("firewall", z)
|
||||||
|
else
|
||||||
|
uci_r:foreach("firewall", "zone",
|
||||||
|
function(s)
|
||||||
|
if s.name == z then
|
||||||
|
self.sid = s['.name']
|
||||||
|
self.data = s
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.get(self, opt)
|
||||||
|
return _get("firewall", self.sid, opt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.set(self, opt, val)
|
||||||
|
return _set("firewall", self.sid, opt, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.masq(self)
|
||||||
|
return (self:get("masq") == "1")
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.name(self)
|
||||||
|
return self:get("name")
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.network(self)
|
||||||
|
return self:get("network")
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.input(self)
|
||||||
|
return self:get("input") or defaults():input() or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.forward(self)
|
||||||
|
return self:get("forward") or defaults():forward() or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.output(self)
|
||||||
|
return self:get("output") or defaults():output() or "DROP"
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.add_network(self, net)
|
||||||
|
if uci_r:get("network", net) == "interface" then
|
||||||
|
local nets = { }
|
||||||
|
|
||||||
|
local n
|
||||||
|
for n in utl.imatch(self:get("network") or self:get("name")) do
|
||||||
|
if n ~= net then
|
||||||
|
nets[#nets+1] = n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nets[#nets+1] = net
|
||||||
|
|
||||||
|
_M:del_network(net)
|
||||||
|
self:set("network", table.concat(nets, " "))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.del_network(self, net)
|
||||||
|
local nets = { }
|
||||||
|
|
||||||
|
local n
|
||||||
|
for n in utl.imatch(self:get("network") or self:get("name")) do
|
||||||
|
if n ~= net then
|
||||||
|
nets[#nets+1] = n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #nets > 0 then
|
||||||
|
self:set("network", table.concat(nets, " "))
|
||||||
|
else
|
||||||
|
self:set("network", " ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.get_networks(self)
|
||||||
|
local nets = { }
|
||||||
|
|
||||||
|
local n
|
||||||
|
for n in utl.imatch(self:get("network") or self:get("name")) do
|
||||||
|
nets[#nets+1] = n
|
||||||
|
end
|
||||||
|
|
||||||
|
return nets
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.clear_networks(self)
|
||||||
|
self:set("network", " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.get_forwardings_by(self, what)
|
||||||
|
local name = self:name()
|
||||||
|
local forwards = { }
|
||||||
|
|
||||||
|
uci_r:foreach("firewall", "forwarding",
|
||||||
|
function(s)
|
||||||
|
if s.src and s.dest and s[what] == name then
|
||||||
|
forwards[#forwards+1] = forwarding(s['.name'])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return forwards
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.add_forwarding_to(self, dest)
|
||||||
|
local exist, forward
|
||||||
|
|
||||||
|
for _, forward in ipairs(self:get_forwardings_by('src')) do
|
||||||
|
if forward:dest() == dest then
|
||||||
|
exist = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not exist and dest ~= self:name() and _valid_id(dest) then
|
||||||
|
local s = uci_r:section("firewall", "forwarding", nil, {
|
||||||
|
src = self:name(),
|
||||||
|
dest = dest
|
||||||
|
})
|
||||||
|
|
||||||
|
return s and forwarding(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.add_forwarding_from(self, src)
|
||||||
|
local exist, forward
|
||||||
|
|
||||||
|
for _, forward in ipairs(self:get_forwardings_by('dest')) do
|
||||||
|
if forward:src() == src then
|
||||||
|
exist = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not exist and src ~= self:name() and _valid_id(src) then
|
||||||
|
local s = uci_r:section("firewall", "forwarding", nil, {
|
||||||
|
src = src,
|
||||||
|
dest = self:name()
|
||||||
|
})
|
||||||
|
|
||||||
|
return s and forwarding(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.del_forwardings_by(self, what)
|
||||||
|
local name = self:name()
|
||||||
|
|
||||||
|
uci_r:delete_all("firewall", "forwarding",
|
||||||
|
function(s)
|
||||||
|
return (s.src and s.dest and s[what] == name)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.add_redirect(self, options)
|
||||||
|
options = options or { }
|
||||||
|
options.src = self:name()
|
||||||
|
|
||||||
|
local s = uci_r:section("firewall", "redirect", nil, options)
|
||||||
|
return s and redirect(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.add_rule(self, options)
|
||||||
|
options = options or { }
|
||||||
|
options.src = self:name()
|
||||||
|
|
||||||
|
local s = uci_r:section("firewall", "rule", nil, options)
|
||||||
|
return s and rule(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
function zone.get_color(self)
|
||||||
|
if self and self:name() == "lan" then
|
||||||
|
return "#90f090"
|
||||||
|
elseif self and self:name() == "wan" then
|
||||||
|
return "#f09090"
|
||||||
|
elseif self then
|
||||||
|
math.randomseed(tpl.hash(self:name()))
|
||||||
|
|
||||||
|
local r = math.random(128)
|
||||||
|
local g = math.random(128)
|
||||||
|
local min = 0
|
||||||
|
local max = 128
|
||||||
|
|
||||||
|
if ( r + g ) < 128 then
|
||||||
|
min = 128 - r - g
|
||||||
|
else
|
||||||
|
max = 255 - r - g
|
||||||
|
end
|
||||||
|
|
||||||
|
local b = min + math.floor( math.random() * ( max - min ) )
|
||||||
|
|
||||||
|
return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
|
||||||
|
else
|
||||||
|
return "#eeeeee"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
forwarding = utl.class()
|
||||||
|
function forwarding.__init__(self, f)
|
||||||
|
self.sid = f
|
||||||
|
end
|
||||||
|
|
||||||
|
function forwarding.src(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "src")
|
||||||
|
end
|
||||||
|
|
||||||
|
function forwarding.dest(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "dest")
|
||||||
|
end
|
||||||
|
|
||||||
|
function forwarding.src_zone(self)
|
||||||
|
return zone(self:src())
|
||||||
|
end
|
||||||
|
|
||||||
|
function forwarding.dest_zone(self)
|
||||||
|
return zone(self:dest())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
rule = utl.class()
|
||||||
|
function rule.__init__(self, f)
|
||||||
|
self.sid = f
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.get(self, opt)
|
||||||
|
return _get("firewall", self.sid, opt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.set(self, opt, val)
|
||||||
|
return _set("firewall", self.sid, opt, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.src(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "src")
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.dest(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "dest")
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.src_zone(self)
|
||||||
|
return zone(self:src())
|
||||||
|
end
|
||||||
|
|
||||||
|
function rule.dest_zone(self)
|
||||||
|
return zone(self:dest())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
redirect = utl.class()
|
||||||
|
function redirect.__init__(self, f)
|
||||||
|
self.sid = f
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.get(self, opt)
|
||||||
|
return _get("firewall", self.sid, opt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.set(self, opt, val)
|
||||||
|
return _set("firewall", self.sid, opt, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.src(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "src")
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.dest(self)
|
||||||
|
return uci_r:get("firewall", self.sid, "dest")
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.src_zone(self)
|
||||||
|
return zone(self:src())
|
||||||
|
end
|
||||||
|
|
||||||
|
function redirect.dest_zone(self)
|
||||||
|
return zone(self:dest())
|
||||||
|
end
|
239
1_1.mi_Lua/luci/model/ipkg.lua
Normal file
239
1_1.mi_Lua/luci/model/ipkg.lua
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
(c) 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
(c) 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local os = require "os"
|
||||||
|
local io = require "io"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local util = require "luci.util"
|
||||||
|
|
||||||
|
local type = type
|
||||||
|
local pairs = pairs
|
||||||
|
local error = error
|
||||||
|
local table = table
|
||||||
|
|
||||||
|
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
|
||||||
|
local icfg = "/etc/opkg.conf"
|
||||||
|
|
||||||
|
--- LuCI OPKG call abstraction library
|
||||||
|
module "luci.model.ipkg"
|
||||||
|
|
||||||
|
|
||||||
|
-- Internal action function
|
||||||
|
local function _action(cmd, ...)
|
||||||
|
local pkg = ""
|
||||||
|
for k, v in pairs({...}) do
|
||||||
|
pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg }
|
||||||
|
local r = os.execute(c)
|
||||||
|
local e = fs.readfile("/tmp/opkg.stderr")
|
||||||
|
local o = fs.readfile("/tmp/opkg.stdout")
|
||||||
|
|
||||||
|
fs.unlink("/tmp/opkg.stderr")
|
||||||
|
fs.unlink("/tmp/opkg.stdout")
|
||||||
|
|
||||||
|
return r, o or "", e or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Internal parser function
|
||||||
|
local function _parselist(rawdata)
|
||||||
|
if type(rawdata) ~= "function" then
|
||||||
|
error("OPKG: Invalid rawdata given")
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = {}
|
||||||
|
local c = {}
|
||||||
|
local l = nil
|
||||||
|
|
||||||
|
for line in rawdata do
|
||||||
|
if line:sub(1, 1) ~= " " then
|
||||||
|
local key, val = line:match("(.-): ?(.*)%s*")
|
||||||
|
|
||||||
|
if key and val then
|
||||||
|
if key == "Package" then
|
||||||
|
c = {Package = val}
|
||||||
|
data[val] = c
|
||||||
|
elseif key == "Status" then
|
||||||
|
c.Status = {}
|
||||||
|
for j in val:gmatch("([^ ]+)") do
|
||||||
|
c.Status[j] = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
c[key] = val
|
||||||
|
end
|
||||||
|
l = key
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Multi-line field
|
||||||
|
c[l] = c[l] .. "\n" .. line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Internal lookup function
|
||||||
|
local function _lookup(act, pkg)
|
||||||
|
local cmd = ipkg .. " " .. act
|
||||||
|
if pkg then
|
||||||
|
cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- OPKG sometimes kills the whole machine because it sucks
|
||||||
|
-- Therefore we have to use a sucky approach too and use
|
||||||
|
-- tmpfiles instead of directly reading the output
|
||||||
|
local tmpfile = os.tmpname()
|
||||||
|
os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile))
|
||||||
|
|
||||||
|
local data = _parselist(io.lines(tmpfile))
|
||||||
|
os.remove(tmpfile)
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Return information about installed and available packages.
|
||||||
|
-- @param pkg Limit output to a (set of) packages
|
||||||
|
-- @return Table containing package information
|
||||||
|
function info(pkg)
|
||||||
|
return _lookup("info", pkg)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the package status of one or more packages.
|
||||||
|
-- @param pkg Limit output to a (set of) packages
|
||||||
|
-- @return Table containing package status information
|
||||||
|
function status(pkg)
|
||||||
|
return _lookup("status", pkg)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Install one or more packages.
|
||||||
|
-- @param ... List of packages to install
|
||||||
|
-- @return Boolean indicating the status of the action
|
||||||
|
-- @return OPKG return code, STDOUT and STDERR
|
||||||
|
function install(...)
|
||||||
|
return _action("install", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine whether a given package is installed.
|
||||||
|
-- @param pkg Package
|
||||||
|
-- @return Boolean
|
||||||
|
function installed(pkg)
|
||||||
|
local p = status(pkg)[pkg]
|
||||||
|
return (p and p.Status and p.Status.installed)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove one or more packages.
|
||||||
|
-- @param ... List of packages to install
|
||||||
|
-- @return Boolean indicating the status of the action
|
||||||
|
-- @return OPKG return code, STDOUT and STDERR
|
||||||
|
function remove(...)
|
||||||
|
return _action("remove", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update package lists.
|
||||||
|
-- @return Boolean indicating the status of the action
|
||||||
|
-- @return OPKG return code, STDOUT and STDERR
|
||||||
|
function update()
|
||||||
|
return _action("update")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Upgrades all installed packages.
|
||||||
|
-- @return Boolean indicating the status of the action
|
||||||
|
-- @return OPKG return code, STDOUT and STDERR
|
||||||
|
function upgrade()
|
||||||
|
return _action("upgrade")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- List helper
|
||||||
|
function _list(action, pat, cb)
|
||||||
|
local fd = io.popen(ipkg .. " " .. action ..
|
||||||
|
(pat and (" '%s'" % pat:gsub("'", "")) or ""))
|
||||||
|
|
||||||
|
if fd then
|
||||||
|
local name, version, desc
|
||||||
|
while true do
|
||||||
|
local line = fd:read("*l")
|
||||||
|
if not line then break end
|
||||||
|
|
||||||
|
name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
|
||||||
|
|
||||||
|
if not name then
|
||||||
|
name, version = line:match("^(.-) %- (.+)")
|
||||||
|
desc = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
cb(name, version, desc)
|
||||||
|
|
||||||
|
name = nil
|
||||||
|
version = nil
|
||||||
|
desc = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
fd:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- List all packages known to opkg.
|
||||||
|
-- @param pat Only find packages matching this pattern, nil lists all packages
|
||||||
|
-- @param cb Callback function invoked for each package, receives name, version and description as arguments
|
||||||
|
-- @return nothing
|
||||||
|
function list_all(pat, cb)
|
||||||
|
_list("list", pat, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- List installed packages.
|
||||||
|
-- @param pat Only find packages matching this pattern, nil lists all packages
|
||||||
|
-- @param cb Callback function invoked for each package, receives name, version and description as arguments
|
||||||
|
-- @return nothing
|
||||||
|
function list_installed(pat, cb)
|
||||||
|
_list("list_installed", pat, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find packages that match the given pattern.
|
||||||
|
-- @param pat Find packages whose names or descriptions match this pattern, nil results in zero results
|
||||||
|
-- @param cb Callback function invoked for each patckage, receives name, version and description as arguments
|
||||||
|
-- @return nothing
|
||||||
|
function find(pat, cb)
|
||||||
|
_list("find", pat, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Determines the overlay root used by opkg.
|
||||||
|
-- @return String containing the directory path of the overlay root.
|
||||||
|
function overlay_root()
|
||||||
|
local od = "/"
|
||||||
|
local fd = io.open(icfg, "r")
|
||||||
|
|
||||||
|
if fd then
|
||||||
|
local ln
|
||||||
|
|
||||||
|
repeat
|
||||||
|
ln = fd:read("*l")
|
||||||
|
if ln and ln:match("^%s*option%s+overlay_root%s+") then
|
||||||
|
od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
|
||||||
|
|
||||||
|
local s = fs.stat(od)
|
||||||
|
if not s or s.type ~= "dir" then
|
||||||
|
od = "/"
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
until not ln
|
||||||
|
|
||||||
|
fd:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return od
|
||||||
|
end
|
1639
1_1.mi_Lua/luci/model/network.lua
Normal file
1639
1_1.mi_Lua/luci/model/network.lua
Normal file
File diff suppressed because it is too large
Load Diff
104
1_1.mi_Lua/luci/model/network/proto_ppp.lua
Normal file
104
1_1.mi_Lua/luci/model/network/proto_ppp.lua
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Network model - 3G, PPP, PPtP, PPPoE and PPPoA protocol extension
|
||||||
|
|
||||||
|
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local netmod = luci.model.network
|
||||||
|
|
||||||
|
local _, p
|
||||||
|
for _, p in ipairs({"ppp", "pptp", "pppoe", "pppoa", "3g", "l2tp"}) do
|
||||||
|
|
||||||
|
local proto = netmod:register_protocol(p)
|
||||||
|
|
||||||
|
function proto.get_i18n(self)
|
||||||
|
if p == "ppp" then
|
||||||
|
return luci.i18n.translate("PPP")
|
||||||
|
elseif p == "pptp" then
|
||||||
|
return luci.i18n.translate("PPtP")
|
||||||
|
elseif p == "3g" then
|
||||||
|
return luci.i18n.translate("UMTS/GPRS/EV-DO")
|
||||||
|
elseif p == "pppoe" then
|
||||||
|
return luci.i18n.translate("PPPoE")
|
||||||
|
elseif p == "pppoa" then
|
||||||
|
return luci.i18n.translate("PPPoATM")
|
||||||
|
elseif p == "l2tp" then
|
||||||
|
return luci.i18n.translate("L2TP")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.ifname(self)
|
||||||
|
return p .. "-" .. self.sid
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.opkg_package(self)
|
||||||
|
if p == "ppp" then
|
||||||
|
return p
|
||||||
|
elseif p == "3g" then
|
||||||
|
return "comgt"
|
||||||
|
elseif p == "pptp" then
|
||||||
|
return "ppp-mod-pptp"
|
||||||
|
elseif p == "pppoe" then
|
||||||
|
return "ppp-mod-pppoe"
|
||||||
|
elseif p == "pppoa" then
|
||||||
|
return "ppp-mod-pppoa"
|
||||||
|
elseif p == "l2tp" then
|
||||||
|
return "xl2tpd"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.is_installed(self)
|
||||||
|
if p == "pppoa" then
|
||||||
|
return (nixio.fs.glob("/usr/lib/pppd/*/pppoatm.so")() ~= nil)
|
||||||
|
elseif p == "pppoe" then
|
||||||
|
return (nixio.fs.glob("/usr/lib/pppd/*/rp-pppoe.so")() ~= nil)
|
||||||
|
elseif p == "pptp" then
|
||||||
|
return (nixio.fs.glob("/usr/lib/pppd/*/pptp.so")() ~= nil)
|
||||||
|
elseif p == "3g" then
|
||||||
|
return nixio.fs.access("/lib/netifd/proto/3g.sh")
|
||||||
|
elseif p == "l2tp" then
|
||||||
|
return nixio.fs.access("/lib/netifd/proto/l2tp.sh")
|
||||||
|
else
|
||||||
|
return nixio.fs.access("/lib/netifd/proto/ppp.sh")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.is_floating(self)
|
||||||
|
return (p ~= "pppoe")
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.is_virtual(self)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.get_interfaces(self)
|
||||||
|
if self:is_floating() then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return netmod.protocol.get_interfaces(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function proto.contains_interface(self, ifc)
|
||||||
|
if self:is_floating() then
|
||||||
|
return (netmod:ifnameof(ifc) == self:ifname())
|
||||||
|
else
|
||||||
|
return netmod.protocol.contains_interface(self, ifc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
netmod:register_pattern_virtual("^%s-%%w" % p)
|
||||||
|
end
|
404
1_1.mi_Lua/luci/model/uci.lua
Normal file
404
1_1.mi_Lua/luci/model/uci.lua
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - UCI model
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Generalized UCI model
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: uci.lua 8127 2011-12-20 19:02:14Z jow $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
local os = require "os"
|
||||||
|
local uci = require "uci"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
|
|
||||||
|
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||||||
|
local require, getmetatable = require, getmetatable
|
||||||
|
local error, pairs, ipairs = error, pairs, ipairs
|
||||||
|
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
|
||||||
|
|
||||||
|
--- LuCI UCI model library.
|
||||||
|
-- The typical workflow for UCI is: Get a cursor instance from the
|
||||||
|
-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
|
||||||
|
-- save the changes to the staging area via Cursor.save and finally
|
||||||
|
-- Cursor.commit the data to the actual config files.
|
||||||
|
-- LuCI then needs to Cursor.apply the changes so deamons etc. are
|
||||||
|
-- reloaded.
|
||||||
|
-- @cstyle instance
|
||||||
|
module "luci.model.uci"
|
||||||
|
|
||||||
|
--- Create a new UCI-Cursor.
|
||||||
|
-- @class function
|
||||||
|
-- @name cursor
|
||||||
|
-- @return UCI-Cursor
|
||||||
|
cursor = uci.cursor
|
||||||
|
|
||||||
|
APIVERSION = uci.APIVERSION
|
||||||
|
|
||||||
|
--- Create a new Cursor initialized to the state directory.
|
||||||
|
-- @return UCI cursor
|
||||||
|
function cursor_state()
|
||||||
|
return cursor(nil, "/var/state")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
inst = cursor()
|
||||||
|
inst_state = cursor_state()
|
||||||
|
|
||||||
|
local Cursor = getmetatable(inst)
|
||||||
|
|
||||||
|
--- Applies UCI configuration changes
|
||||||
|
-- @param configlist List of UCI configurations
|
||||||
|
-- @param command Don't apply only return the command
|
||||||
|
function Cursor.apply(self, configlist, command)
|
||||||
|
configlist = self:_affected(configlist)
|
||||||
|
if command then
|
||||||
|
return { "/sbin/luci-reload", unpack(configlist) }
|
||||||
|
else
|
||||||
|
return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
|
||||||
|
% table.concat(configlist, " "))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Delete all sections of a given type that match certain criteria.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param type UCI section type
|
||||||
|
-- @param comparator Function that will be called for each section and
|
||||||
|
-- returns a boolean whether to delete the current section (optional)
|
||||||
|
function Cursor.delete_all(self, config, stype, comparator)
|
||||||
|
local del = {}
|
||||||
|
|
||||||
|
if type(comparator) == "table" then
|
||||||
|
local tbl = comparator
|
||||||
|
comparator = function(section)
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if section[k] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function helper (section)
|
||||||
|
|
||||||
|
if not comparator or comparator(section) then
|
||||||
|
del[#del+1] = section[".name"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:foreach(config, stype, helper)
|
||||||
|
|
||||||
|
for i, j in ipairs(del) do
|
||||||
|
self:delete(config, j)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new section and initialize it with data.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param type UCI section type
|
||||||
|
-- @param name UCI section name (optional)
|
||||||
|
-- @param values Table of key - value pairs to initialize the section with
|
||||||
|
-- @return Name of created section
|
||||||
|
function Cursor.section(self, config, type, name, values)
|
||||||
|
local stat = true
|
||||||
|
if name then
|
||||||
|
stat = self:set(config, name, type)
|
||||||
|
else
|
||||||
|
name = self:add(config, type)
|
||||||
|
stat = name and true
|
||||||
|
end
|
||||||
|
|
||||||
|
if stat and values then
|
||||||
|
stat = self:tset(config, name, values)
|
||||||
|
end
|
||||||
|
|
||||||
|
return stat and name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updated the data of a section using data from a table.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name (optional)
|
||||||
|
-- @param values Table of key - value pairs to update the section with
|
||||||
|
function Cursor.tset(self, config, section, values)
|
||||||
|
local stat = true
|
||||||
|
for k, v in pairs(values) do
|
||||||
|
if k:sub(1, 1) ~= "." then
|
||||||
|
stat = stat and self:set(config, section, k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return stat
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a boolean option and return it's value as true or false.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option
|
||||||
|
-- @return Boolean
|
||||||
|
function Cursor.get_bool(self, ...)
|
||||||
|
local val = self:get(...)
|
||||||
|
return ( val == "1" or val == "true" or val == "yes" or val == "on" )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get an option or list and return values as table.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option
|
||||||
|
-- @return UCI value
|
||||||
|
function Cursor.get_list(self, config, section, option)
|
||||||
|
if config and section and option then
|
||||||
|
local val = self:get(config, section, option)
|
||||||
|
return ( type(val) == "table" and val or { val } )
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the given option from the first section with the given type.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param type UCI section type
|
||||||
|
-- @param option UCI option (optional)
|
||||||
|
-- @param default Default value (optional)
|
||||||
|
-- @return UCI value
|
||||||
|
function Cursor.get_first(self, conf, stype, opt, def)
|
||||||
|
local rv = def
|
||||||
|
|
||||||
|
self:foreach(conf, stype,
|
||||||
|
function(s)
|
||||||
|
local val = not opt and s['.name'] or s[opt]
|
||||||
|
|
||||||
|
if type(def) == "number" then
|
||||||
|
val = tonumber(val)
|
||||||
|
elseif type(def) == "boolean" then
|
||||||
|
val = (val == "1" or val == "true" or
|
||||||
|
val == "yes" or val == "on")
|
||||||
|
end
|
||||||
|
|
||||||
|
if val ~= nil then
|
||||||
|
rv = val
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set given values as list.
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option
|
||||||
|
-- @param value UCI value
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
function Cursor.set_list(self, config, section, option, value)
|
||||||
|
if config and section and option then
|
||||||
|
return self:set(
|
||||||
|
config, section, option,
|
||||||
|
( type(value) == "table" and value or { value } )
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return a list of initscripts affected by configuration changes.
|
||||||
|
function Cursor._affected(self, configlist)
|
||||||
|
configlist = type(configlist) == "table" and configlist or {configlist}
|
||||||
|
|
||||||
|
local c = cursor()
|
||||||
|
c:load("ucitrack")
|
||||||
|
|
||||||
|
-- Resolve dependencies
|
||||||
|
local reloadlist = {}
|
||||||
|
|
||||||
|
local function _resolve_deps(name)
|
||||||
|
local reload = {name}
|
||||||
|
local deps = {}
|
||||||
|
|
||||||
|
c:foreach("ucitrack", name,
|
||||||
|
function(section)
|
||||||
|
if section.affects then
|
||||||
|
for i, aff in ipairs(section.affects) do
|
||||||
|
deps[#deps+1] = aff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
for i, dep in ipairs(deps) do
|
||||||
|
for j, add in ipairs(_resolve_deps(dep)) do
|
||||||
|
reload[#reload+1] = add
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return reload
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Collect initscripts
|
||||||
|
for j, config in ipairs(configlist) do
|
||||||
|
for i, e in ipairs(_resolve_deps(config)) do
|
||||||
|
if not util.contains(reloadlist, e) then
|
||||||
|
reloadlist[#reloadlist+1] = e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return reloadlist
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a sub-state of this cursor. The sub-state is tied to the parent
|
||||||
|
-- curser, means it the parent unloads or loads configs, the sub state will
|
||||||
|
-- do so as well.
|
||||||
|
-- @return UCI state cursor tied to the parent cursor
|
||||||
|
function Cursor.substate(self)
|
||||||
|
Cursor._substates = Cursor._substates or { }
|
||||||
|
Cursor._substates[self] = Cursor._substates[self] or cursor_state()
|
||||||
|
return Cursor._substates[self]
|
||||||
|
end
|
||||||
|
|
||||||
|
local _load = Cursor.load
|
||||||
|
function Cursor.load(self, ...)
|
||||||
|
if Cursor._substates and Cursor._substates[self] then
|
||||||
|
_load(Cursor._substates[self], ...)
|
||||||
|
end
|
||||||
|
return _load(self, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local _unload = Cursor.unload
|
||||||
|
function Cursor.unload(self, ...)
|
||||||
|
if Cursor._substates and Cursor._substates[self] then
|
||||||
|
_unload(Cursor._substates[self], ...)
|
||||||
|
end
|
||||||
|
return _unload(self, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Add an anonymous section.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.add
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param type UCI section type
|
||||||
|
-- @return Name of created section
|
||||||
|
|
||||||
|
--- Get a table of saved but uncommitted changes.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.changes
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Table of changes
|
||||||
|
-- @see Cursor.save
|
||||||
|
|
||||||
|
--- Commit saved changes.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.commit
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
-- @see Cursor.revert
|
||||||
|
-- @see Cursor.save
|
||||||
|
|
||||||
|
--- Deletes a section or an option.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.delete
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option (optional)
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
|
||||||
|
--- Call a function for every section of a certain type.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.foreach
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param type UCI section type
|
||||||
|
-- @param callback Function to be called
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
|
||||||
|
--- Get a section type or an option
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.get
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option (optional)
|
||||||
|
-- @return UCI value
|
||||||
|
|
||||||
|
--- Get all sections of a config or all values of a section.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.get_all
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name (optional)
|
||||||
|
-- @return Table of UCI sections or table of UCI values
|
||||||
|
|
||||||
|
--- Manually load a config.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.load
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
-- @see Cursor.save
|
||||||
|
-- @see Cursor.unload
|
||||||
|
|
||||||
|
--- Revert saved but uncommitted changes.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.revert
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
-- @see Cursor.commit
|
||||||
|
-- @see Cursor.save
|
||||||
|
|
||||||
|
--- Saves changes made to a config to make them committable.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.save
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
-- @see Cursor.load
|
||||||
|
-- @see Cursor.unload
|
||||||
|
|
||||||
|
--- Set a value or create a named section.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.set
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @param section UCI section name
|
||||||
|
-- @param option UCI option or UCI section type
|
||||||
|
-- @param value UCI value or nil if you want to create a section
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
|
||||||
|
--- Get the configuration directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.get_confdir
|
||||||
|
-- @return Configuration directory
|
||||||
|
|
||||||
|
--- Get the directory for uncomitted changes.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.get_savedir
|
||||||
|
-- @return Save directory
|
||||||
|
|
||||||
|
--- Set the configuration directory.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.set_confdir
|
||||||
|
-- @param directory UCI configuration directory
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
|
||||||
|
--- Set the directory for uncommited changes.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.set_savedir
|
||||||
|
-- @param directory UCI changes directory
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
|
||||||
|
--- Discard changes made to a config.
|
||||||
|
-- @class function
|
||||||
|
-- @name Cursor.unload
|
||||||
|
-- @param config UCI config
|
||||||
|
-- @return Boolean whether operation succeeded
|
||||||
|
-- @see Cursor.load
|
||||||
|
-- @see Cursor.save
|
165
1_1.mi_Lua/luci/sauth.lua
Normal file
165
1_1.mi_Lua/luci/sauth.lua
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
Session authentication
|
||||||
|
(c) 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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: sauth.lua 8900 2012-08-08 09:48:47Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--- LuCI session library.
|
||||||
|
module("luci.sauth", package.seeall)
|
||||||
|
require("luci.util")
|
||||||
|
require("luci.sys")
|
||||||
|
require("luci.config")
|
||||||
|
local nixio = require "nixio", require "nixio.util"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
local XQLog = require("xiaoqiang.XQLog")
|
||||||
|
|
||||||
|
luci.config.sauth = luci.config.sauth or {}
|
||||||
|
sessionpath = luci.config.sauth.sessionpath
|
||||||
|
sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
|
||||||
|
|
||||||
|
--- Prepare session storage by creating the session directory.
|
||||||
|
function prepare()
|
||||||
|
fs.mkdir(sessionpath, 700)
|
||||||
|
if not sane() then
|
||||||
|
error("Security Exception: Session path is not sane!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _read(id)
|
||||||
|
local blob = fs.readfile(sessionpath .. "/" .. id)
|
||||||
|
return blob
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _write(id, data)
|
||||||
|
local tempid = luci.sys.uniqueid(16)
|
||||||
|
local tempfile = sessionpath .. "/" .. tempid
|
||||||
|
local sessfile = sessionpath .. "/" .. id
|
||||||
|
local f = nixio.open(tempfile, "w", 600)
|
||||||
|
f:writeall(data)
|
||||||
|
f:close()
|
||||||
|
fs.rename(tempfile, sessfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _checkid(id)
|
||||||
|
return not not (id and #id == 32 and id:match("^[a-fA-F0-9]+$"))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Write session data to a session file.
|
||||||
|
-- @param id Session identifier
|
||||||
|
-- @param data Session data table
|
||||||
|
function write(id, data)
|
||||||
|
if not sane() then
|
||||||
|
prepare()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _checkid(id) then
|
||||||
|
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.write")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(data) ~= "table" then
|
||||||
|
XQLog.log(3,"Security Exception: Session data invalid! sauth.write")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
data.atime = luci.sys.uptime()
|
||||||
|
|
||||||
|
_write(id, luci.util.get_bytecode(data))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Read a session and return its content.
|
||||||
|
-- @param id Session identifier
|
||||||
|
-- @return Session data table or nil if the given id is not found
|
||||||
|
function read(id)
|
||||||
|
if not id or #id == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _checkid(id) then
|
||||||
|
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.read")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not sane(sessionpath .. "/" .. id) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local blob = _read(id)
|
||||||
|
local func = loadstring(blob)
|
||||||
|
setfenv(func, {})
|
||||||
|
|
||||||
|
local sess = func()
|
||||||
|
if type(sess) ~= "table" then
|
||||||
|
XQLog.log(3,"Security Exception: Session data invalid! sauth.read")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if sess.atime and sess.atime + sessiontime < luci.sys.uptime() then
|
||||||
|
kill(id)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- refresh atime in session
|
||||||
|
write(id, sess)
|
||||||
|
|
||||||
|
return sess
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check whether Session environment is sane.
|
||||||
|
-- @return Boolean status
|
||||||
|
function sane(file)
|
||||||
|
return luci.sys.process.info("uid")
|
||||||
|
== fs.stat(file or sessionpath, "uid")
|
||||||
|
and fs.stat(file or sessionpath, "modestr")
|
||||||
|
== (file and "rw-------" or "rwx------")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Kills a session
|
||||||
|
-- @param id Session identifier
|
||||||
|
function kill(id)
|
||||||
|
if not _checkid(id) then
|
||||||
|
XQLog.log(3,"Security Exception: Session ID is invalid! sauth.kill")
|
||||||
|
else
|
||||||
|
fs.unlink(sessionpath .. "/" .. id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove all expired session data files
|
||||||
|
function reap()
|
||||||
|
if sane() then
|
||||||
|
local id
|
||||||
|
for id in nixio.fs.dir(sessionpath) do
|
||||||
|
if _checkid(id) then
|
||||||
|
-- reading the session will kill it if it is expired
|
||||||
|
read(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get available session data
|
||||||
|
function available()
|
||||||
|
if sane() then
|
||||||
|
local id
|
||||||
|
for id in nixio.fs.dir(sessionpath) do
|
||||||
|
if _checkid(id) then
|
||||||
|
-- reading the session will kill it if it is expired
|
||||||
|
local available = read(id)
|
||||||
|
if available then
|
||||||
|
return available
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
95
1_1.mi_Lua/luci/sgi/cgi.lua
Normal file
95
1_1.mi_Lua/luci/sgi/cgi.lua
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - SGI-Module for CGI
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Server Gateway Interface for CGI
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: cgi.lua 6535 2010-11-23 01:02:21Z soma $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
exectime = os.clock()
|
||||||
|
module("luci.sgi.cgi", package.seeall)
|
||||||
|
local ltn12 = require("luci.ltn12")
|
||||||
|
require("nixio.util")
|
||||||
|
require("luci.http")
|
||||||
|
require("luci.sys")
|
||||||
|
require("luci.dispatcher")
|
||||||
|
|
||||||
|
-- Limited source to avoid endless blocking
|
||||||
|
local function limitsource(handle, limit)
|
||||||
|
limit = limit or 0
|
||||||
|
local BLOCKSIZE = ltn12.BLOCKSIZE
|
||||||
|
|
||||||
|
return function()
|
||||||
|
if limit < 1 then
|
||||||
|
handle:close()
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local read = (limit > BLOCKSIZE) and BLOCKSIZE or limit
|
||||||
|
limit = limit - read
|
||||||
|
|
||||||
|
local chunk = handle:read(read)
|
||||||
|
if not chunk then handle:close() end
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function run()
|
||||||
|
local r = luci.http.Request(
|
||||||
|
luci.sys.getenv(),
|
||||||
|
limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
|
||||||
|
ltn12.sink.file(io.stderr)
|
||||||
|
)
|
||||||
|
|
||||||
|
local x = coroutine.create(luci.dispatcher.httpdispatch)
|
||||||
|
local hcache = ""
|
||||||
|
local active = true
|
||||||
|
|
||||||
|
while coroutine.status(x) ~= "dead" do
|
||||||
|
local res, id, data1, data2 = coroutine.resume(x, r)
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
print("Status: 500 Internal Server Error")
|
||||||
|
print("Content-Type: text/plain\n")
|
||||||
|
print(id)
|
||||||
|
break;
|
||||||
|
end
|
||||||
|
|
||||||
|
if active then
|
||||||
|
if id == 1 then
|
||||||
|
io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
|
||||||
|
elseif id == 2 then
|
||||||
|
hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
|
||||||
|
elseif id == 3 then
|
||||||
|
io.write(hcache)
|
||||||
|
io.write("\r\n")
|
||||||
|
elseif id == 4 then
|
||||||
|
io.write(tostring(data1 or ""))
|
||||||
|
elseif id == 5 then
|
||||||
|
io.flush()
|
||||||
|
io.close()
|
||||||
|
active = false
|
||||||
|
elseif id == 6 then
|
||||||
|
data1:copyz(nixio.stdout, data2)
|
||||||
|
data1:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
121
1_1.mi_Lua/luci/sgi/uhttpd.lua
Normal file
121
1_1.mi_Lua/luci/sgi/uhttpd.lua
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Server Gateway Interface for the uHTTPd server
|
||||||
|
|
||||||
|
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "nixio.util"
|
||||||
|
require "luci.http"
|
||||||
|
require "luci.sys"
|
||||||
|
require "luci.dispatcher"
|
||||||
|
require "luci.ltn12"
|
||||||
|
|
||||||
|
function handle_request(env)
|
||||||
|
exectime = os.clock()
|
||||||
|
local renv = {
|
||||||
|
CONTENT_LENGTH = env.CONTENT_LENGTH,
|
||||||
|
CONTENT_TYPE = env.CONTENT_TYPE,
|
||||||
|
REQUEST_METHOD = env.REQUEST_METHOD,
|
||||||
|
REQUEST_URI = env.REQUEST_URI,
|
||||||
|
PATH_INFO = env.PATH_INFO,
|
||||||
|
SCRIPT_NAME = env.SCRIPT_NAME:gsub("/+$", ""),
|
||||||
|
SCRIPT_FILENAME = env.SCRIPT_NAME,
|
||||||
|
SERVER_PROTOCOL = env.SERVER_PROTOCOL,
|
||||||
|
QUERY_STRING = env.QUERY_STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
local k, v
|
||||||
|
for k, v in pairs(env.headers) do
|
||||||
|
k = k:upper():gsub("%-", "_")
|
||||||
|
renv["HTTP_" .. k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
local len = env.CONTENT_LENGTH or 0
|
||||||
|
local function recv()
|
||||||
|
if len > 0 then
|
||||||
|
local rlen, rbuf = uhttpd.recv(4096)
|
||||||
|
if rlen >= 0 then
|
||||||
|
len = len - rlen
|
||||||
|
return rbuf
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function send(...)
|
||||||
|
return uhttpd.send(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sendc(...)
|
||||||
|
if env.HTTP_VERSION > 1.0 then
|
||||||
|
return uhttpd.sendc(...)
|
||||||
|
else
|
||||||
|
return uhttpd.send(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local req = luci.http.Request(
|
||||||
|
renv, recv, luci.ltn12.sink.file(io.stderr)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
local x = coroutine.create(luci.dispatcher.httpdispatch)
|
||||||
|
local hcache = { }
|
||||||
|
local active = true
|
||||||
|
|
||||||
|
if env.HTTP_VERSION > 1.0 then
|
||||||
|
hcache["Transfer-Encoding"] = "chunked"
|
||||||
|
end
|
||||||
|
|
||||||
|
while coroutine.status(x) ~= "dead" do
|
||||||
|
local res, id, data1, data2 = coroutine.resume(x, req)
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
send(env.SERVER_PROTOCOL)
|
||||||
|
send(" 500 Internal Server Error\r\n")
|
||||||
|
send("Content-Type: text/plain\r\n\r\n")
|
||||||
|
send(tostring(id))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if active then
|
||||||
|
if id == 1 then
|
||||||
|
send(env.SERVER_PROTOCOL)
|
||||||
|
send(" ")
|
||||||
|
send(tostring(data1))
|
||||||
|
send(" ")
|
||||||
|
send(tostring(data2))
|
||||||
|
send("\r\n")
|
||||||
|
elseif id == 2 then
|
||||||
|
hcache[data1] = data2
|
||||||
|
elseif id == 3 then
|
||||||
|
for k, v in pairs(hcache) do
|
||||||
|
send(tostring(k))
|
||||||
|
send(": ")
|
||||||
|
send(tostring(v))
|
||||||
|
send("\r\n")
|
||||||
|
end
|
||||||
|
send("\r\n")
|
||||||
|
elseif id == 4 then
|
||||||
|
sendc(tostring(data1 or ""))
|
||||||
|
elseif id == 5 then
|
||||||
|
active = false
|
||||||
|
elseif id == 6 then
|
||||||
|
data1:copyz(nixio.stdout, data2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
1_1.mi_Lua/luci/store.lua
Normal file
16
1_1.mi_Lua/luci/store.lua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
LuCI - Lua Development Framework
|
||||||
|
(c) 2009 Steven Barth <steven@midlink.org>
|
||||||
|
(c) 2009 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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
module("luci.store", util.threadlocal)
|
966
1_1.mi_Lua/luci/sys.lua
Normal file
966
1_1.mi_Lua/luci/sys.lua
Normal file
@ -0,0 +1,966 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - System library
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Utilities for interaction with the Linux system
|
||||||
|
|
||||||
|
FileId:
|
||||||
|
$Id: sys.lua 9623 2013-01-18 14:08:37Z jow $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
local io = require "io"
|
||||||
|
local os = require "os"
|
||||||
|
local table = require "table"
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local uci = require "luci.model.uci"
|
||||||
|
|
||||||
|
local luci = {}
|
||||||
|
luci.util = require "luci.util"
|
||||||
|
luci.ip = require "luci.ip"
|
||||||
|
|
||||||
|
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
|
||||||
|
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI Linux and POSIX system utilities.
|
||||||
|
module "luci.sys"
|
||||||
|
|
||||||
|
--- Execute a given shell command and return the error code
|
||||||
|
-- @class function
|
||||||
|
-- @name call
|
||||||
|
-- @param ... Command to call
|
||||||
|
-- @return Error code of the command
|
||||||
|
function call(...)
|
||||||
|
return os.execute(...) / 256
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Execute a given shell command and capture its standard output
|
||||||
|
-- @class function
|
||||||
|
-- @name exec
|
||||||
|
-- @param command Command to call
|
||||||
|
-- @return String containg the return the output of the command
|
||||||
|
exec = luci.util.exec
|
||||||
|
|
||||||
|
--- Retrieve information about currently mounted file systems.
|
||||||
|
-- @return Table containing mount information
|
||||||
|
function mounts()
|
||||||
|
local data = {}
|
||||||
|
local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
|
||||||
|
local ps = luci.util.execi("df")
|
||||||
|
|
||||||
|
if not ps then
|
||||||
|
return
|
||||||
|
else
|
||||||
|
ps()
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in ps do
|
||||||
|
local row = {}
|
||||||
|
|
||||||
|
local j = 1
|
||||||
|
for value in line:gmatch("[^%s]+") do
|
||||||
|
row[k[j]] = value
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if row[k[1]] then
|
||||||
|
|
||||||
|
-- this is a rather ugly workaround to cope with wrapped lines in
|
||||||
|
-- the df output:
|
||||||
|
--
|
||||||
|
-- /dev/scsi/host0/bus0/target0/lun0/part3
|
||||||
|
-- 114382024 93566472 15005244 86% /mnt/usb
|
||||||
|
--
|
||||||
|
|
||||||
|
if not row[k[2]] then
|
||||||
|
j = 2
|
||||||
|
line = ps()
|
||||||
|
for value in line:gmatch("[^%s]+") do
|
||||||
|
row[k[j]] = value
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(data, row)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Retrieve environment variables. If no variable is given then a table
|
||||||
|
-- containing the whole environment is returned otherwise this function returns
|
||||||
|
-- the corresponding string value for the given name or nil if no such variable
|
||||||
|
-- exists.
|
||||||
|
-- @class function
|
||||||
|
-- @name getenv
|
||||||
|
-- @param var Name of the environment variable to retrieve (optional)
|
||||||
|
-- @return String containg the value of the specified variable
|
||||||
|
-- @return Table containing all variables if no variable name is given
|
||||||
|
getenv = nixio.getenv
|
||||||
|
|
||||||
|
--- Get or set the current hostname.
|
||||||
|
-- @param String containing a new hostname to set (optional)
|
||||||
|
-- @return String containing the system hostname
|
||||||
|
function hostname(newname)
|
||||||
|
if type(newname) == "string" and #newname > 0 then
|
||||||
|
fs.writefile( "/proc/sys/kernel/hostname", newname )
|
||||||
|
return newname
|
||||||
|
else
|
||||||
|
return nixio.uname().nodename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the contents of a documented referred by an URL.
|
||||||
|
-- @param url The URL to retrieve
|
||||||
|
-- @param stream Return a stream instead of a buffer
|
||||||
|
-- @param target Directly write to target file name
|
||||||
|
-- @return String containing the contents of given the URL
|
||||||
|
function httpget(url, stream, target)
|
||||||
|
if not target then
|
||||||
|
local source = stream and io.popen or luci.util.exec
|
||||||
|
return source("wget -qO- '"..url:gsub("'", "").."'")
|
||||||
|
else
|
||||||
|
return os.execute("wget -qO '%s' '%s'" %
|
||||||
|
{target:gsub("'", ""), url:gsub("'", "")})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the system load average values.
|
||||||
|
-- @return String containing the average load value 1 minute ago
|
||||||
|
-- @return String containing the average load value 5 minutes ago
|
||||||
|
-- @return String containing the average load value 15 minutes ago
|
||||||
|
function loadavg()
|
||||||
|
local info = nixio.sysinfo()
|
||||||
|
return info.loads[1], info.loads[2], info.loads[3]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Initiate a system reboot.
|
||||||
|
-- @return Return value of os.execute()
|
||||||
|
function reboot()
|
||||||
|
return os.execute("reboot >/dev/null 2>&1")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the system type, cpu name and installed physical memory.
|
||||||
|
-- @return String containing the system or platform identifier
|
||||||
|
-- @return String containing hardware model information
|
||||||
|
-- @return String containing the total memory amount in kB
|
||||||
|
-- @return String containing the memory used for caching in kB
|
||||||
|
-- @return String containing the memory used for buffering in kB
|
||||||
|
-- @return String containing the free memory amount in kB
|
||||||
|
-- @return String containing the cpu bogomips (number)
|
||||||
|
function sysinfo()
|
||||||
|
local cpuinfo = fs.readfile("/proc/cpuinfo")
|
||||||
|
local meminfo = fs.readfile("/proc/meminfo")
|
||||||
|
|
||||||
|
local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)"))
|
||||||
|
local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)"))
|
||||||
|
local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)"))
|
||||||
|
local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)"))
|
||||||
|
local bogomips = tonumber(cpuinfo:match("[Bb]ogo[Mm][Ii][Pp][Ss].-: ([^\n]+)")) or 0
|
||||||
|
|
||||||
|
local system =
|
||||||
|
cpuinfo:match("system type\t+: ([^\n]+)") or
|
||||||
|
cpuinfo:match("Processor\t+: ([^\n]+)") or
|
||||||
|
cpuinfo:match("model name\t+: ([^\n]+)")
|
||||||
|
|
||||||
|
local model =
|
||||||
|
luci.util.pcdata(fs.readfile("/tmp/sysinfo/model")) or
|
||||||
|
cpuinfo:match("machine\t+: ([^\n]+)") or
|
||||||
|
cpuinfo:match("Hardware\t+: ([^\n]+)") or
|
||||||
|
luci.util.pcdata(fs.readfile("/proc/diag/model")) or
|
||||||
|
nixio.uname().machine or
|
||||||
|
system
|
||||||
|
|
||||||
|
return system, model, memtotal, memcached, membuffers, memfree, bogomips
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Retrieves the output of the "logread" command.
|
||||||
|
-- @return String containing the current log buffer
|
||||||
|
function syslog()
|
||||||
|
return luci.util.exec("logread")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Retrieves the output of the "dmesg" command.
|
||||||
|
-- @return String containing the current log buffer
|
||||||
|
function dmesg()
|
||||||
|
return luci.util.exec("dmesg")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Generates a random id with specified length.
|
||||||
|
-- @param bytes Number of bytes for the unique id
|
||||||
|
-- @return String containing hex encoded id
|
||||||
|
function uniqueid(bytes)
|
||||||
|
local rand = fs.readfile("/dev/urandom", bytes)
|
||||||
|
return rand and nixio.bin.hexlify(rand)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the current system uptime stats.
|
||||||
|
-- @return String containing total uptime in seconds
|
||||||
|
function uptime()
|
||||||
|
return nixio.sysinfo().uptime
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI system utilities / network related functions.
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.sys.net
|
||||||
|
net = {}
|
||||||
|
|
||||||
|
--- Returns the current arp-table entries as two-dimensional table.
|
||||||
|
-- @return Table of table containing the current arp entries.
|
||||||
|
-- The following fields are defined for arp entry objects:
|
||||||
|
-- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
|
||||||
|
function net.arptable(callback)
|
||||||
|
local arp, e, r, v
|
||||||
|
if fs.access("/proc/net/arp") then
|
||||||
|
for e in io.lines("/proc/net/arp") do
|
||||||
|
local r = { }, v
|
||||||
|
for v in e:gmatch("%S+") do
|
||||||
|
r[#r+1] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
if r[1] ~= "IP" then
|
||||||
|
local x = {
|
||||||
|
["IP address"] = r[1],
|
||||||
|
["HW type"] = r[2],
|
||||||
|
["Flags"] = r[3],
|
||||||
|
["HW address"] = r[4],
|
||||||
|
["Mask"] = r[5],
|
||||||
|
["Device"] = r[6]
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback then
|
||||||
|
callback(x)
|
||||||
|
else
|
||||||
|
arp = arp or { }
|
||||||
|
arp[#arp+1] = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return arp
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _nethints(what, callback)
|
||||||
|
local _, k, e, mac, ip, name
|
||||||
|
local cur = uci.cursor()
|
||||||
|
local ifn = { }
|
||||||
|
local hosts = { }
|
||||||
|
|
||||||
|
local function _add(i, ...)
|
||||||
|
local k = select(i, ...)
|
||||||
|
if k then
|
||||||
|
if not hosts[k] then hosts[k] = { } end
|
||||||
|
hosts[k][1] = select(1, ...) or hosts[k][1]
|
||||||
|
hosts[k][2] = select(2, ...) or hosts[k][2]
|
||||||
|
hosts[k][3] = select(3, ...) or hosts[k][3]
|
||||||
|
hosts[k][4] = select(4, ...) or hosts[k][4]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.access("/proc/net/arp") then
|
||||||
|
for e in io.lines("/proc/net/arp") do
|
||||||
|
ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+")
|
||||||
|
if ip and mac then
|
||||||
|
_add(what, mac:upper(), ip, nil, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.access("/etc/ethers") then
|
||||||
|
for e in io.lines("/etc/ethers") do
|
||||||
|
mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
|
||||||
|
if mac and ip then
|
||||||
|
_add(what, mac:upper(), ip, nil, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.access("/var/dhcp.leases") then
|
||||||
|
for e in io.lines("/var/dhcp.leases") do
|
||||||
|
mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
|
||||||
|
if mac and ip then
|
||||||
|
_add(what, mac:upper(), ip, nil, name ~= "*" and name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cur:foreach("dhcp", "host",
|
||||||
|
function(s)
|
||||||
|
for mac in luci.util.imatch(s.mac) do
|
||||||
|
_add(what, mac:upper(), s.ip, nil, s.name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
for _, e in ipairs(nixio.getifaddrs()) do
|
||||||
|
if e.name ~= "lo" then
|
||||||
|
ifn[e.name] = ifn[e.name] or { }
|
||||||
|
if e.family == "packet" and e.addr and #e.addr == 17 then
|
||||||
|
ifn[e.name][1] = e.addr:upper()
|
||||||
|
elseif e.family == "inet" then
|
||||||
|
ifn[e.name][2] = e.addr
|
||||||
|
elseif e.family == "inet6" then
|
||||||
|
ifn[e.name][3] = e.addr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, e in pairs(ifn) do
|
||||||
|
if e[what] and (e[2] or e[3]) then
|
||||||
|
_add(what, e[1], e[2], e[3], e[4])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, e in luci.util.kspairs(hosts) do
|
||||||
|
callback(e[1], e[2], e[3], e[4])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a two-dimensional table of mac address hints.
|
||||||
|
-- @return Table of table containing known hosts from various sources.
|
||||||
|
-- Each entry contains the values in the following order:
|
||||||
|
-- [ "mac", "name" ]
|
||||||
|
function net.mac_hints(callback)
|
||||||
|
if callback then
|
||||||
|
_nethints(1, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
|
||||||
|
if name and name ~= mac then
|
||||||
|
callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
local rv = { }
|
||||||
|
_nethints(1, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
|
||||||
|
if name and name ~= mac then
|
||||||
|
rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a two-dimensional table of IPv4 address hints.
|
||||||
|
-- @return Table of table containing known hosts from various sources.
|
||||||
|
-- Each entry contains the values in the following order:
|
||||||
|
-- [ "ip", "name" ]
|
||||||
|
function net.ipv4_hints(callback)
|
||||||
|
if callback then
|
||||||
|
_nethints(2, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v4, nil, 100) or mac
|
||||||
|
if name and name ~= v4 then
|
||||||
|
callback(v4, name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
local rv = { }
|
||||||
|
_nethints(2, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v4, nil, 100) or mac
|
||||||
|
if name and name ~= v4 then
|
||||||
|
rv[#rv+1] = { v4, name }
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a two-dimensional table of IPv6 address hints.
|
||||||
|
-- @return Table of table containing known hosts from various sources.
|
||||||
|
-- Each entry contains the values in the following order:
|
||||||
|
-- [ "ip", "name" ]
|
||||||
|
function net.ipv6_hints(callback)
|
||||||
|
if callback then
|
||||||
|
_nethints(3, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v6, nil, 100) or mac
|
||||||
|
if name and name ~= v6 then
|
||||||
|
callback(v6, name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
local rv = { }
|
||||||
|
_nethints(3, function(mac, v4, v6, name)
|
||||||
|
name = name or nixio.getnameinfo(v6, nil, 100) or mac
|
||||||
|
if name and name ~= v6 then
|
||||||
|
rv[#rv+1] = { v6, name }
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns conntrack information
|
||||||
|
-- @return Table with the currently tracked IP connections
|
||||||
|
function net.conntrack(callback)
|
||||||
|
local connt = {}
|
||||||
|
if fs.access("/proc/net/nf_conntrack", "r") then
|
||||||
|
for line in io.lines("/proc/net/nf_conntrack") do
|
||||||
|
line = line:match "^(.-( [^ =]+=).-)%2"
|
||||||
|
local entry, flags = _parse_mixed_record(line, " +")
|
||||||
|
if flags[6] ~= "TIME_WAIT" then
|
||||||
|
entry.layer3 = flags[1]
|
||||||
|
entry.layer4 = flags[3]
|
||||||
|
for i=1, #entry do
|
||||||
|
entry[i] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if callback then
|
||||||
|
callback(entry)
|
||||||
|
else
|
||||||
|
connt[#connt+1] = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif fs.access("/proc/net/ip_conntrack", "r") then
|
||||||
|
for line in io.lines("/proc/net/ip_conntrack") do
|
||||||
|
line = line:match "^(.-( [^ =]+=).-)%2"
|
||||||
|
local entry, flags = _parse_mixed_record(line, " +")
|
||||||
|
if flags[4] ~= "TIME_WAIT" then
|
||||||
|
entry.layer3 = "ipv4"
|
||||||
|
entry.layer4 = flags[1]
|
||||||
|
for i=1, #entry do
|
||||||
|
entry[i] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if callback then
|
||||||
|
callback(entry)
|
||||||
|
else
|
||||||
|
connt[#connt+1] = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return connt
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine the current IPv4 default route. If multiple default routes exist,
|
||||||
|
-- return the one with the lowest metric.
|
||||||
|
-- @return Table with the properties of the current default route.
|
||||||
|
-- The following fields are defined:
|
||||||
|
-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
|
||||||
|
-- "flags", "device" }
|
||||||
|
function net.defaultroute()
|
||||||
|
local route
|
||||||
|
|
||||||
|
net.routes(function(rt)
|
||||||
|
if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
|
||||||
|
route = rt
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return route
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine the current IPv6 default route. If multiple default routes exist,
|
||||||
|
-- return the one with the lowest metric.
|
||||||
|
-- @return Table with the properties of the current default route.
|
||||||
|
-- The following fields are defined:
|
||||||
|
-- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
|
||||||
|
-- "flags", "device" }
|
||||||
|
function net.defaultroute6()
|
||||||
|
local route
|
||||||
|
|
||||||
|
net.routes6(function(rt)
|
||||||
|
if rt.dest:prefix() == 0 and rt.device ~= "lo" and
|
||||||
|
(not route or route.metric > rt.metric)
|
||||||
|
then
|
||||||
|
route = rt
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not route then
|
||||||
|
local global_unicast = luci.ip.IPv6("2000::/3")
|
||||||
|
net.routes6(function(rt)
|
||||||
|
if rt.dest:equal(global_unicast) and
|
||||||
|
(not route or route.metric > rt.metric)
|
||||||
|
then
|
||||||
|
route = rt
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return route
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determine the names of available network interfaces.
|
||||||
|
-- @return Table containing all current interface names
|
||||||
|
function net.devices()
|
||||||
|
local devs = {}
|
||||||
|
for k, v in ipairs(nixio.getifaddrs()) do
|
||||||
|
if v.family == "packet" then
|
||||||
|
devs[#devs+1] = v.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return devs
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Return information about available network interfaces.
|
||||||
|
-- @return Table containing all current interface names and their information
|
||||||
|
function net.deviceinfo()
|
||||||
|
local devs = {}
|
||||||
|
for k, v in ipairs(nixio.getifaddrs()) do
|
||||||
|
if v.family == "packet" then
|
||||||
|
local d = v.data
|
||||||
|
d[1] = d.rx_bytes
|
||||||
|
d[2] = d.rx_packets
|
||||||
|
d[3] = d.rx_errors
|
||||||
|
d[4] = d.rx_dropped
|
||||||
|
d[5] = 0
|
||||||
|
d[6] = 0
|
||||||
|
d[7] = 0
|
||||||
|
d[8] = d.multicast
|
||||||
|
d[9] = d.tx_bytes
|
||||||
|
d[10] = d.tx_packets
|
||||||
|
d[11] = d.tx_errors
|
||||||
|
d[12] = d.tx_dropped
|
||||||
|
d[13] = 0
|
||||||
|
d[14] = d.collisions
|
||||||
|
d[15] = 0
|
||||||
|
d[16] = 0
|
||||||
|
devs[v.name] = d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return devs
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Determine the MAC address belonging to the given IP address.
|
||||||
|
-- @param ip IPv4 address
|
||||||
|
-- @return String containing the MAC address or nil if it cannot be found
|
||||||
|
function net.ip4mac(ip)
|
||||||
|
local mac = nil
|
||||||
|
net.arptable(function(e)
|
||||||
|
if e["IP address"] == ip then
|
||||||
|
mac = e["HW address"]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return mac
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the current kernel routing table entries.
|
||||||
|
-- @return Table of tables with properties of the corresponding routes.
|
||||||
|
-- The following fields are defined for route entry tables:
|
||||||
|
-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
|
||||||
|
-- "flags", "device" }
|
||||||
|
function net.routes(callback)
|
||||||
|
local routes = { }
|
||||||
|
|
||||||
|
for line in io.lines("/proc/net/route") do
|
||||||
|
|
||||||
|
local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
|
||||||
|
dst_mask, mtu, win, irtt = line:match(
|
||||||
|
"([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
|
||||||
|
"(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if dev then
|
||||||
|
gateway = luci.ip.Hex( gateway, 32, luci.ip.FAMILY_INET4 )
|
||||||
|
dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
|
||||||
|
dst_ip = luci.ip.Hex(
|
||||||
|
dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
|
||||||
|
)
|
||||||
|
|
||||||
|
local rt = {
|
||||||
|
dest = dst_ip,
|
||||||
|
gateway = gateway,
|
||||||
|
metric = tonumber(metric),
|
||||||
|
refcount = tonumber(refcnt),
|
||||||
|
usecount = tonumber(usecnt),
|
||||||
|
mtu = tonumber(mtu),
|
||||||
|
window = tonumber(window),
|
||||||
|
irtt = tonumber(irtt),
|
||||||
|
flags = tonumber(flags, 16),
|
||||||
|
device = dev
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback then
|
||||||
|
callback(rt)
|
||||||
|
else
|
||||||
|
routes[#routes+1] = rt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return routes
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the current ipv6 kernel routing table entries.
|
||||||
|
-- @return Table of tables with properties of the corresponding routes.
|
||||||
|
-- The following fields are defined for route entry tables:
|
||||||
|
-- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
|
||||||
|
-- "flags", "device" }
|
||||||
|
function net.routes6(callback)
|
||||||
|
if fs.access("/proc/net/ipv6_route", "r") then
|
||||||
|
local routes = { }
|
||||||
|
|
||||||
|
for line in io.lines("/proc/net/ipv6_route") do
|
||||||
|
|
||||||
|
local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
|
||||||
|
metric, refcnt, usecnt, flags, dev = line:match(
|
||||||
|
"([a-f0-9]+) ([a-f0-9]+) " ..
|
||||||
|
"([a-f0-9]+) ([a-f0-9]+) " ..
|
||||||
|
"([a-f0-9]+) ([a-f0-9]+) " ..
|
||||||
|
"([a-f0-9]+) ([a-f0-9]+) " ..
|
||||||
|
"([a-f0-9]+) +([^%s]+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if dst_ip and dst_prefix and
|
||||||
|
src_ip and src_prefix and
|
||||||
|
nexthop and metric and
|
||||||
|
refcnt and usecnt and
|
||||||
|
flags and dev
|
||||||
|
then
|
||||||
|
src_ip = luci.ip.Hex(
|
||||||
|
src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
|
||||||
|
)
|
||||||
|
|
||||||
|
dst_ip = luci.ip.Hex(
|
||||||
|
dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
|
||||||
|
)
|
||||||
|
|
||||||
|
nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
|
||||||
|
|
||||||
|
local rt = {
|
||||||
|
source = src_ip,
|
||||||
|
dest = dst_ip,
|
||||||
|
nexthop = nexthop,
|
||||||
|
metric = tonumber(metric, 16),
|
||||||
|
refcount = tonumber(refcnt, 16),
|
||||||
|
usecount = tonumber(usecnt, 16),
|
||||||
|
flags = tonumber(flags, 16),
|
||||||
|
device = dev,
|
||||||
|
|
||||||
|
-- lua number is too small for storing the metric
|
||||||
|
-- add a metric_raw field with the original content
|
||||||
|
metric_raw = metric
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback then
|
||||||
|
callback(rt)
|
||||||
|
else
|
||||||
|
routes[#routes+1] = rt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return routes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Tests whether the given host responds to ping probes.
|
||||||
|
-- @param host String containing a hostname or IPv4 address
|
||||||
|
-- @return Number containing 0 on success and >= 1 on error
|
||||||
|
function net.pingtest(host)
|
||||||
|
return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI system utilities / process related functions.
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.sys.process
|
||||||
|
process = {}
|
||||||
|
|
||||||
|
--- Get the current process id.
|
||||||
|
-- @class function
|
||||||
|
-- @name process.info
|
||||||
|
-- @return Number containing the current pid
|
||||||
|
function process.info(key)
|
||||||
|
local s = {uid = nixio.getuid(), gid = nixio.getgid()}
|
||||||
|
return not key and s or s[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Retrieve information about currently running processes.
|
||||||
|
-- @return Table containing process information
|
||||||
|
function process.list()
|
||||||
|
local data = {}
|
||||||
|
local k
|
||||||
|
local ps = luci.util.execi("/bin/busybox top -bn1")
|
||||||
|
|
||||||
|
if not ps then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in ps do
|
||||||
|
local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
|
||||||
|
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
local idx = tonumber(pid)
|
||||||
|
if idx then
|
||||||
|
data[idx] = {
|
||||||
|
['PID'] = pid,
|
||||||
|
['PPID'] = ppid,
|
||||||
|
['USER'] = user,
|
||||||
|
['STAT'] = stat,
|
||||||
|
['VSZ'] = vsz,
|
||||||
|
['%MEM'] = mem,
|
||||||
|
['%CPU'] = cpu,
|
||||||
|
['COMMAND'] = cmd
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the gid of a process identified by given pid.
|
||||||
|
-- @param gid Number containing the Unix group id
|
||||||
|
-- @return Boolean indicating successful operation
|
||||||
|
-- @return String containing the error message if failed
|
||||||
|
-- @return Number containing the error code if failed
|
||||||
|
function process.setgroup(gid)
|
||||||
|
return nixio.setgid(gid)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the uid of a process identified by given pid.
|
||||||
|
-- @param uid Number containing the Unix user id
|
||||||
|
-- @return Boolean indicating successful operation
|
||||||
|
-- @return String containing the error message if failed
|
||||||
|
-- @return Number containing the error code if failed
|
||||||
|
function process.setuser(uid)
|
||||||
|
return nixio.setuid(uid)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send a signal to a process identified by given pid.
|
||||||
|
-- @class function
|
||||||
|
-- @name process.signal
|
||||||
|
-- @param pid Number containing the process id
|
||||||
|
-- @param sig Signal to send (default: 15 [SIGTERM])
|
||||||
|
-- @return Boolean indicating successful operation
|
||||||
|
-- @return Number containing the error code if failed
|
||||||
|
process.signal = nixio.kill
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI system utilities / user related functions.
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.sys.user
|
||||||
|
user = {}
|
||||||
|
|
||||||
|
--- Retrieve user informations for given uid.
|
||||||
|
-- @class function
|
||||||
|
-- @name getuser
|
||||||
|
-- @param uid Number containing the Unix user id
|
||||||
|
-- @return Table containing the following fields:
|
||||||
|
-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
|
||||||
|
user.getuser = nixio.getpw
|
||||||
|
|
||||||
|
--- Retrieve the current user password hash.
|
||||||
|
-- @param username String containing the username to retrieve the password for
|
||||||
|
-- @return String containing the hash or nil if no password is set.
|
||||||
|
-- @return Password database entry
|
||||||
|
function user.getpasswd(username)
|
||||||
|
if username and username:lower()=="admin" then
|
||||||
|
username = "root"
|
||||||
|
end
|
||||||
|
local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
|
||||||
|
local pwh = pwe and (pwe.pwdp or pwe.passwd)
|
||||||
|
if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then
|
||||||
|
return nil, pwe
|
||||||
|
else
|
||||||
|
return pwh, pwe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether given string matches the password of a given system user.
|
||||||
|
-- @param username String containing the Unix user name
|
||||||
|
-- @param pass String containing the password to compare
|
||||||
|
-- @return Boolean indicating wheather the passwords are equal
|
||||||
|
function user.checkpasswd(username, pass)
|
||||||
|
if username and username:lower()=="admin" then
|
||||||
|
username = "root"
|
||||||
|
end
|
||||||
|
local pwh, pwe = user.getpasswd(username)
|
||||||
|
if pwe then
|
||||||
|
return (pwh == nil or nixio.crypt(pass, pwh) == pwh)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Change the password of given user.
|
||||||
|
-- @param username String containing the Unix user name
|
||||||
|
-- @param password String containing the password to compare
|
||||||
|
-- @return Number containing 0 on success and >= 1 on error
|
||||||
|
function user.setpasswd(username, password)
|
||||||
|
if username and username:lower()=="admin" then
|
||||||
|
username = "root"
|
||||||
|
end
|
||||||
|
if password then
|
||||||
|
password = password:gsub("'", [['"'"']])
|
||||||
|
end
|
||||||
|
|
||||||
|
if username then
|
||||||
|
username = username:gsub("'", [['"'"']])
|
||||||
|
end
|
||||||
|
|
||||||
|
return os.execute(
|
||||||
|
"(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " ..
|
||||||
|
"passwd '" .. username .. "' >/dev/null 2>&1"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI system utilities / wifi related functions.
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.sys.wifi
|
||||||
|
wifi = {}
|
||||||
|
|
||||||
|
--- Get wireless information for given interface.
|
||||||
|
-- @param ifname String containing the interface name
|
||||||
|
-- @return A wrapped iwinfo object instance
|
||||||
|
function wifi.getiwinfo(ifname)
|
||||||
|
local stat, iwinfo = pcall(require, "iwinfo")
|
||||||
|
|
||||||
|
if ifname then
|
||||||
|
local c = 0
|
||||||
|
local u = uci.cursor_state()
|
||||||
|
local d, n = ifname:match("^(%w+)%.network(%d+)")
|
||||||
|
if d and n then
|
||||||
|
ifname = d
|
||||||
|
n = tonumber(n)
|
||||||
|
u:foreach("wireless", "wifi-iface",
|
||||||
|
function(s)
|
||||||
|
if s.device == d then
|
||||||
|
c = c + 1
|
||||||
|
if c == n then
|
||||||
|
ifname = s.ifname or s.device
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
elseif u:get("wireless", ifname) == "wifi-device" then
|
||||||
|
u:foreach("wireless", "wifi-iface",
|
||||||
|
function(s)
|
||||||
|
if s.device == ifname and s.ifname then
|
||||||
|
ifname = s.ifname
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local t = stat and iwinfo.type(ifname)
|
||||||
|
local x = t and iwinfo[t] or { }
|
||||||
|
return setmetatable({}, {
|
||||||
|
__index = function(t, k)
|
||||||
|
if k == "ifname" then
|
||||||
|
return ifname
|
||||||
|
elseif x[k] then
|
||||||
|
return x[k](ifname)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- LuCI system utilities / init related functions.
|
||||||
|
-- @class module
|
||||||
|
-- @name luci.sys.init
|
||||||
|
init = {}
|
||||||
|
init.dir = "/etc/init.d/"
|
||||||
|
|
||||||
|
--- Get the names of all installed init scripts
|
||||||
|
-- @return Table containing the names of all inistalled init scripts
|
||||||
|
function init.names()
|
||||||
|
local names = { }
|
||||||
|
for name in fs.glob(init.dir.."*") do
|
||||||
|
names[#names+1] = fs.basename(name)
|
||||||
|
end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the index of he given init script
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Numeric index value
|
||||||
|
function init.index(name)
|
||||||
|
if fs.access(init.dir..name) then
|
||||||
|
return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
|
||||||
|
%{ init.dir, name })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function init_action(action, name)
|
||||||
|
if fs.access(init.dir..name) then
|
||||||
|
return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Test whether the given init script is enabled
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Boolean indicating whether init is enabled
|
||||||
|
function init.enabled(name)
|
||||||
|
return (init_action("enabled", name) == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Enable the given init script
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Boolean indicating success
|
||||||
|
function init.enable(name)
|
||||||
|
return (init_action("enable", name) == 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Disable the given init script
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Boolean indicating success
|
||||||
|
function init.disable(name)
|
||||||
|
return (init_action("disable", name) == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start the given init script
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Boolean indicating success
|
||||||
|
function init.start(name)
|
||||||
|
return (init_action("start", name) == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the given init script
|
||||||
|
-- @param name Name of the init script
|
||||||
|
-- @return Boolean indicating success
|
||||||
|
function init.stop(name)
|
||||||
|
return (init_action("stop", name) == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Internal functions
|
||||||
|
|
||||||
|
function _parse_mixed_record(cnt, delimiter)
|
||||||
|
delimiter = delimiter or " "
|
||||||
|
local data = {}
|
||||||
|
local flags = {}
|
||||||
|
|
||||||
|
for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
|
||||||
|
for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
|
||||||
|
local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
|
||||||
|
|
||||||
|
if k then
|
||||||
|
if x == "" then
|
||||||
|
table.insert(flags, k)
|
||||||
|
else
|
||||||
|
data[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data, flags
|
||||||
|
end
|
371
1_1.mi_Lua/luci/sys/iptparser.lua
Normal file
371
1_1.mi_Lua/luci/sys/iptparser.lua
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
--[[
|
||||||
|
|
||||||
|
Iptables parser and query library
|
||||||
|
(c) 2008-2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||||
|
(c) 2008-2009 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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: iptparser.lua 6826 2011-01-29 22:47:40Z jow $
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local luci = {}
|
||||||
|
luci.util = require "luci.util"
|
||||||
|
luci.sys = require "luci.sys"
|
||||||
|
luci.ip = require "luci.ip"
|
||||||
|
|
||||||
|
local tonumber, ipairs, table = tonumber, ipairs, table
|
||||||
|
|
||||||
|
--- LuCI iptables parser and query library
|
||||||
|
-- @cstyle instance
|
||||||
|
module("luci.sys.iptparser")
|
||||||
|
|
||||||
|
--- Create a new iptables parser object.
|
||||||
|
-- @class function
|
||||||
|
-- @name IptParser
|
||||||
|
-- @param family Number specifying the address family. 4 for IPv4, 6 for IPv6
|
||||||
|
-- @return IptParser instance
|
||||||
|
IptParser = luci.util.class()
|
||||||
|
|
||||||
|
function IptParser.__init__( self, family )
|
||||||
|
self._family = (tonumber(family) == 6) and 6 or 4
|
||||||
|
self._rules = { }
|
||||||
|
self._chains = { }
|
||||||
|
|
||||||
|
if self._family == 4 then
|
||||||
|
self._nulladdr = "0.0.0.0/0"
|
||||||
|
self._tables = { "filter", "nat", "mangle", "raw" }
|
||||||
|
self._command = "iptables -t %s --line-numbers -nxvL"
|
||||||
|
else
|
||||||
|
self._nulladdr = "::/0"
|
||||||
|
self._tables = { "filter", "mangle", "raw" }
|
||||||
|
self._command = "ip6tables -t %s --line-numbers -nxvL"
|
||||||
|
end
|
||||||
|
|
||||||
|
self:_parse_rules()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find all firewall rules that match the given criteria. Expects a table with
|
||||||
|
-- search criteria as only argument. If args is nil or an empty table then all
|
||||||
|
-- rules will be returned.
|
||||||
|
--
|
||||||
|
-- The following keys in the args table are recognized:
|
||||||
|
-- <ul>
|
||||||
|
-- <li> table - Match rules that are located within the given table
|
||||||
|
-- <li> chain - Match rules that are located within the given chain
|
||||||
|
-- <li> target - Match rules with the given target
|
||||||
|
-- <li> protocol - Match rules that match the given protocol, rules with
|
||||||
|
-- protocol "all" are always matched
|
||||||
|
-- <li> source - Match rules with the given source, rules with source
|
||||||
|
-- "0.0.0.0/0" (::/0) are always matched
|
||||||
|
-- <li> destination - Match rules with the given destination, rules with
|
||||||
|
-- destination "0.0.0.0/0" (::/0) are always matched
|
||||||
|
-- <li> inputif - Match rules with the given input interface, rules
|
||||||
|
-- with input interface "*" (=all) are always matched
|
||||||
|
-- <li> outputif - Match rules with the given output interface, rules
|
||||||
|
-- with output interface "*" (=all) are always matched
|
||||||
|
-- <li> flags - Match rules that match the given flags, current
|
||||||
|
-- supported values are "-f" (--fragment)
|
||||||
|
-- and "!f" (! --fragment)
|
||||||
|
-- <li> options - Match rules containing all given options
|
||||||
|
-- </ul>
|
||||||
|
-- The return value is a list of tables representing the matched rules.
|
||||||
|
-- Each rule table contains the following fields:
|
||||||
|
-- <ul>
|
||||||
|
-- <li> index - The index number of the rule
|
||||||
|
-- <li> table - The table where the rule is located, can be one
|
||||||
|
-- of "filter", "nat" or "mangle"
|
||||||
|
-- <li> chain - The chain where the rule is located, e.g. "INPUT"
|
||||||
|
-- or "postrouting_wan"
|
||||||
|
-- <li> target - The rule target, e.g. "REJECT" or "DROP"
|
||||||
|
-- <li> protocol The matching protocols, e.g. "all" or "tcp"
|
||||||
|
-- <li> flags - Special rule options ("--", "-f" or "!f")
|
||||||
|
-- <li> inputif - Input interface of the rule, e.g. "eth0.0"
|
||||||
|
-- or "*" for all interfaces
|
||||||
|
-- <li> outputif - Output interface of the rule,e.g. "eth0.0"
|
||||||
|
-- or "*" for all interfaces
|
||||||
|
-- <li> source - The source ip range, e.g. "0.0.0.0/0" (::/0)
|
||||||
|
-- <li> destination - The destination ip range, e.g. "0.0.0.0/0" (::/0)
|
||||||
|
-- <li> options - A list of specific options of the rule,
|
||||||
|
-- e.g. { "reject-with", "tcp-reset" }
|
||||||
|
-- <li> packets - The number of packets matched by the rule
|
||||||
|
-- <li> bytes - The number of total bytes matched by the rule
|
||||||
|
-- </ul>
|
||||||
|
-- Example:
|
||||||
|
-- <pre>
|
||||||
|
-- ip = luci.sys.iptparser.IptParser()
|
||||||
|
-- result = ip.find( {
|
||||||
|
-- target="REJECT",
|
||||||
|
-- protocol="tcp",
|
||||||
|
-- options={ "reject-with", "tcp-reset" }
|
||||||
|
-- } )
|
||||||
|
-- </pre>
|
||||||
|
-- This will match all rules with target "-j REJECT",
|
||||||
|
-- protocol "-p tcp" (or "-p all")
|
||||||
|
-- and the option "--reject-with tcp-reset".
|
||||||
|
-- @params args Table containing the search arguments (optional)
|
||||||
|
-- @return Table of matching rule tables
|
||||||
|
function IptParser.find( self, args )
|
||||||
|
|
||||||
|
local args = args or { }
|
||||||
|
local rv = { }
|
||||||
|
|
||||||
|
args.source = args.source and self:_parse_addr(args.source)
|
||||||
|
args.destination = args.destination and self:_parse_addr(args.destination)
|
||||||
|
|
||||||
|
for i, rule in ipairs(self._rules) do
|
||||||
|
local match = true
|
||||||
|
|
||||||
|
-- match table
|
||||||
|
if not ( not args.table or args.table:lower() == rule.table ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match chain
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.chain or args.chain == rule.chain
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match target
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.target or args.target == rule.target
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match protocol
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.protocol or rule.protocol == "all" or
|
||||||
|
args.protocol:lower() == rule.protocol
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match source
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.source or rule.source == self._nulladdr or
|
||||||
|
self:_parse_addr(rule.source):contains(args.source)
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match destination
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.destination or rule.destination == self._nulladdr or
|
||||||
|
self:_parse_addr(rule.destination):contains(args.destination)
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match input interface
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.inputif or rule.inputif == "*" or
|
||||||
|
args.inputif == rule.inputif
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match output interface
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.outputif or rule.outputif == "*" or
|
||||||
|
args.outputif == rule.outputif
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match flags (the "opt" column)
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.flags or rule.flags == args.flags
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- match specific options
|
||||||
|
if not ( match == true and (
|
||||||
|
not args.options or
|
||||||
|
self:_match_options( rule.options, args.options )
|
||||||
|
) ) then
|
||||||
|
match = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- insert match
|
||||||
|
if match == true then
|
||||||
|
rv[#rv+1] = rule
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Rebuild the internal lookup table, for example when rules have changed
|
||||||
|
-- through external commands.
|
||||||
|
-- @return nothing
|
||||||
|
function IptParser.resync( self )
|
||||||
|
self._rules = { }
|
||||||
|
self._chain = nil
|
||||||
|
self:_parse_rules()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Find the names of all tables.
|
||||||
|
-- @return Table of table names.
|
||||||
|
function IptParser.tables( self )
|
||||||
|
return self._tables
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Find the names of all chains within the given table name.
|
||||||
|
-- @param table String containing the table name
|
||||||
|
-- @return Table of chain names in the order they occur.
|
||||||
|
function IptParser.chains( self, table )
|
||||||
|
local lookup = { }
|
||||||
|
local chains = { }
|
||||||
|
for _, r in ipairs(self:find({table=table})) do
|
||||||
|
if not lookup[r.chain] then
|
||||||
|
lookup[r.chain] = true
|
||||||
|
chains[#chains+1] = r.chain
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return chains
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Return the given firewall chain within the given table name.
|
||||||
|
-- @param table String containing the table name
|
||||||
|
-- @param chain String containing the chain name
|
||||||
|
-- @return Table containing the fields "policy", "packets", "bytes"
|
||||||
|
-- and "rules". The "rules" field is a table of rule tables.
|
||||||
|
function IptParser.chain( self, table, chain )
|
||||||
|
return self._chains[table:lower()] and self._chains[table:lower()][chain]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Test whether the given target points to a custom chain.
|
||||||
|
-- @param target String containing the target action
|
||||||
|
-- @return Boolean indicating whether target is a custom chain.
|
||||||
|
function IptParser.is_custom_target( self, target )
|
||||||
|
for _, r in ipairs(self._rules) do
|
||||||
|
if r.chain == target then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- [internal] Parse address according to family.
|
||||||
|
function IptParser._parse_addr( self, addr )
|
||||||
|
if self._family == 4 then
|
||||||
|
return luci.ip.IPv4(addr)
|
||||||
|
else
|
||||||
|
return luci.ip.IPv6(addr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [internal] Parse iptables output from all tables.
|
||||||
|
function IptParser._parse_rules( self )
|
||||||
|
|
||||||
|
for i, tbl in ipairs(self._tables) do
|
||||||
|
|
||||||
|
self._chains[tbl] = { }
|
||||||
|
|
||||||
|
for i, rule in ipairs(luci.util.execl(self._command % tbl)) do
|
||||||
|
|
||||||
|
if rule:find( "^Chain " ) == 1 then
|
||||||
|
|
||||||
|
local crefs
|
||||||
|
local cname, cpol, cpkt, cbytes = rule:match(
|
||||||
|
"^Chain ([^%s]*) %(policy (%w+) " ..
|
||||||
|
"(%d+) packets, (%d+) bytes%)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not cname then
|
||||||
|
cname, crefs = rule:match(
|
||||||
|
"^Chain ([^%s]*) %((%d+) references%)"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
self._chain = cname
|
||||||
|
self._chains[tbl][cname] = {
|
||||||
|
policy = cpol,
|
||||||
|
packets = tonumber(cpkt or 0),
|
||||||
|
bytes = tonumber(cbytes or 0),
|
||||||
|
references = tonumber(crefs or 0),
|
||||||
|
rules = { }
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
if rule:find("%d") == 1 then
|
||||||
|
|
||||||
|
local rule_parts = luci.util.split( rule, "%s+", nil, true )
|
||||||
|
local rule_details = { }
|
||||||
|
|
||||||
|
-- cope with rules that have no target assigned
|
||||||
|
if rule:match("^%d+%s+%d+%s+%d+%s%s") then
|
||||||
|
table.insert(rule_parts, 4, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ip6tables opt column is usually zero-width
|
||||||
|
if self._family == 6 then
|
||||||
|
table.insert(rule_parts, 6, "--")
|
||||||
|
end
|
||||||
|
|
||||||
|
rule_details["table"] = tbl
|
||||||
|
rule_details["chain"] = self._chain
|
||||||
|
rule_details["index"] = tonumber(rule_parts[1])
|
||||||
|
rule_details["packets"] = tonumber(rule_parts[2])
|
||||||
|
rule_details["bytes"] = tonumber(rule_parts[3])
|
||||||
|
rule_details["target"] = rule_parts[4]
|
||||||
|
rule_details["protocol"] = rule_parts[5]
|
||||||
|
rule_details["flags"] = rule_parts[6]
|
||||||
|
rule_details["inputif"] = rule_parts[7]
|
||||||
|
rule_details["outputif"] = rule_parts[8]
|
||||||
|
rule_details["source"] = rule_parts[9]
|
||||||
|
rule_details["destination"] = rule_parts[10]
|
||||||
|
rule_details["options"] = { }
|
||||||
|
|
||||||
|
for i = 11, #rule_parts - 1 do
|
||||||
|
rule_details["options"][i-10] = rule_parts[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
self._rules[#self._rules+1] = rule_details
|
||||||
|
|
||||||
|
self._chains[tbl][self._chain].rules[
|
||||||
|
#self._chains[tbl][self._chain].rules + 1
|
||||||
|
] = rule_details
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._chain = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- [internal] Return true if optlist1 contains all elements of optlist 2.
|
||||||
|
-- Return false in all other cases.
|
||||||
|
function IptParser._match_options( self, o1, o2 )
|
||||||
|
|
||||||
|
-- construct a hashtable of first options list to speed up lookups
|
||||||
|
local oh = { }
|
||||||
|
for i, opt in ipairs( o1 ) do oh[opt] = true end
|
||||||
|
|
||||||
|
-- iterate over second options list
|
||||||
|
-- each string in o2 must be also present in o1
|
||||||
|
-- if o2 contains a string which is not found in o1 then return false
|
||||||
|
for i, opt in ipairs( o2 ) do
|
||||||
|
if not oh[opt] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
28
1_1.mi_Lua/luci/sys/zoneinfo.lua
Normal file
28
1_1.mi_Lua/luci/sys/zoneinfo.lua
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Autogenerated Zoneinfo Module
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local setmetatable, require, rawget, rawset = setmetatable, require, rawget, rawset
|
||||||
|
|
||||||
|
module "luci.sys.zoneinfo"
|
||||||
|
|
||||||
|
setmetatable(_M, {
|
||||||
|
__index = function(t, k)
|
||||||
|
if k == "TZ" and not rawget(t, k) then
|
||||||
|
local m = require "luci.sys.zoneinfo.tzdata"
|
||||||
|
rawset(t, k, rawget(m, k))
|
||||||
|
elseif k == "OFFSET" and not rawget(t, k) then
|
||||||
|
local m = require "luci.sys.zoneinfo.tzoffset"
|
||||||
|
rawset(t, k, rawget(m, k))
|
||||||
|
end
|
||||||
|
|
||||||
|
return rawget(t, k)
|
||||||
|
end
|
||||||
|
})
|
420
1_1.mi_Lua/luci/sys/zoneinfo/tzdata.lua
Normal file
420
1_1.mi_Lua/luci/sys/zoneinfo/tzdata.lua
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Autogenerated Zoneinfo Module
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
module "luci.sys.zoneinfo.tzdata"
|
||||||
|
|
||||||
|
TZ = {
|
||||||
|
{ 'Africa/Abidjan', 'GMT0' },
|
||||||
|
{ 'Africa/Accra', 'GMT0' },
|
||||||
|
{ 'Africa/Addis Ababa', 'EAT-3' },
|
||||||
|
{ 'Africa/Algiers', 'CET-1' },
|
||||||
|
{ 'Africa/Asmara', 'EAT-3' },
|
||||||
|
{ 'Africa/Bamako', 'GMT0' },
|
||||||
|
{ 'Africa/Bangui', 'WAT-1' },
|
||||||
|
{ 'Africa/Banjul', 'GMT0' },
|
||||||
|
{ 'Africa/Bissau', 'GMT0' },
|
||||||
|
{ 'Africa/Blantyre', 'CAT-2' },
|
||||||
|
{ 'Africa/Brazzaville', 'WAT-1' },
|
||||||
|
{ 'Africa/Bujumbura', 'CAT-2' },
|
||||||
|
{ 'Africa/Casablanca', 'WET0' },
|
||||||
|
{ 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Africa/Conakry', 'GMT0' },
|
||||||
|
{ 'Africa/Dakar', 'GMT0' },
|
||||||
|
{ 'Africa/Dar es Salaam', 'EAT-3' },
|
||||||
|
{ 'Africa/Djibouti', 'EAT-3' },
|
||||||
|
{ 'Africa/Douala', 'WAT-1' },
|
||||||
|
{ 'Africa/El Aaiun', 'WET0' },
|
||||||
|
{ 'Africa/Freetown', 'GMT0' },
|
||||||
|
{ 'Africa/Gaborone', 'CAT-2' },
|
||||||
|
{ 'Africa/Harare', 'CAT-2' },
|
||||||
|
{ 'Africa/Johannesburg', 'SAST-2' },
|
||||||
|
{ 'Africa/Juba', 'EAT-3' },
|
||||||
|
{ 'Africa/Kampala', 'EAT-3' },
|
||||||
|
{ 'Africa/Khartoum', 'EAT-3' },
|
||||||
|
{ 'Africa/Kigali', 'CAT-2' },
|
||||||
|
{ 'Africa/Kinshasa', 'WAT-1' },
|
||||||
|
{ 'Africa/Lagos', 'WAT-1' },
|
||||||
|
{ 'Africa/Libreville', 'WAT-1' },
|
||||||
|
{ 'Africa/Lome', 'GMT0' },
|
||||||
|
{ 'Africa/Luanda', 'WAT-1' },
|
||||||
|
{ 'Africa/Lubumbashi', 'CAT-2' },
|
||||||
|
{ 'Africa/Lusaka', 'CAT-2' },
|
||||||
|
{ 'Africa/Malabo', 'WAT-1' },
|
||||||
|
{ 'Africa/Maputo', 'CAT-2' },
|
||||||
|
{ 'Africa/Maseru', 'SAST-2' },
|
||||||
|
{ 'Africa/Mbabane', 'SAST-2' },
|
||||||
|
{ 'Africa/Mogadishu', 'EAT-3' },
|
||||||
|
{ 'Africa/Monrovia', 'GMT0' },
|
||||||
|
{ 'Africa/Nairobi', 'EAT-3' },
|
||||||
|
{ 'Africa/Ndjamena', 'WAT-1' },
|
||||||
|
{ 'Africa/Niamey', 'WAT-1' },
|
||||||
|
{ 'Africa/Nouakchott', 'GMT0' },
|
||||||
|
{ 'Africa/Ouagadougou', 'GMT0' },
|
||||||
|
{ 'Africa/Porto-Novo', 'WAT-1' },
|
||||||
|
{ 'Africa/Sao Tome', 'GMT0' },
|
||||||
|
{ 'Africa/Tripoli', 'EET-2' },
|
||||||
|
{ 'Africa/Tunis', 'CET-1' },
|
||||||
|
{ 'Africa/Windhoek', 'WAT-1WAST,M9.1.0,M4.1.0' },
|
||||||
|
{ 'America/Adak', 'HAST10HADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Anchorage', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Anguilla', 'AST4' },
|
||||||
|
{ 'America/Antigua', 'AST4' },
|
||||||
|
{ 'America/Araguaina', 'BRT3' },
|
||||||
|
{ 'America/Argentina/Buenos Aires', 'ART3' },
|
||||||
|
{ 'America/Argentina/Catamarca', 'ART3' },
|
||||||
|
{ 'America/Argentina/Cordoba', 'ART3' },
|
||||||
|
{ 'America/Argentina/Jujuy', 'ART3' },
|
||||||
|
{ 'America/Argentina/La Rioja', 'ART3' },
|
||||||
|
{ 'America/Argentina/Mendoza', 'ART3' },
|
||||||
|
{ 'America/Argentina/Rio Gallegos', 'ART3' },
|
||||||
|
{ 'America/Argentina/Salta', 'ART3' },
|
||||||
|
{ 'America/Argentina/San Juan', 'ART3' },
|
||||||
|
{ 'America/Argentina/Tucuman', 'ART3' },
|
||||||
|
{ 'America/Argentina/Ushuaia', 'ART3' },
|
||||||
|
{ 'America/Aruba', 'AST4' },
|
||||||
|
{ 'America/Asuncion', 'PYT4PYST,M10.1.0/0,M4.2.0/0' },
|
||||||
|
{ 'America/Atikokan', 'EST5' },
|
||||||
|
{ 'America/Bahia', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
|
||||||
|
{ 'America/Bahia Banderas', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Barbados', 'AST4' },
|
||||||
|
{ 'America/Belem', 'BRT3' },
|
||||||
|
{ 'America/Belize', 'CST6' },
|
||||||
|
{ 'America/Blanc-Sablon', 'AST4' },
|
||||||
|
{ 'America/Boa Vista', 'AMT4' },
|
||||||
|
{ 'America/Bogota', 'COT5' },
|
||||||
|
{ 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
|
||||||
|
{ 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Caracas', 'VET4:30' },
|
||||||
|
{ 'America/Cayenne', 'GFT3' },
|
||||||
|
{ 'America/Cayman', 'EST5' },
|
||||||
|
{ 'America/Chicago', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Chihuahua', 'MST7MDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Costa Rica', 'CST6' },
|
||||||
|
{ 'America/Cuiaba', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
|
||||||
|
{ 'America/Curacao', 'AST4' },
|
||||||
|
{ 'America/Danmarkshavn', 'GMT0' },
|
||||||
|
{ 'America/Dawson', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Dawson Creek', 'MST7' },
|
||||||
|
{ 'America/Denver', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Detroit', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Dominica', 'AST4' },
|
||||||
|
{ 'America/Edmonton', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Eirunepe', 'AMT4' },
|
||||||
|
{ 'America/El Salvador', 'CST6' },
|
||||||
|
{ 'America/Fortaleza', 'BRT3' },
|
||||||
|
{ 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Goose Bay', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Grand Turk', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Grenada', 'AST4' },
|
||||||
|
{ 'America/Guadeloupe', 'AST4' },
|
||||||
|
{ 'America/Guatemala', 'CST6' },
|
||||||
|
{ 'America/Guayaquil', 'ECT5' },
|
||||||
|
{ 'America/Guyana', 'GYT4' },
|
||||||
|
{ 'America/Halifax', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Havana', 'CST5CDT,M3.2.0/0,M10.5.0/1' },
|
||||||
|
{ 'America/Hermosillo', 'MST7' },
|
||||||
|
{ 'America/Indiana/Indianapolis', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Knox', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Marengo', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Petersburg', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Tell City', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Vevay', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Vincennes', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Indiana/Winamac', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Inuvik', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Iqaluit', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Jamaica', 'EST5' },
|
||||||
|
{ 'America/Juneau', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Kentucky/Louisville', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Kentucky/Monticello', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Kralendijk', 'AST4' },
|
||||||
|
{ 'America/La Paz', 'BOT4' },
|
||||||
|
{ 'America/Lima', 'PET5' },
|
||||||
|
{ 'America/Los Angeles', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Lower Princes', 'AST4' },
|
||||||
|
{ 'America/Maceio', 'BRT3' },
|
||||||
|
{ 'America/Managua', 'CST6' },
|
||||||
|
{ 'America/Manaus', 'AMT4' },
|
||||||
|
{ 'America/Marigot', 'AST4' },
|
||||||
|
{ 'America/Martinique', 'AST4' },
|
||||||
|
{ 'America/Matamoros', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Mazatlan', 'MST7MDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Menominee', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Merida', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Metlakatla', 'MeST8' },
|
||||||
|
{ 'America/Mexico City', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Miquelon', 'PMST3PMDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Moncton', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Monterrey', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Montevideo', 'UYT3UYST,M10.1.0,M3.2.0' },
|
||||||
|
{ 'America/Montreal', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Montserrat', 'AST4' },
|
||||||
|
{ 'America/Nassau', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/New York', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Nipigon', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Nome', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Noronha', 'FNT2' },
|
||||||
|
{ 'America/North Dakota/Beulah', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/North Dakota/Center', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/North Dakota/New Salem', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Ojinaga', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Panama', 'EST5' },
|
||||||
|
{ 'America/Pangnirtung', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Paramaribo', 'SRT3' },
|
||||||
|
{ 'America/Phoenix', 'MST7' },
|
||||||
|
{ 'America/Port of Spain', 'AST4' },
|
||||||
|
{ 'America/Port-au-Prince', 'EST5' },
|
||||||
|
{ 'America/Porto Velho', 'AMT4' },
|
||||||
|
{ 'America/Puerto Rico', 'AST4' },
|
||||||
|
{ 'America/Rainy River', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Rankin Inlet', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Recife', 'BRT3' },
|
||||||
|
{ 'America/Regina', 'CST6' },
|
||||||
|
{ 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Rio Branco', 'AMT4' },
|
||||||
|
{ 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' },
|
||||||
|
{ 'America/Santarem', 'BRT3' },
|
||||||
|
{ 'America/Santo Domingo', 'AST4' },
|
||||||
|
{ 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
|
||||||
|
{ 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' },
|
||||||
|
{ 'America/Shiprock', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Sitka', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/St Barthelemy', 'AST4' },
|
||||||
|
{ 'America/St Johns', 'NST3:30NDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/St Kitts', 'AST4' },
|
||||||
|
{ 'America/St Lucia', 'AST4' },
|
||||||
|
{ 'America/St Thomas', 'AST4' },
|
||||||
|
{ 'America/St Vincent', 'AST4' },
|
||||||
|
{ 'America/Swift Current', 'CST6' },
|
||||||
|
{ 'America/Tegucigalpa', 'CST6' },
|
||||||
|
{ 'America/Thule', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Thunder Bay', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Tijuana', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Toronto', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Tortola', 'AST4' },
|
||||||
|
{ 'America/Vancouver', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Whitehorse', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Winnipeg', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Yakutat', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'America/Yellowknife', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'Antarctica/Casey', 'WST-8' },
|
||||||
|
{ 'Antarctica/Davis', 'DAVT-7' },
|
||||||
|
{ 'Antarctica/DumontDUrville', 'DDUT-10' },
|
||||||
|
{ 'Antarctica/Macquarie', 'MIST-11' },
|
||||||
|
{ 'Antarctica/Mawson', 'MAWT-5' },
|
||||||
|
{ 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
|
||||||
|
{ 'Antarctica/Rothera', 'ROTT3' },
|
||||||
|
{ 'Antarctica/South Pole', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
|
||||||
|
{ 'Antarctica/Syowa', 'SYOT-3' },
|
||||||
|
{ 'Antarctica/Vostok', 'VOST-6' },
|
||||||
|
{ 'Arctic/Longyearbyen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Asia/Aden', 'AST-3' },
|
||||||
|
{ 'Asia/Almaty', 'ALMT-6' },
|
||||||
|
{ 'Asia/Anadyr', 'ANAT-12' },
|
||||||
|
{ 'Asia/Aqtau', 'AQTT-5' },
|
||||||
|
{ 'Asia/Aqtobe', 'AQTT-5' },
|
||||||
|
{ 'Asia/Ashgabat', 'TMT-5' },
|
||||||
|
{ 'Asia/Baghdad', 'AST-3' },
|
||||||
|
{ 'Asia/Bahrain', 'AST-3' },
|
||||||
|
{ 'Asia/Baku', 'AZT-4AZST,M3.5.0/4,M10.5.0/5' },
|
||||||
|
{ 'Asia/Bangkok', 'ICT-7' },
|
||||||
|
{ 'Asia/Beirut', 'EET-2EEST,M3.5.0/0,M10.5.0/0' },
|
||||||
|
{ 'Asia/Bishkek', 'KGT-6' },
|
||||||
|
{ 'Asia/Brunei', 'BNT-8' },
|
||||||
|
{ 'Asia/Choibalsan', 'CHOT-8' },
|
||||||
|
{ 'Asia/Chongqing', 'CST-8' },
|
||||||
|
{ 'Asia/Colombo', 'IST-5:30' },
|
||||||
|
{ 'Asia/Damascus', 'EET-2EEST,M4.1.5/0,M10.5.5/0' },
|
||||||
|
{ 'Asia/Dhaka', 'BDT-6' },
|
||||||
|
{ 'Asia/Dili', 'TLT-9' },
|
||||||
|
{ 'Asia/Dubai', 'GST-4' },
|
||||||
|
{ 'Asia/Dushanbe', 'TJT-5' },
|
||||||
|
{ 'Asia/Gaza', 'EET-2' },
|
||||||
|
{ 'Asia/Harbin', 'CST-8' },
|
||||||
|
{ 'Asia/Hebron', 'EET-2' },
|
||||||
|
{ 'Asia/Ho Chi Minh', 'ICT-7' },
|
||||||
|
{ 'Asia/Hong Kong', 'HKT-8' },
|
||||||
|
{ 'Asia/Hovd', 'HOVT-7' },
|
||||||
|
{ 'Asia/Irkutsk', 'IRKT-9' },
|
||||||
|
{ 'Asia/Jakarta', 'WIT-7' },
|
||||||
|
{ 'Asia/Jayapura', 'EIT-9' },
|
||||||
|
{ 'Asia/Kabul', 'AFT-4:30' },
|
||||||
|
{ 'Asia/Kamchatka', 'PETT-12' },
|
||||||
|
{ 'Asia/Karachi', 'PKT-5' },
|
||||||
|
{ 'Asia/Kashgar', 'CST-8' },
|
||||||
|
{ 'Asia/Kathmandu', 'NPT-5:45' },
|
||||||
|
{ 'Asia/Kolkata', 'IST-5:30' },
|
||||||
|
{ 'Asia/Krasnoyarsk', 'KRAT-8' },
|
||||||
|
{ 'Asia/Kuala Lumpur', 'MYT-8' },
|
||||||
|
{ 'Asia/Kuching', 'MYT-8' },
|
||||||
|
{ 'Asia/Kuwait', 'AST-3' },
|
||||||
|
{ 'Asia/Macau', 'CST-8' },
|
||||||
|
{ 'Asia/Magadan', 'MAGT-12' },
|
||||||
|
{ 'Asia/Makassar', 'CIT-8' },
|
||||||
|
{ 'Asia/Manila', 'PHT-8' },
|
||||||
|
{ 'Asia/Muscat', 'GST-4' },
|
||||||
|
{ 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Asia/Novokuznetsk', 'NOVT-7' },
|
||||||
|
{ 'Asia/Novosibirsk', 'NOVT-7' },
|
||||||
|
{ 'Asia/Omsk', 'OMST-7' },
|
||||||
|
{ 'Asia/Oral', 'ORAT-5' },
|
||||||
|
{ 'Asia/Phnom Penh', 'ICT-7' },
|
||||||
|
{ 'Asia/Pontianak', 'WIT-7' },
|
||||||
|
{ 'Asia/Pyongyang', 'KST-9' },
|
||||||
|
{ 'Asia/Qatar', 'AST-3' },
|
||||||
|
{ 'Asia/Qyzylorda', 'QYZT-6' },
|
||||||
|
{ 'Asia/Rangoon', 'MMT-6:30' },
|
||||||
|
{ 'Asia/Riyadh', 'AST-3' },
|
||||||
|
{ 'Asia/Sakhalin', 'SAKT-11' },
|
||||||
|
{ 'Asia/Samarkand', 'UZT-5' },
|
||||||
|
{ 'Asia/Seoul', 'KST-9' },
|
||||||
|
{ 'Asia/Shanghai', 'CST-8' },
|
||||||
|
{ 'Asia/Singapore', 'SGT-8' },
|
||||||
|
{ 'Asia/Taipei', 'CST-8' },
|
||||||
|
{ 'Asia/Tashkent', 'UZT-5' },
|
||||||
|
{ 'Asia/Tbilisi', 'GET-4' },
|
||||||
|
{ 'Asia/Thimphu', 'BTT-6' },
|
||||||
|
{ 'Asia/Tokyo', 'JST-9' },
|
||||||
|
{ 'Asia/Ulaanbaatar', 'ULAT-8' },
|
||||||
|
{ 'Asia/Urumqi', 'CST-8' },
|
||||||
|
{ 'Asia/Vientiane', 'ICT-7' },
|
||||||
|
{ 'Asia/Vladivostok', 'VLAT-11' },
|
||||||
|
{ 'Asia/Yakutsk', 'YAKT-10' },
|
||||||
|
{ 'Asia/Yekaterinburg', 'YEKT-6' },
|
||||||
|
{ 'Asia/Yerevan', 'AMT-4AMST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Atlantic/Azores', 'AZOT1AZOST,M3.5.0/0,M10.5.0/1' },
|
||||||
|
{ 'Atlantic/Bermuda', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||||
|
{ 'Atlantic/Canary', 'WET0WEST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Atlantic/Cape Verde', 'CVT1' },
|
||||||
|
{ 'Atlantic/Faroe', 'WET0WEST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Atlantic/Madeira', 'WET0WEST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Atlantic/Reykjavik', 'GMT0' },
|
||||||
|
{ 'Atlantic/South Georgia', 'GST2' },
|
||||||
|
{ 'Atlantic/St Helena', 'GMT0' },
|
||||||
|
{ 'Atlantic/Stanley', 'FKT4FKST,M9.1.0,M4.3.0' },
|
||||||
|
{ 'Australia/Adelaide', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Australia/Brisbane', 'EST-10' },
|
||||||
|
{ 'Australia/Broken Hill', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Australia/Currie', 'EST-10EST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Australia/Darwin', 'CST-9:30' },
|
||||||
|
{ 'Australia/Eucla', 'CWST-8:45' },
|
||||||
|
{ 'Australia/Hobart', 'EST-10EST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Australia/Lindeman', 'EST-10' },
|
||||||
|
{ 'Australia/Lord Howe', 'LHST-10:30LHST-11,M10.1.0,M4.1.0' },
|
||||||
|
{ 'Australia/Melbourne', 'EST-10EST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Australia/Perth', 'WST-8' },
|
||||||
|
{ 'Australia/Sydney', 'EST-10EST,M10.1.0,M4.1.0/3' },
|
||||||
|
{ 'Europe/Amsterdam', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Andorra', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Athens', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Belgrade', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Berlin', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Bratislava', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Brussels', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Bucharest', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Budapest', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Chisinau', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Isle of Man', 'GMT0BST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Istanbul', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Jersey', 'GMT0BST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Kaliningrad', 'FET-3' },
|
||||||
|
{ 'Europe/Kiev', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Lisbon', 'WET0WEST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Ljubljana', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/London', 'GMT0BST,M3.5.0/1,M10.5.0' },
|
||||||
|
{ 'Europe/Luxembourg', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Madrid', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Malta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Mariehamn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Minsk', 'FET-3' },
|
||||||
|
{ 'Europe/Monaco', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Moscow', 'MSK-4' },
|
||||||
|
{ 'Europe/Oslo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Paris', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Podgorica', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Prague', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Riga', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Rome', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Samara', 'SAMT-4' },
|
||||||
|
{ 'Europe/San Marino', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Sarajevo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Simferopol', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Skopje', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Sofia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Stockholm', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Tallinn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Tirane', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Uzhgorod', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Vaduz', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Volgograd', 'VOLT-4' },
|
||||||
|
{ 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||||
|
{ 'Europe/Zurich', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||||
|
{ 'Indian/Antananarivo', 'EAT-3' },
|
||||||
|
{ 'Indian/Chagos', 'IOT-6' },
|
||||||
|
{ 'Indian/Christmas', 'CXT-7' },
|
||||||
|
{ 'Indian/Cocos', 'CCT-6:30' },
|
||||||
|
{ 'Indian/Comoro', 'EAT-3' },
|
||||||
|
{ 'Indian/Kerguelen', 'TFT-5' },
|
||||||
|
{ 'Indian/Mahe', 'SCT-4' },
|
||||||
|
{ 'Indian/Maldives', 'MVT-5' },
|
||||||
|
{ 'Indian/Mauritius', 'MUT-4' },
|
||||||
|
{ 'Indian/Mayotte', 'EAT-3' },
|
||||||
|
{ 'Indian/Reunion', 'RET-4' },
|
||||||
|
{ 'Pacific/Apia', 'WST-13' },
|
||||||
|
{ 'Pacific/Auckland', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
|
||||||
|
{ 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' },
|
||||||
|
{ 'Pacific/Chuuk', 'CHUT-10' },
|
||||||
|
{ 'Pacific/Efate', 'VUT-11' },
|
||||||
|
{ 'Pacific/Enderbury', 'PHOT-13' },
|
||||||
|
{ 'Pacific/Fakaofo', 'TKT10' },
|
||||||
|
{ 'Pacific/Fiji', 'FJT-12' },
|
||||||
|
{ 'Pacific/Funafuti', 'TVT-12' },
|
||||||
|
{ 'Pacific/Galapagos', 'GALT6' },
|
||||||
|
{ 'Pacific/Gambier', 'GAMT9' },
|
||||||
|
{ 'Pacific/Guadalcanal', 'SBT-11' },
|
||||||
|
{ 'Pacific/Guam', 'ChST-10' },
|
||||||
|
{ 'Pacific/Honolulu', 'HST10' },
|
||||||
|
{ 'Pacific/Johnston', 'HST10' },
|
||||||
|
{ 'Pacific/Kiritimati', 'LINT-14' },
|
||||||
|
{ 'Pacific/Kosrae', 'KOST-11' },
|
||||||
|
{ 'Pacific/Kwajalein', 'MHT-12' },
|
||||||
|
{ 'Pacific/Majuro', 'MHT-12' },
|
||||||
|
{ 'Pacific/Marquesas', 'MART9:30' },
|
||||||
|
{ 'Pacific/Midway', 'SST11' },
|
||||||
|
{ 'Pacific/Nauru', 'NRT-12' },
|
||||||
|
{ 'Pacific/Niue', 'NUT11' },
|
||||||
|
{ 'Pacific/Norfolk', 'NFT-11:30' },
|
||||||
|
{ 'Pacific/Noumea', 'NCT-11' },
|
||||||
|
{ 'Pacific/Pago Pago', 'SST11' },
|
||||||
|
{ 'Pacific/Palau', 'PWT-9' },
|
||||||
|
{ 'Pacific/Pitcairn', 'PST8' },
|
||||||
|
{ 'Pacific/Pohnpei', 'PONT-11' },
|
||||||
|
{ 'Pacific/Port Moresby', 'PGT-10' },
|
||||||
|
{ 'Pacific/Rarotonga', 'CKT10' },
|
||||||
|
{ 'Pacific/Saipan', 'ChST-10' },
|
||||||
|
{ 'Pacific/Tahiti', 'TAHT10' },
|
||||||
|
{ 'Pacific/Tarawa', 'GILT-12' },
|
||||||
|
{ 'Pacific/Tongatapu', 'TOT-13' },
|
||||||
|
{ 'Pacific/Wake', 'WAKT-12' },
|
||||||
|
{ 'Pacific/Wallis', 'WFT-12' },
|
||||||
|
}
|
162
1_1.mi_Lua/luci/sys/zoneinfo/tzoffset.lua
Normal file
162
1_1.mi_Lua/luci/sys/zoneinfo/tzoffset.lua
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Autogenerated Zoneinfo Module
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
module "luci.sys.zoneinfo.tzoffset"
|
||||||
|
|
||||||
|
OFFSET = {
|
||||||
|
gmt = 0, -- GMT
|
||||||
|
eat = 10800, -- EAT
|
||||||
|
cet = 3600, -- CET
|
||||||
|
wat = 3600, -- WAT
|
||||||
|
cat = 7200, -- CAT
|
||||||
|
wet = 0, -- WET
|
||||||
|
sast = 7200, -- SAST
|
||||||
|
eet = 7200, -- EET
|
||||||
|
hast = -36000, -- HAST
|
||||||
|
hadt = -32400, -- HADT
|
||||||
|
akst = -32400, -- AKST
|
||||||
|
akdt = -28800, -- AKDT
|
||||||
|
ast = -14400, -- AST
|
||||||
|
brt = -10800, -- BRT
|
||||||
|
art = -10800, -- ART
|
||||||
|
pyt = -14400, -- PYT
|
||||||
|
pyst = -10800, -- PYST
|
||||||
|
est = -18000, -- EST
|
||||||
|
cst = -21600, -- CST
|
||||||
|
cdt = -18000, -- CDT
|
||||||
|
amt = -14400, -- AMT
|
||||||
|
cot = -18000, -- COT
|
||||||
|
mst = -25200, -- MST
|
||||||
|
mdt = -21600, -- MDT
|
||||||
|
vet = -16200, -- VET
|
||||||
|
gft = -10800, -- GFT
|
||||||
|
pst = -28800, -- PST
|
||||||
|
pdt = -25200, -- PDT
|
||||||
|
ect = -18000, -- ECT
|
||||||
|
gyt = -14400, -- GYT
|
||||||
|
bot = -14400, -- BOT
|
||||||
|
pet = -18000, -- PET
|
||||||
|
pmst = -10800, -- PMST
|
||||||
|
pmdt = -7200, -- PMDT
|
||||||
|
uyt = -10800, -- UYT
|
||||||
|
uyst = -7200, -- UYST
|
||||||
|
fnt = -7200, -- FNT
|
||||||
|
srt = -10800, -- SRT
|
||||||
|
egt = -3600, -- EGT
|
||||||
|
egst = 0, -- EGST
|
||||||
|
nst = -12600, -- NST
|
||||||
|
ndt = -9000, -- NDT
|
||||||
|
wst = 28800, -- WST
|
||||||
|
davt = 25200, -- DAVT
|
||||||
|
ddut = 36000, -- DDUT
|
||||||
|
mist = 39600, -- MIST
|
||||||
|
mawt = 18000, -- MAWT
|
||||||
|
nzst = 43200, -- NZST
|
||||||
|
nzdt = 46800, -- NZDT
|
||||||
|
rott = -10800, -- ROTT
|
||||||
|
syot = 10800, -- SYOT
|
||||||
|
vost = 21600, -- VOST
|
||||||
|
almt = 21600, -- ALMT
|
||||||
|
anat = 43200, -- ANAT
|
||||||
|
aqtt = 18000, -- AQTT
|
||||||
|
tmt = 18000, -- TMT
|
||||||
|
azt = 14400, -- AZT
|
||||||
|
azst = 18000, -- AZST
|
||||||
|
ict = 25200, -- ICT
|
||||||
|
kgt = 21600, -- KGT
|
||||||
|
bnt = 28800, -- BNT
|
||||||
|
chot = 28800, -- CHOT
|
||||||
|
ist = 19800, -- IST
|
||||||
|
bdt = 21600, -- BDT
|
||||||
|
tlt = 32400, -- TLT
|
||||||
|
gst = 14400, -- GST
|
||||||
|
tjt = 18000, -- TJT
|
||||||
|
hkt = 28800, -- HKT
|
||||||
|
hovt = 25200, -- HOVT
|
||||||
|
irkt = 32400, -- IRKT
|
||||||
|
wit = 25200, -- WIT
|
||||||
|
eit = 32400, -- EIT
|
||||||
|
aft = 16200, -- AFT
|
||||||
|
pett = 43200, -- PETT
|
||||||
|
pkt = 18000, -- PKT
|
||||||
|
npt = 20700, -- NPT
|
||||||
|
krat = 28800, -- KRAT
|
||||||
|
myt = 28800, -- MYT
|
||||||
|
magt = 43200, -- MAGT
|
||||||
|
cit = 28800, -- CIT
|
||||||
|
pht = 28800, -- PHT
|
||||||
|
novt = 25200, -- NOVT
|
||||||
|
omst = 25200, -- OMST
|
||||||
|
orat = 18000, -- ORAT
|
||||||
|
kst = 32400, -- KST
|
||||||
|
qyzt = 21600, -- QYZT
|
||||||
|
mmt = 23400, -- MMT
|
||||||
|
sakt = 39600, -- SAKT
|
||||||
|
uzt = 18000, -- UZT
|
||||||
|
sgt = 28800, -- SGT
|
||||||
|
get = 14400, -- GET
|
||||||
|
btt = 21600, -- BTT
|
||||||
|
jst = 32400, -- JST
|
||||||
|
ulat = 28800, -- ULAT
|
||||||
|
vlat = 39600, -- VLAT
|
||||||
|
yakt = 36000, -- YAKT
|
||||||
|
yekt = 21600, -- YEKT
|
||||||
|
azot = -3600, -- AZOT
|
||||||
|
azost = 0, -- AZOST
|
||||||
|
cvt = -3600, -- CVT
|
||||||
|
fkt = -14400, -- FKT
|
||||||
|
fkst = -10800, -- FKST
|
||||||
|
cwst = 31500, -- CWST
|
||||||
|
lhst = 37800, -- LHST
|
||||||
|
lhst = 39600, -- LHST
|
||||||
|
fet = 10800, -- FET
|
||||||
|
msk = 14400, -- MSK
|
||||||
|
samt = 14400, -- SAMT
|
||||||
|
volt = 14400, -- VOLT
|
||||||
|
iot = 21600, -- IOT
|
||||||
|
cxt = 25200, -- CXT
|
||||||
|
cct = 23400, -- CCT
|
||||||
|
tft = 18000, -- TFT
|
||||||
|
sct = 14400, -- SCT
|
||||||
|
mvt = 18000, -- MVT
|
||||||
|
mut = 14400, -- MUT
|
||||||
|
ret = 14400, -- RET
|
||||||
|
chast = 45900, -- CHAST
|
||||||
|
chadt = 49500, -- CHADT
|
||||||
|
chut = 36000, -- CHUT
|
||||||
|
vut = 39600, -- VUT
|
||||||
|
phot = 46800, -- PHOT
|
||||||
|
tkt = -36000, -- TKT
|
||||||
|
fjt = 43200, -- FJT
|
||||||
|
tvt = 43200, -- TVT
|
||||||
|
galt = -21600, -- GALT
|
||||||
|
gamt = -32400, -- GAMT
|
||||||
|
sbt = 39600, -- SBT
|
||||||
|
hst = -36000, -- HST
|
||||||
|
lint = 50400, -- LINT
|
||||||
|
kost = 39600, -- KOST
|
||||||
|
mht = 43200, -- MHT
|
||||||
|
mart = -34200, -- MART
|
||||||
|
sst = -39600, -- SST
|
||||||
|
nrt = 43200, -- NRT
|
||||||
|
nut = -39600, -- NUT
|
||||||
|
nft = 41400, -- NFT
|
||||||
|
nct = 39600, -- NCT
|
||||||
|
pwt = 32400, -- PWT
|
||||||
|
pont = 39600, -- PONT
|
||||||
|
pgt = 36000, -- PGT
|
||||||
|
ckt = -36000, -- CKT
|
||||||
|
taht = -36000, -- TAHT
|
||||||
|
gilt = 43200, -- GILT
|
||||||
|
tot = 46800, -- TOT
|
||||||
|
wakt = 43200, -- WAKT
|
||||||
|
wft = 43200, -- WFT
|
||||||
|
}
|
107
1_1.mi_Lua/luci/template.lua
Normal file
107
1_1.mi_Lua/luci/template.lua
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
--[[
|
||||||
|
LuCI - Template Parser
|
||||||
|
|
||||||
|
Description:
|
||||||
|
A template parser supporting includes, translations, Lua code blocks
|
||||||
|
and more. It can be used either as a compiler or as an interpreter.
|
||||||
|
|
||||||
|
FileId: $Id: template.lua 9558 2012-12-18 13:58:22Z jow $
|
||||||
|
|
||||||
|
License:
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
local config = require "luci.config"
|
||||||
|
local tparser = require "luci.template.parser"
|
||||||
|
|
||||||
|
local tostring, pairs, loadstring = tostring, pairs, loadstring
|
||||||
|
local setmetatable, loadfile = setmetatable, loadfile
|
||||||
|
local getfenv, setfenv, rawget = getfenv, setfenv, rawget
|
||||||
|
local assert, type, error = assert, type, error
|
||||||
|
|
||||||
|
--- LuCI template library.
|
||||||
|
module "luci.template"
|
||||||
|
|
||||||
|
config.template = config.template or {}
|
||||||
|
viewdir = config.template.viewdir or util.libpath() .. "/view"
|
||||||
|
|
||||||
|
|
||||||
|
-- Define the namespace for template modules
|
||||||
|
context = util.threadlocal()
|
||||||
|
|
||||||
|
--- Render a certain template.
|
||||||
|
-- @param name Template name
|
||||||
|
-- @param scope Scope to assign to template (optional)
|
||||||
|
function render(name, scope)
|
||||||
|
return Template(name):render(scope or getfenv(2))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Template class
|
||||||
|
Template = util.class()
|
||||||
|
|
||||||
|
-- Shared template cache to store templates in to avoid unnecessary reloading
|
||||||
|
Template.cache = setmetatable({}, {__mode = "v"})
|
||||||
|
|
||||||
|
|
||||||
|
-- Constructor - Reads and compiles the template on-demand
|
||||||
|
function Template.__init__(self, name)
|
||||||
|
|
||||||
|
self.template = self.cache[name]
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
-- Create a new namespace for this template
|
||||||
|
self.viewns = context.viewns
|
||||||
|
|
||||||
|
-- If we have a cached template, skip compiling and loading
|
||||||
|
if not self.template then
|
||||||
|
|
||||||
|
-- Compile template
|
||||||
|
local err
|
||||||
|
local sourcefile = viewdir .. "/" .. name .. ".htm"
|
||||||
|
|
||||||
|
self.template, _, err = tparser.parse(sourcefile)
|
||||||
|
|
||||||
|
-- If we have no valid template throw error, otherwise cache the template
|
||||||
|
if not self.template then
|
||||||
|
error("Failed to load template '" .. name .. "'.\n" ..
|
||||||
|
"Error while parsing template '" .. sourcefile .. "':\n" ..
|
||||||
|
(err or "Unknown syntax error"))
|
||||||
|
else
|
||||||
|
self.cache[name] = self.template
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Renders a template
|
||||||
|
function Template.render(self, scope)
|
||||||
|
scope = scope or getfenv(2)
|
||||||
|
|
||||||
|
-- Put our predefined objects in the scope of the template
|
||||||
|
setfenv(self.template, setmetatable({}, {__index =
|
||||||
|
function(tbl, key)
|
||||||
|
return rawget(tbl, key) or self.viewns[key] or scope[key]
|
||||||
|
end}))
|
||||||
|
|
||||||
|
-- Now finally render the thing
|
||||||
|
local stat, err = util.copcall(self.template)
|
||||||
|
if not stat then
|
||||||
|
error("Failed to execute template '" .. self.name .. "'.\n" ..
|
||||||
|
"A runtime error occured: " .. tostring(err or "(nil)"))
|
||||||
|
end
|
||||||
|
end
|
BIN
1_1.mi_Lua/luci/template/parser.so
Normal file
BIN
1_1.mi_Lua/luci/template/parser.so
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user