mirror of
https://github.com/JamesonHuang/OpenWrt_Luci_Lua.git
synced 2024-11-23 13:50:11 +00:00
adjust code directory
This commit is contained in:
parent
5b79635498
commit
b09950bf62
@ -1,113 +0,0 @@
|
||||
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
|
@ -1,15 +0,0 @@
|
||||
--[[
|
||||
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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,376 +0,0 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- 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
|
@ -1,201 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
@ -1,20 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,43 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,49 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,79 +0,0 @@
|
||||
---------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,33 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,62 +0,0 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
Binary file not shown.
@ -1,292 +0,0 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
@ -1,23 +0,0 @@
|
||||
--[[
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,345 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,87 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,42 +0,0 @@
|
||||
--[[
|
||||
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)
|
@ -1,10 +0,0 @@
|
||||
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
|
@ -1,336 +0,0 @@
|
||||
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
|
@ -1,293 +0,0 @@
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,316 +0,0 @@
|
||||
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
|
@ -1,67 +0,0 @@
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,53 +0,0 @@
|
||||
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
|
@ -1,18 +0,0 @@
|
||||
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
|
@ -1,23 +0,0 @@
|
||||
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
|
@ -1,32 +0,0 @@
|
||||
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
|
@ -1,382 +0,0 @@
|
||||
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
|
@ -1,10 +0,0 @@
|
||||
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
|
@ -1,96 +0,0 @@
|
||||
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
|
||||
|
Binary file not shown.
@ -1,37 +0,0 @@
|
||||
local debug = require "debug"
|
||||
local io = require "io"
|
||||
local collectgarbage, floor = collectgarbage, math.floor
|
||||
|
||||
module "luci.debug"
|
||||
__file__ = debug.getinfo(1, 'S').source:sub(2)
|
||||
|
||||
-- Enables the memory tracer with given flags and returns a function to disable the tracer again
|
||||
function trap_memtrace(flags, dest)
|
||||
flags = flags or "clr"
|
||||
local tracefile = io.open(dest or "/tmp/memtrace", "w")
|
||||
local peak = 0
|
||||
|
||||
local function trap(what, line)
|
||||
local info = debug.getinfo(2, "Sn")
|
||||
local size = floor(collectgarbage("count"))
|
||||
if size > peak then
|
||||
peak = size
|
||||
end
|
||||
if tracefile then
|
||||
tracefile:write(
|
||||
"[", what, "] ", info.source, ":", (line or "?"), "\t",
|
||||
(info.namewhat or ""), "\t",
|
||||
(info.name or ""), "\t",
|
||||
size, " (", peak, ")\n"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
debug.sethook(trap, flags)
|
||||
|
||||
return function()
|
||||
debug.sethook()
|
||||
tracefile:close()
|
||||
end
|
||||
end
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,244 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,336 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,727 +0,0 @@
|
||||
--[[
|
||||
|
||||
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",
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,115 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,99 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,104 +0,0 @@
|
||||
--[[
|
||||
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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,39 +0,0 @@
|
||||
--[[
|
||||
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"
|
@ -1,684 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,566 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,391 +0,0 @@
|
||||
--[[
|
||||
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
|
||||
|
@ -1,88 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,69 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,13 +0,0 @@
|
||||
--[[
|
||||
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 = ...
|
@ -1,136 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,142 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,136 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,116 +0,0 @@
|
||||
--[[
|
||||
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)"
|
@ -1,83 +0,0 @@
|
||||
--[[
|
||||
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"
|
@ -1,38 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,178 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,144 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,338 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,269 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,243 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,88 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,582 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,239 +0,0 @@
|
||||
--[[
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,104 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,404 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,165 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,95 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,121 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,16 +0,0 @@
|
||||
--[[
|
||||
|
||||
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)
|
@ -1,966 +0,0 @@
|
||||
--[[
|
||||
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
|
@ -1,371 +0,0 @@
|
||||
--[[
|
||||
|
||||
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
|
@ -1,28 +0,0 @@
|
||||
--[[
|
||||
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
|
||||
})
|
@ -1,420 +0,0 @@
|
||||
--[[
|
||||
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' },
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
--[[
|
||||
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
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
--[[
|
||||
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
|
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